使用同步机制将单例模式中的懒汉式改写为线程安全的
public class BankTest {
}
class Bank{
private Bank(){
}
private static Bank instance=null;
public static Bank getInstance(){
if(instance==null){
//说明之前没有被创建
instance=new Bank();
}
return instance;
}
}
要求Bank这个类是单例的
如果在run方法中调用了getInstance方法,创建多个线程,在各自的run方法中,各自又调了getInstance。首次调用的时候instance是null,最开始的那个线程就进入了if语句,但进去之后可能就阻塞了,即使不阻塞,CPU也可能切换到其他的线程,使得当前线程进入就绪状态,另外的线程就进来了。同样判断instance还是null,所以也进入了if,所以会造出不止一个对象,就不是单例模式了
出现了线程的安全问题,因为存在共享数据为instance
注意return instance虽然用了共享数据,但不算是对共享数据的操作,但是判断和赋值都是明显的对共享数据进行操作
方法一:使用同步方法,声明为synchronized:
静态同步方法的锁是当前类本身Bank.class
class Bank{
private Bank(){
}
private static Bank instance=null;
public static synchronized Bank getInstance(){
if(instance==null){
//说明之前没有被创建
instance=new Bank();
}
return instance;
}
}
第一个进入的线程之后的其他线程进入进行判断if得到的结果都是false,直接就return
第二种方法:使用同步代码块:
快速形成同步代码块的方法,选中同步代码块中的代码,Alt+shift+z,再点击synchronized即可
class Bank{
private Bank(){
}
private static Bank instance=null;
public static Bank getInstance(){
synchronized (Bank.class) {
if(instance==null){
//说明之前没有被创建
instance=new Bank();
}
return instance;
}
}
}
还有效率更高的方法:
本质上第一个进入的线程执行完之后对象就创建好了,那么其他的线程只要不是第一个线程就可以直接拿对象走了,因为对象已经造好了,没有必要等了,所以可以把同步代码块包小一点,换句话说可以不把return instance看成是对共享数据的操作
最外层的if因为没有加同步所以都能判断,在同步代码块开始的地方就开始抢锁,假设线程一抢到了,new完之后返回对象,已经来到同步代码块头处的线程是需要等线程1的。但如果是再后来的线程,他们去判断if就已经不是null了,所以直接return而不会到同步代码块头等着了
class Bank{
private Bank(){
}
private static Bank instance=null;
public static Bank getInstance(){
if(instance==null) {
synchronized (Bank.class) {
if (instance == null) {//注意,不能把里面的if删掉
//说明之前没有被创建
instance = new Bank();
}
}
}
return instance;
}
}
写的时候建议写这种写法,效率比较高