首页 > 代码库 > JAVA单例模式的实践

JAVA单例模式的实践

单例模式是JAVA设计模式中最常用、最重要的设计模式之一。

最简单的写法是:

public class TestSingleton {

    private static String ourInstance = null;
    //私有化构造器
    private TestSingleton() {
    }
    //提供静态方法给外部访问
    public static String getOurInstance(){
        if(ourInstance == null){
              ourInstance="单例模式赋值成功!!!";
              System.out.print(ourInstance);
        }

        return ourInstance;
    }
}

这种写法的单例模式是线程不安全的,下面用代码来模拟一下多线程并发,代码的执行情况:

public class Test {

    /**
     * 线程池
     */
   final static ExecutorService threadPool = Executors.newCachedThreadPool();

    /**
     * 并发数
     */
   private final static int CONCURRENT_COUNT = 10;
    /**
     *同步工具类?
     */
   final static CountDownLatch locks = new CountDownLatch(CONCURRENT_COUNT);

    public static void main(String[] args) {

        for (int i = 0; i < CONCURRENT_COUNT; i++) {
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    locks.countDown();
                    TestSinleton.getOurInstance();
                }
            } );
        }
    }

}

代码非常简单,用线程池执行CONCURRENT_COUNT 个任务,每个任务执行到

locks.countDown()

时被阻塞,当被阻塞的任务数达到CONCURRENT_COUNT个时,所有任务同时往下执行,这样模拟了多个用户并发的场景。(CountDownLatch 的使用方法)

思考:如果以上写法的单例模式线程安全,那么控制台只会打印一次:单例模式赋值成功!!!

运行3次程序,查看控制台输出:

"C:\Program Files\Java\jdk1.8.0_101\bin\java" ...
单例模式复制成功!!!单例模式复制成功!!!
Process finished with exit code 0
"C:\Program Files\Java\jdk1.8.0_101\bin\java" ...
单例模式赋值成功!!!单例模式赋值成功!!!单例模式赋值成功!!!
Process finished with exit code 1
"C:\Program Files\Java\jdk1.8.0_101\bin\java"...
单例模式赋值成功!!!
Process finished with exit code 1

发现并不是想的这样,很容易就出现了变量被多次赋值的情况。所以,以上的单例模式是线程不安全的。

思考为什么会出现这种情况?

因为多用户并发操作的情况下,if(ourInstance == null) 可能被同时执行,如果ourInstance为空的话,就会出现多个线程判断(ourInstance == null)为true了。

修改代码:

public class TestSinleton {

    private static String ourInstance = null;

    //私有化构造器
    private TestSinleton() {
    }
    //提供静态方法给外部访问
    public static String getOurInstance(){
        if(ourInstance == null){
           synchronized (TestSinleton.class){
                if(ourInstance == null){     
                    ourInstance="单例模式复制成功!!!";
                    System.out.print(ourInstance);
                }
            }
            //ourInstance="单例模式赋值成功!!!";
            //System.out.print(ourInstance);
        }
        return ourInstance;
    }


}

第一次判空,大部分线程被挡掉。继续往下是一个synchronized 方法,只允许单线程执行,所以第二次判空就不会存在多个线程同时执行的情况。

多次运行程序,查看控制台:

"C:\Program Files\Java\jdk1.8.0_101\bin\java" ...
单例模式复制成功!!!
Process finished with exit code 0

发现只会打印一次了。


 

JAVA单例模式的实践