多线程
static Thread.currentThread(); 获取当前线程对象
this.getName(); 获取线程名称
setName ; 设置线程名称
1.定义类实现Runnable接口
2.覆盖Runnbale接口中的run方法,将线程要运行的代码存放在该run方法中。
3.通过Thread类建立线程对象
4.将Runnbale接口的子类对象作为实际参数传递给Thread类的构造函数
为什么要将Runnable接口的子类对象传递给Thread的构造函数,因为,自定义的run方法所属的对象是Runnable接口的子类对象。所以要让线程去指定对象的run方法,就必须明确该run方法所属的对象。
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法
实现方式好处:避免了单继承的局限性。在定义线程时,建议使用实现方式。
两种方式区别:
继承Thread:线程代码存放Thread子类run方法中。
实现Runnable,线程代码存放在接口的子类的run方法。
通过分析发现多线程的运行出现了安全问题,问题的原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行
java对于多线程的安全问题提供了专业的解决方式。就是同步代码块。synchronized(对象){需要被同步的代码}
对象如同锁。持有锁的线程可以在同步中执行,没有只有锁的线程即使获取了cpu的执行权也进不去,因为没有锁。
火车上的卫生间--经典。
同步的前提:
1.必须要有两个或者两个以上的线程。
2.必须是多个线程使用同一个锁。必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,较为消耗资源。
同步有两种变现形式
同步代码块
同步函数
同步函数用的是那一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用,就是this.所以同步函数使用的锁就是this.
如果同步函数被静态修饰后,使用的锁是什么呢?通过验证,发现不在是this.因为静态方法中也不可以定义this。静态进内存时,内存中没有本来对象,但是一定有该类对应的字节码文件对象。类名.class 该对象的类型是Class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
//懒汉式 延迟加载 如果多线程会出现安全问题 需要双重判断, 锁里面的对象是该类所属的字节码文件
class Single{
private static Single s = null;
private Single(){}
public static Single getInstance(){
if(s==null){
synchronized(Single.class){
if(s==null)
s = new Single();
}
}
return s;
}
}
class SingleDemo{
public static void main(String[] args){
}
}
死锁:
你持有一个锁,我持有一个锁,我到你那里运行,要拿你的锁,你要到我里面运行,要拿我的锁,你也不给锁,我也不让锁,程序就挂了,产生死锁。
同步中嵌套同步,而锁却不同
线程间通信:
其实就是多个线程在操作同一个资源,但是操作的动作不同。
等待唤醒机制:
class Res{
String name;
String sex;
boolean flag = false;
}
class Input implements Runnable{
private Res r;
Input(Res r){
this.r = r;
}
public void run(){
int x = 0;
while(true){
synchronized(r){
if(r.flag){
try(r.wait();}catch(Exception e){}
if(x==0){
r.name = "mike";
r.sex = "man";
}else{
r.name = "丽丽";
r.sex = "女女";
}
x = (x+1)%2;
r.falg =true;
r.notify();
}
}
}
}
}
class Output implements Runnable{
private Res r;
Output(Res r){
this.r = r;
}
public void run(){
while(true){
synchronized(r){
if(!r.flag){
try(r.wait();}catch(Exception e){}
System.out.println(r.name+"..."+r.sex);
r.flag = false;
r.notify();
}
}
}
}
}
notifyAll();
wait
notify
notifyAll
都使用在同步中,因为要对持有监视器(锁)的线程操作。所以要使用在同步中,因为只有同步才具有锁。为什么这些操作线程的方法要定义在Object类中呢?因为这些方法在操作同步中线程时,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程,可以被同一个锁上notify唤醒,不可以对不同锁中的线程进行唤醒。也就是说等待和唤醒必须是同一个锁。而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。
|