自学it18大数据笔记-第一阶段Java-day05-day06-day07-day08
笔记为自学时随手记录,如有错误,欢迎指正,不胜感激!
笔记分享:自学it18大数据笔记-第一阶段Java-day05-day06-day07-day08——会持续更新……
第一阶段Java-day03-day04见:http://t.cn/R65SR0f
day05笔记见下图:
day06笔记见下图:
day07笔记见下图:
day08笔记见下图:
Day05
回顾:面向对象编程:OOP——oriented object program
Class:类 软件抽象
Object 实实在在的物体
类的成员
-----------------------
成员变量 成员函数 构造代码块 静态代码块 内部类
New 后面必须跟构造函数 构造函数只能通过new来调用
Windows中文件命名是不区分大小写的!
有对象 才有this
子类的实例化过程
==========================================
1,子类中所有的构造函数默认都会访问父类中空参数的构造函数,因为每一个构造函数的第一行都有一条默认的语句super();
3,子类会具备父类中的数据,所以要先明确父类是如何对这些数据初始化的。
4,当父类中没有空参数的构造函数时,子类的构造函数必须通过this或者super语句指定要访问的构造函数。
构造函数的第一句 一般都是 this()或者super(),默认是空参,一般空参都省略了
Final 最终的
-------------------------------------
1,final可以修饰类,方法,变量。
2,final修饰的类不可以被继承。
3,final修饰的方法不可以被覆盖。
4,final修饰的变量是一个常量。只能被赋值一次。
5,内部类访问局部变量,次局部变量不能进行重新赋值,类似final修饰
内部类:编译后名字会有$出现
内部类
----------------------------------
1,定义在类内部的类称为内部类
2,内部类可以在类成员位置上
编译时产生的类:外部类名$内部类名 见上图 Benz$Engine.class
3,还可以在方法内定义Benz$1Engine.class 会多个数字
================
访问特点:
内部类可以直接访问外部类中的成员,包括私有成员。
而外部类要访问内部类中的成员必须要建立内部类的对象。
内部类定义在成员位置上
可以被private static成员修饰符修饰。
被static修饰的内部类只能访问外部类中的静态成员。
内部类定义在局部位置上
也可以直接访问外部类中的成员。
同时可以访问所在局部中的局部变量,但必须是被final修饰的。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
抽象类 包含抽象方法的类 抽取超类
------------------------------------------------------------------
//抽象定义:
//抽象就是从多个事物中将共性的,本质的内容抽取出来。
//例如:狼和狗共性都是犬科,犬科就是抽象出来的概念。
抽象类:
Java中可以定义没有方法体{}的方法,该方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类。
抽象方法的由来:
多个对象都具备相同的功能,但是功能具体内容有所不同,那么在抽取过程中,只抽取了功能定义,并未抽取功能主体,那么只有功能声明,没有功能主体的方法称为抽象方法。
例如:狼和狗都有吼叫的方法,可是吼叫内容是不一样的。所以抽象出来的犬科虽然有吼叫功能,但是并不明确吼叫的细节。
=========================================
特点:
1,抽象类和抽象方法必须用abstract关键字来修饰。
2,抽象方法只有方法声明,没有方法体,定义在抽象类中。
格式:修饰符 abstract 返回值类型 函数名(参数列表) ; —— 没有花括号{}
3,抽象类不可以被实例化,也就是不可以用new创建对象。原因如下:
抽象类是具体事物抽取出来的,本身是不具体的,没有对应的实例。例如:犬科是一个抽象的概念,真正存在的是狼和狗。
而且抽象类即使创建了对象,调用抽象方法也没有意义。
4,抽象类通过其子类实例化,而子类需要覆盖掉抽象类中所有的抽象方法后才可以创建对象,否则该子类也是抽象类。
1,抽象方法:没有函数体的方法,必须使用abstract修饰。
抽象方法必须在抽象类中。抽象类可以没有抽象方法
抽象类不能实例化。
一个对象的创建必须经过构造函数;一个对象的创建是它整个家族树的创建
抽象类有构造函数 只是不让创建对象 它中的构造函数无法创建一个完整的对象
非法修饰符
---------------------------
Private + abstract
Final + abstract
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Day 06
回顾:OOP class类成员
----------------------------------
构造函数:和类同名,没有返回值,可以重载
第一行要么this()要么super() 默认是super() 小括号调用的是构造函数……
成员变量:类中声明的字段也叫属性 修饰符:private public final
成员函数:普通方法
静态 和对象无关和类有关 静态代码块:类加载时候执行
extends 单重继承 多层继承
抽象类:abstract修饰类,不能实例化,一定有构造函数,可以没有抽象方法,但是抽象方法必须在抽象类中或者接口当中
抽象方法:没有函数体的方法。用abstract修饰
Final
-----------------
Final class //不能继承
final 方法 //不能重写
final 变量 //常量
内部类
-------------------------
访问局部变量,不允许赋值 内部类就像个黑匣子一样 你不能在其中赋值 外部看不到
只可以访问其他类中的静态内部类
JVM
------------------------
方法区:
栈区: -Xss 修改栈的大小 死递归可以栈的溢出
堆区: -Xmx最大值 -Xms初始值 对象太多 空间沾满就会溢出
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
接口:interface {} 类似USB
----------------------------------------------------------------------------------------
接口中的成员修饰符是固定的。
成员常量:public static final
成员函数:public abstract 接口中所有方法都是抽象的(就是没有方法体,不一定用abstract修饰)
发现接口中的成员都是public的
接口的出现将“多继承”通过另一种形式体现出来,即“多实现”。
>>>>类与接口之间是实现关系,而且类可以继承一个类的同时实现多个接口。接口与接口之间可以有继承关系。接口可以继承多个接口。
>>>>>接口与抽象类
共 性: |
都是不断抽取出来的抽象的概念 |
区别 1: |
抽象类体现继承关系,一个类只能单继承 接口体现实现关系,一个类可以多实现 |
区别 2: |
抽象类是继承,是 "is a "关系 接口是实现,是 "like a"关系 |
区别 3: |
抽象类中可以定义非抽象方法,供子类直接使用 接口的方法都是抽象,接口中的成员都有固定修饰符 |
接口是对外 暴露的规则
接口是程序的功能扩展
接口的出现降低耦合(交互)度 (高内聚低耦合——内部联系越紧越好,外部模块之间联系越松散越好)
接口可以用来多实现 implements 加s 单数第三人称
==========================================
接口:最低标准——符合它就可以
接口和类相似 编译后 是xxx.class
class InterfaceDemo{
public static void main(String[] args){
WomenStar ws = new WomenStar();
ws.white();
ws.rich();
TuHao th = new TuHao();
th.marry(ws);
}
}
//定义接口
interface White{
public void white();
}
interface Rich{
public void rich();
}
//类实现接口
class WomenStar implements White,Rich{
public void white(){
System.out.println("很白!");
}
public void rich(){
System.out.println("有钱!");
}
}
class TuHao{
//white是接口
public void marry(White w){
w.white();
}
}
????一般出现接口A这要完成:类Xxxx实现接口A;类Yyyyy含调用接口A类型的参数的函数——类似:实现接口的类|MP3(有数据线的一头插口)<<>>接口|数据线<<>>含有接口类型参数函数的类|电脑(有数据线的另一头接口)????不知道对不对???
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
多态 多种状态(形态)——好丈夫+好父亲+好儿子
================================
>>定义:某一类事物的多种存在形态。
例:动物中猫,狗。
>>猫这个对象对应的类型是猫类型
猫 x = new 猫();
>>同时猫也是动物中的一种,也可以把猫称为动物。
动物 y = new 猫();
动物是猫和狗具体事物中抽取出来的父类型。父类型引用指向了子类对象
>>程序中体现:父类或者接口的引用指向或者接收自己的子类对象。
>>好处和作用:多态的存在提高了程序的扩展性和后期可维护性
>>前提:需要存在继承或者实现关系
要有覆盖操作
==============
多态特点: 类中方法可以覆盖,成员变量(资产)不能覆盖。
>>成员函数:
编译时:要查看引用变量所属的类中是否有所调用的成员。
在运行时:要查看对象所属的类中是否有所调用的成员。
>>成员变量:只看引用变量所属的类。
===============
用到多态的时候 只能看到部分(超类)——用父类视角看子类!视角减小!!
赋值强转:必须有继承关系
左边的变量类型是站在那个角度来观察
继承和实现是多态的前提
函数的调用一定要注意它的可见性
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
匿名内部类 就是内部类的简化写法。
---------------------------------------------------------------------------
前提: 内部类可以继承或实现一个外部类或者接口。
格式为:匿名内部类对象
new 外部类名或者接口名(){覆盖类或者接口中的代码,(也可以自定义内容)}
-----------继承抽象类来创建匿名内部类对象--------------------
class NoNameDemo{
public static void main(String[] args){
//匿名内部类对象 多态创建对象 是以父类视角
Pet p = new Pet(){
public void meng(){
run ();
System.out.println("ameng");
}
public void run(){
System.out.println("runing………………");
}
};
p.meng();
//p.run(); 无法调用 父类中没有run()函数
}
}
abstract class Pet{
abstract public void meng();
}
这样就可以调用run()方法了!!
--------------------------------------------------------------------------------------
一般函数名后面的()就是构造函数的意思
匿名内部类是用超类或接口的视角来看的(上面多态一个性质!)
简单理解:就是建立一个带内容的外部类或者接口的子类匿名对象。
什么时候使用匿名内部类呢?通常在使用方法是接口类型参数,并该接口中的方法不超过三个时,可以将匿名内部类作为参数传递。增强阅读性。
匿名内部类的好处:将类的定义,方法的实现,对象的创建一气呵成
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
GUI:
graphic user interface 图形用户接口
IDE 集成开发环境
适配器模式 adapter
---------------------------------------
GOF,gang of four四人帮——java设计模式——23种
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。(用电器做例子,笔记本电脑的插头一般都是三相的,即除了阳极、阴极外,还有一个地极。而有些地方的电源插座却只有两极,没有地极。电源插座与笔记本电脑的电源插头不匹配使得笔记本电脑无法使用。这时候一个三相到两相的转换器(适配器)就能解决此问题,而这正像是本模式所做的事情。)
方法链编程+适配器模式
-------------------------------------------------------------------------
class AdapterDemo{
public static void main(String[] args){
Button b = new Button();
//匿名内部类对象
b.setListener(new MouseAdapter(){
public void onClick(){
System.out.println("b单击");
}
public void onDbClick(){
System.out.println("b双击");
}
});
b.click();
b.dbClick();
Button c = new Button();
c.setListener(new MouseAdapter(){
public void onClick(){
System.out.println("c单击");
}
});
c.click();
//融合上面c的所有操作(16行-22行内容)
//方法链编程
new Button().setListener(new MouseAdapter(){
public void onClick(){
System.out.println("匿名Button单击");
}
public void onDbClick(){
System.out.println("匿名Button双击");
}
}).click()
.dbClick();
}
}
//按钮
class Button{
private MouseListener l;
public Button setListener(MouseListener l){
this.l = l;
return this; //对应Button
}
public Button click(){
l.onClick();
return this; //对应Button
}
public void dbClick(){
l.onDbClick();
}
}
interface MouseListener{
public void onClick();
public void onDbClick();
public void onRightClick();
public void onMiddleClick();
}
//适配器
abstract class MouseAdapter implements MouseListener{
public void onClick(){}
public void onDbClick(){}
public void onRightClick(){}
public void onMiddleClick(){}
}
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Day07
回顾:面向接口编程
----------------------------
降低耦合度 接口是最低标准 没数量上的限制 可多重继承
设计模式——适配器模式:预实现,主要用在匿名内部类对象
Java中方法是可以覆盖但属性不可以覆盖
+++++++++++++++++++++++++++++++++++++++++
异常 Exception 例外
-----------------------------------------------
异常在Java中是一个体系:Java在设计异常体系时,将容易出现的情况都封装成了对象。
=======================
>>异常的体系
Throwable 可抛出的 是所有异常的超类
java.lang.Objict
|
|-----java.lang.Throwable
|
|----java.lang.Error //硬伤,很严重
|----java.lang.Exception //
Error------通常出现重大问题如:运行的类不存在或者内存溢出等。
不编写针对代码对其处理
Exception在运行时运行出现的一起情况,可以通过try catch finally来解决
>>Exception和Error的子类名都是以父类名作为后缀
Exception in thread "main" java.lang.NullPointerException 空指针异常
Throwable中的方法
----------------------------------
getMessage() 获取异常信息,返回字符串。
toString() 获取异常类名和异常信息,返回字符串。
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。
printStackTrace(PrintStream s) 通常用该方法将异常内容保存在日志文件中,以便查阅。类似重定向
throws和throw
------------------------------------
>>throws用于标识函数暴露出的异常。//在方法中声明抛出异常的关键字
>>throw用于抛出异常对象。// 抛出异常对象的指令(同理new——只要创建对象就要new一下,只要抛出异常对象就throw一下)
>>throws与throw的区别:
throws用在函数上,后面跟异常类名。 声明异常的
throw用在函数内,后面跟异常对象。 抛出异常对象
>>定义功能方法时,需要把出现的问题暴露出来让调用者去处理。那么就通过throws在函数上标识。
>>在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
==================================================
try
{
需要检测的代码;
}
catch(异常类 变量) //catch(Exception e) catch(Throwable e)
{
异常处理代码;
}
finally
{
一定会执行的代码;
}
Finally代码块只有一种情况不会被执行。就是在之前执行了System.exit(0)。
处理过程:
Try中检测到异常会将异常对象传递给catch,catch捕获到异常进行处理。
Finally里通常用来关闭资源。比如:数据库资源,IO资源等。
需要注意:try是一个独立的代码块,在其中定义的变量只在该变量块中有效。
如果在try以外继续使用,需要在try建立引用。在try对其进行初始化。IO,Socket就会遇到。
可以有多个catch语句,catch的顺序需要注意:子类异常需要先行catch,否则代码不可达,编译不通过
方法在重写时,不能声明抛出新的异常类型,只能是原有异常类型体系(异常的子类)
=================
自定义异常
自定义类继承Exception或者其子类。
通过构造函数定义异常信息。
例:
AgeTooSmallException AgeTooBigException
------------------------------------------------------------
class ExceptionDemo3{
public static void main(String[] args) throws AgeTooSmallException,AgeTooBigException
{
Person p =new Person();
p.setAge(150);
p.setAge(-1);
System.out.println("over");
}
}
//自定义异常
class Person{
private int age;
public int getAge(){
return age;
}
public void setAge(int age) throws AgeTooSmallException,AgeTooBigException{
if(age < 0){
throw new AgeTooSmallException("年龄小于0,不合法");
}
if(age > 200){
throw new AgeTooBigException("年龄大于200,不合法");
}
this.age = age;
}
}
//自定义异常
class AgeTooSmallException extends Exception{
public AgeTooSmallException(){}
public AgeTooSmallException(String msg){
super(msg);
}
}
class AgeTooBigException extends Exception{
public AgeTooBigException(){}
public AgeTooBigException(String msg){
super(msg);
}
}
-------------------------------------------------------------
三角形非法参数异常。
非法日期异常
通过throw将自定义异常抛出。
===========异常细节===============================
>>RuntimeException(运行时异常)以及其子类如果在函数中被throw抛出,可以不用在函数上声明。
>>一个方法被覆盖时,覆盖它的方法必须抛出相同的异常或异常的子类。
>>如果父类抛出多个异常,那么重写(覆盖)方法必须抛出那些异常的一个子集,不能抛出新的异常。
>>介绍异常在分层设计时的层内封装。
++++++++++++++++++++++++++++++++++++++++++++++++
package 包 类似于文件夹,组织管理类的结构。
------------------------------------------------------------------------------------------------------------
>>>对类文件进行分类管理。>>>给类提供多层命名空间(根包,子包……)。>>>写在程序文件的第一行。>>>类名的全称的是 包名.类名。>>>包也是一种封装形式
包名:(邮箱地址相反) www.sohu.com:com.sohu.www
路径和包名的区别:/和. 的区别
路 径:com\it18zhang\java\PackageDemo.class
完整类名:com.it18zhang.java.PackageDemo
--------------------------------
>>>编译程序
javac -d classes(文件夹名) xxx.java //-d指定存放classes文件的位置同时生成对应的文件夹目录树(tree命令可查看)
javac -cp classes -d . xxxx.java //-cp指定编译程序时需要搜索的类路径顺序 可以多个路径用分号; 隔开
>>>运行程序:运行Java程序,需要指定类的完整类名(全限定名)
Java -cp classes com.it18zhang.java1.PackageDemo1 // 指定
>>>含有包名的类需要使用public修饰,否则无法在其他包中访问
package com.it18zhang.java1;
public class PackageDemo1{
…………
}
>>>引用包中的类,需要使用import关键字
import com.it18zhang.java1;
public class Xxx{
………
}
DOS命令: tree /F //输出目录树
rmdir /S/Q //递归删除文件夹,不提示
classpath
----------------------------
给JVM提供的一个环境变量。指定类或者包所在的路径。
classpath变量值的最后有分号与无分号的区别。
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
权限修饰符(可见性)
---------------------------------------------------------
1,public //公有的
2,protected //受保护的
3,default //默认的
4,private //私有的
被访问的包中的类权限必须是public的。
类中的成员权限:public或者protected
protected是为其他包中的子类提供的一种权限
|
public |
protected |
default |
private |
同一类中 |
√ |
√ |
√ |
√ |
同一包中 |
√ |
√ |
√ |
|
子类 |
√ |
√ |
不同包子类不能访问 |
|
不同包中 |
√ |
|
|
|
关系:同类>同包>子类>不同包
import 简化类名
---------------------
一个程序文件中只有一个package,可以有多个import。
用来导包中的类,不导入包中的包。
通常写import mypack.Demo;
+++++++++++++++++++++++++++++++++++++++++++++++++++++
jar包 java archieve,Java归档,打包
--------------------------
jar war ear har
jar展现出来的一定是根包 对Java的类文件进行归档
jar cvf xxx.jar -C foo/. //将foo目录下所有文件进行归档生成xxx.jar文件
jar cvfe xxx.jar com.it18zhang.e.EE -C foo/classes . 归档时指定入口点,会在清单文件中添加Mai-Class属性
通过jar文件执行程序
Java -cp xxx.jar com.it18zhang.e.EE
java -jar xxx.jar >>>有归档指定入口点的jar包
----------------------------------------------------
Java的压缩包 方便项目的携带。
方便于使用,只要在classpath设置jar路径即可。
数据库驱动,SSH框架等都是以jar包体现的
通过jar.exe工具对jar的操作。DOS命令
创建jar包
jar -cvf mypack.jar packa packb
jar -cvf mypack.jar -C classes/. (点)
查看jar包
jar -tvf mypack.jar [>定向文件]
解压缩
jar -xvf mypack.jar
自定义jar包的清单文件
jar –cvfm mypack.jar mf.txt packa packb
{c:创建压缩文档。f:制定存档名称。v:显示详细信息。m:加入自定义清单信息。
通常应用与Java制作的图形界面程序。在清单文件中其中定一个Main-Class:空格 带有主函数的类名回车
在设置一下jar文件的打开方式通过javaw –jar就可以双击执行了。}
java -cp myjar.jar com.it18zhang.e.EE
java -cp myjar.jar;myjar2.jar com.………… cp 指类路径
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
day08
多线程 (不要较真,多线程执行期间CPU调用线程有一定的随机性!)
====================
进程:运行时(runtime)的应用程序。进程之间的内存不是共享的(独占)。
进程间通信使用socket(套接字)。
线程:进程内并发执行的代码段。线程之间共享内存。多线程能够创建灵活响应的桌面程序。
每个运行着的线程对应一个stack(栈)。应用程序至少有一个线程(主线程)。
线程类:java.lang.Thread
>>>>>>>>>>>>>>>>>>>>>>>>>>>>多线程的进程代码多为循环!
创建线程方式一
-------------------------------
继承Thread类
1,子类覆盖父类中的run方法,将线程运行的代码存放在run中。
2,建立子类对象的同时线程也被创建。
3,通过调用start()开启线程
java.lang.Thread
------------------------------
1,yield 放弃、礼让、谦逊(瞬时的状态)
Thead.yield()方法
让当前线程谦让出CPU抢占权,具有谦让之意,瞬时动作。
yiled()是静态方法 和对象无关
------------------------------------------
class ThreadDemo2{
public static void main(String[] args){
//创建对象
MyThread t1 = new MyThread("线程1");
MyThread t2 = new MyThread("线程2!!");
//直接调用run(),就不是线程而是主函数的run()调用,run()还是压栈到主函数的栈中(每个运行着的线程对应一个stack(栈))
//t1.run(); //线程中run()不是我们调用,是CPU调用的,我们只是告诉CPU可以调用了(用start()告知)
t1.start();
t2.start();
}
}
//线程
class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name = name;
}
public void run(){
for(;;){
System.out.println("我是" + name);
Thread.yield();
}
}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2,join 加入、等待。案例:Player类,打麻将
Thread.sleep()方法 sleep()是静态方法可以直接类来调用! wait()不是静态方法
让当前线程休眠指定毫秒数。
释放CPU抢占权,和锁旗标监视权没关系。
Thread.join()方法
当前线程等待指定的线程结束后才能继续运行;
Thread t =……
t.join();
……
-----------------
class ThreadDemo3{
public static void main(String[] args){
Player p1 = new Player("唐僧",3000);
Player p2 = new Player("孙悟空",200);
Player p3 = new Player("猪八戒",1500);
Player p4 = new Player("沙僧",1000);
p1.start();
p2.start();
p3.start();
p4.start();
try{
p1.join();
p2.join();
p3.join();
p4.join();
}
catch(Exception e){
}
System.out.println("开局!");
}
}
class Player extends Thread{
private String name;
private int time;
public Player(String name,int time){
this.name = name;
this.time = time;
}
public void run(){
System.out.println(name + "出发了!");
try{
//异常
Thread.sleep(time);
}
catch(Exception e){
}
System.out.println(name + "到了!用时:" + time);
}
}
-------------------------------------------------------------------------------
Interrupted Exception 中断故障(异常)——join()函数必须try{}catch(){}一下
->>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
3,deamon ['di:mən] 守护。守护线程像服务员
Thread.setDaemon(true);方法 ()里是boolean 类型:true false
为其他线程提供服务的线程,若进程中剩余的线程都是守护线程的话,则进程终止。
------------------------------
class ThreadDemo4{
public static void main(String[] args){
Box no1 = new Box("NO1",7000);
Box no2 = new Box("NO2",2000);
Waiter w = new Waiter();
w.setDaemon(true); //设置守护进程
no1.start();
no2.start();
w.start();
}
}
//线程1
class Box extends Thread{
private String no;
private int time;
public Box(String no,int time){
this.no = no;
this.time = time;
}
public void run(){
System.out.println(no + "号房间开始使用!");
try{
Thread.sleep(time);
}catch(Exception e){}
System.out.println(no + "号房间消费时长:" + time +"!结束消费……");
}
}
//服务员线程
class Waiter extends Thread{
public void run(){
while(true){
System.out.println("巡房时间:" + (new java.util.Date()));//打印系统时间
try{
Thread.sleep(1000);
}catch(Exception e){}
}
}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
最常见的多线程场景就是资源共享的循环操作:如卖票
卖票牵扯到的知识点:
1,静态和对象无关和类有关,相当于一个“全局变量”(Java中没有全局变量)——类上的静态变量,所有对象都可以访问的)
2,减减 —— 原子性操作,就无法分割的操作|a=a-1就不是;并发操作时前者不会重复,后者则有一定几率出现重复!!
------------------------------------------
多线程难点:线程间通信,共享资源的问题
解决办法:锁,防止并发访问,由并行改串行;锁起到参照物(信号灯),锁旗标的作用;
锁机制:类似在一个封闭式取款机外有很多没有排队的人要使用!——锁开,抢(锁定权),锁上,其他人等待:保证只有仅有一人在使用取款机!!!
同步(synchronized) ['sɪŋkrənaɪzd] 同步的
-------------------------------------
同步的前提:
同步需要两个或者两个以上的线程。
多个线程使用的是同一个锁。
未满足这两个条件,不能称其为同步。
同步的弊端:当线程相当多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
CAS:compare and set 比较并设置机制:避免同步带来的低效率
同步代码块↓↓↓ 同一时刻只能有一个线程使用同步代码块的内容
synchronized(对象)
{
需要同步的代码;
}
同步可以解决安全问题的根本原因就在那个对象上。对象为synchronize的参照物,保证是同一个!!!该对象如同锁的功能。
**********************************
同步代码块的执行期间,线程始终持有对象的监控权,其他线程处于阻塞状态!
--------------------------------------------------------------------------
class ThreadDemo5{
public static void main(String[] args){
Saler s1 = new Saler("票务甲");
Saler s2 = new Saler("票务乙");
Saler s3 = new Saler("票务丙");
s1.start();
s2.start();
s3.start();
}
}
class Saler extends Thread{
static int tickets = 30;//30张票
static Object lock = new Object();//锁旗标
//静态的锁保证了new Saler只有一个lock
private String name;
public Saler(String name){
this.name = name;
}
public void run(){
while(true){
int t =getTicket();
if(t == -1){
return ;
}
else{
System.out.println(name + ":" + t );
}
}
}
//取票
public int getTicket(){
synchronized(lock){
int t = tickets;
tickets = tickets - 1;
return t < 1 ? -1 : t ;
}
}
}
//不用静态lock,也可主函数里新建lock来保证同一把锁(见上图)
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
票池
------------------------
class ThreadDemo6{
public static void main(String[] args){
TickertPool p = new TickertPool();//建立票池
Saler s1 = new Saler("票务甲" , p);
Saler s2 = new Saler("票务乙" , p);
Saler s3 = new Saler("票务丙" , p);
s1.start();
s2.start();
s3.start();
}
}
class Saler extends Thread{
private String name;
private TickertPool pool;
public Saler(String name , TickertPool pool){
this.name = name;
this.pool = pool;
}
public void run(){
while(true){
int t =pool.getTicket();
if(t == 0){
return ;
}
else{
System.out.println(name + ":" + t );
}
}
}
}
//票池
class TickertPool{
private int tickets = 20 ;
public int getTicket(){
synchronized(this){ //同步代码块,以本身为锁旗标!
int temp = tickets ;
tickets = tickets - 1 ;
return temp > 0 ? temp : 0 ;
}
}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
同步函数:在函数上加上synchronized修饰符即可。(见上图↑↑↑)
synchronized(this) === 同步方法
此同步方法(函数)是非静态的,那么同步方法就是以当前对象做锁旗标。
----------------------------
同步静态方法:使用类的描述符(.class)作为同步的标记(锁旗标)。——直接不用再创建对象。
静态方法只能访问静态成员(静态产生在对象创建之前,以xxx.class为标记)
public static synchronized xxxx(…){
……
}
-------------------------------------------------------------------------------------
集合:容量不固定,里面只能放对象(数组是固定长度)
int i = 100 :: Integer ii = new Integer(100); //集合里只能是对象,基本数字类型元素转换成数字对象元素(对数字包装形成类)
*****************************************************
通知者 == 同步锁
wait()函数
wait(1000);加时间限,最多等1秒!解决死锁问题
让当前的线程进入锁旗标的等待队列,释放CPU抢占权,还释放锁旗标的监控权。等待Object.notify()来通知可以“抢”了 ——Object.notify()通知等待队列中的一个线程被唤醒!
Object.notifyAll():通知所有线程可以抢占CPU和锁旗标监控权,可以解决死锁
---------多线程-生产者-消费者---------------
class ThreadDemo7{
public static void main(String[] args){
//使用java中集合类,List是列表。
Pool pool = new Pool();
Productor p1 = new Productor("生产者1",pool);
Productor p2 = new Productor("生产者2",pool);
Productor p3 = new Productor("生产者3",pool);
Consumer c1 = new Consumer("消费者1",pool);
Consumer c2 = new Consumer("消费者2",pool);
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
}
}
//生产者
class Productor extends Thread{
static int i = 0 ;
private String name ;
private Pool pool ;
public Productor(String name ,Pool pool){
this.name = name ;
this.pool = pool ;
}
public void run(){
//int i = 0 ;
while(true){
pool.add(i ++);
try{
Thread.sleep(1000);
}catch(Exception e){}
System.out.println("生产 :" + i );
}
}
}
//消费者
class Consumer extends Thread{
private String name ;
private Pool pool ;
public Consumer(String name ,Pool pool){
this.name = name ;
this.pool = pool;
}
public void run(){
//int i = 0 ;
while(true){
int i = pool.remove();
try{
Thread.sleep(1000);
}catch(Exception e){}
System.out.println("消费 : " + i );
}
}
}
class Pool{
private java.util.List<Integer> list = new java.util.ArrayList<Integer>();
//容器最大值
private int MAX = 100 ;
//添加元素
public void add(int n){
synchronized(this){
try{
while(list.size() >= MAX){
this.wait();
}
list.add(n);
System.out.println("库存:" + list.size());
this.notify();
}catch(Exception e){
e.printStackTrace();
}
}
}
//删除元素
public int remove(){
synchronized(this){
try{
while(list.size() == 0){
this.wait();
}
int i = list.remove(0);
this.notify();
return i ;
}
catch(Exception e){
e.printStackTrace();
}
return -1 ;
}
}
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
线程间通信
--------------------------
wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中:
1,这些方法存在于同步中。
2,使用这些方法时必须要标识所属的同步的锁。
3,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。
wait(),sleep()区别:
wait():释放cpu执行权,释放锁。
sleep():释放cpu执行权,不释放锁。
解决死锁:notifyAll()或添加了时间限的wait(1000)函数
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++