心得2--线程回顾之加深版--同步锁详解
这节与大家分析一下线程的高深部分,多线程的锁问题及同步问题,关键字:synchronized。下面从基础概念介绍起,后面与大家分享下两种单例模式:饿汉式和懒汉式中运行线程同步的情况。
1.互斥锁
µ 每个对象都对应于一个可称为“互斥锁”的标记,这个标记用来保证在任意时刻,只能有一个线程访问该对象。
µ Java对象默认是可以被多个线程共用的,只是在需要时才启动“互斥锁”机制,成为专用对象。
µ 关键字synchronized用来与对象的互斥锁联系
µ 当某个对象用synchronized修饰时,表明该对象已启动“互斥锁”机制,在任一时刻只能由一个线程访问,即使该线程出现堵塞,该对象的被锁定状态也不会解除,其他线程任不能访问该对象。
2.synchronized关键字的使用方式有两种:
¯ 用在对象前面限制一段代码的执行(同步代码块)
public void push(char c){
…
sychronized(this){
data[index]=c;
index++
}
}
¯ 用在方法声明中,表示整个方法为同步方法
public synchronized char pop(){
index--;
return data[index];
}
同步的两种表现形式简释为:
1).同步代码块
synchronized(对象){
需要同步的代码
}
2).同步函数: 使用的锁是this
public synchronized void show(){
}
同步代码块可以使用任意对象作为锁,同步方法使用的锁只有一个--this。static同步方法使用的锁是该方法所属类的对象。类型.class
µ 同步好处:解决了线程安全问题
µ 同步弊端
ü 降低了运行效率(判断锁是较为消耗资源的)
ü 同步嵌套,容易出现死锁
3. 死锁
两个线程A、B用到同一个对象s(s为共享资源),且线程A在执行中要用到B运行后所创造的条件。在这种前提下A先开始运行,进入同步块后,对象s被锁定,接着线程A因等待B运行结束而进入阻塞状态,于是B开始运行,但因无法访问对象s,线程B也进入阻塞状态,等待s被线程A解锁。最终的结果:两个线程互相等待,都无法运行。
4.线程同步通信
为避免死锁,就应该让线程在进入阻塞状态时尽量释放其锁定的资源,以为其他的线程提供运行的机会,Object类中定义了几个有用的方法:wait()、notify()、notifyAll()。
µ wait():被锁定的对象可以调用wait()方法,这将导致当前线程被阻塞并释放该对象的互斥锁,即解除了wait()方法当前对象的锁定状态,其他的线程就有机会访问该对象。
µ notify():唤醒调用wait()方法后被阻塞的线程。每次运行该方法只能唤醒一个线程。
µ notifyAll():唤醒所有调用wait()方法被阻塞的线程。
5. 单例中的同步:
懒汉式:
代码块方式:
classSingle{
private static Single s=null;
private Single(){}
public static Single getInstance(){
if(s==null)
synchronized(Singel.class){
if(s==null)
s=new Single();
}
return s;
}
函数方式:
classSingle{
private static Single s=null;
private Single(){}
public static synchronized SinglegetInstance(){
if(s==null)
s=newSingle()
return s;
}
Single.getInstance();
饿汉式:
classSingle{
private static Single s=new Single();
private Single(){}
public static Single getInstance(){
return s;
}
}