1,饿汉模式代码:
public class SingleTest {
private static SingleTest instance = new SingleTest();
private SingleTest(){
}
public SingleTest getInstance(){
return instance;
}
}
2,懒汉模式代码:
public class SingleTest {
private static SingleTest instance = null;
private SingleTest(){
}
public SingleTest getInstance(){
if(null == instance)
instance = new SingleTest();
return instance;
}
}
3,懒汉模式中存在的多线程安全性问题
当有多个线程同时执行到if语句判断时:线程A执行if判断 instace==null;CPU切换到线程B执行,线程B执行if判断 instance==null,这样就 new 了两个SingleTest对象。线程B new 的SingleTest对象更改了线程A new 的SingleTest对象的引用。
if(null == instance)
instance = new SingleTest();
改进:
public class SingleTest {
private static SingleTest instance = null;
private SingleTest(){
}
public SingleTest getInstance(){
if(null == instance)
synchronized (SingleTest.class) {
if(null == instance)
instance = new SingleTest();
}
return instance;
}
}
使用了static 锁进行同步。
当有多个线程同时执行到if语句判断时(第一个if语句):线程A执行if判断 instace==null;CPU切换到线程B执行,线程B执行if判断 instance==null。但是只有一个线程能够获得 static 锁进入同步代码块,当第一个线程进入同步代码块后 new 了SingleTest对象,后面的进程再进入同步代码块时,进行if判断为假,就不会再创建第二个SingleTest对象了。
4,饿汉模式与线程安全的懒汉模式比较
①二者都是线程安全的。对于饿汉模式而言,它在JVM加载该类时就已经创建好SingleTest对象了,因此有多个线程调用getInstance()时始终只有一个SingleTest对象存在。
②不管需不需要SingleTest对象,只要一加载SingleTest.java类,就会创建SingleTest对象,比如:假设SingleTest类中还定义了其他静态方法,通过类名.静态方法名() 调用静态方法时,会创建SingleTest对象,尽管此时并不需要使用SingleTest的实例对象。
若创建SingleTest对象开销很大,则有性能问题。而对于懒汉模式而言,只有在需要SingleTest对象时,才会创建,因为它是通过调用getInstance方法,然后在该方法里面new的对象;而对于饿汉模式,SingleTest对象在加载时已经生成,getInstance()方法只是负责返回该对象。
5,其他的一些多线程问题
①static 锁 与 this 锁
静态同步方法与同步方法 使用的锁
静态同步方法使用的是static锁。调用静态同步方法相当于获得了一个以 类名.class 的锁,如下:
synchronized (SingleTest.class) {//static 锁
//processing....
}
同步方法使用的是this锁。调用同步方法相当于获得了一个当前对象的锁。因为对于方法而言,是通过 对象.方法名进行调用的,即该方法被调用时,一定是某个对象调用了它,同步方法就获得了该对象的锁,如下:
synchronized(this){
//processing
}
②线程执行了Thread.sleep(long)方法时,不会释放它所占有的锁。(注意:没有不带参数的sleep()方法)
private Object lock = new Object();
public void run(){
while(true)
{
synchronized(lock){
//do some processing
try{
Thread.sleep(100);
}catch(InterruptedException e){
}
//do another processing
}
}
}
当线程获得了 lock锁进行了一些处理,然后睡眠100ms。当它执行sleep(100)后,会放弃CPU,进入阻塞状态。但是,它不会释放占有的 lock 锁。只有当它睡眠100ms后,重新获得CPU,再执行完另一些处理退出syncronized代码块之后,才会释放 lock 锁。
多线程入门资料参考:JAVA 多线程编程深入详解--汪文君