零、背景
同事分享《Effective Java》
其中第十章,并发部分例子有争议
变量是否需要(代码如下) static?
几个大佬说需要加,我众目睽睽下反驳不需要,略尴尬
// 是否需要加 static?才能保证单例正确
private volatile Singleton singleton;
一、程序关键代码
1.1 原程序(错误)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton { private volatile Singleton singleton;
public Singleton() { }
private Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
|
1.2 正确程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Singleton { private static volatile Singleton singleton;
public Singleton() { }
private Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
|
二、验证
2.1 验证程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public class Singleton { private static final AtomicInteger COUNT = new AtomicInteger(0); private volatile Singleton singleton;
public Singleton() { }
private Singleton getInstance() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { System.out.println("new:" + COUNT.addAndGet(1)); singleton = new Singleton(); } } } return singleton; }
@Test public void test() { int taskCount = 700; final CountDownLatch begin = new CountDownLatch(1);
final ExecutorService exec = Executors.newFixedThreadPool(taskCount);
for (int index = 0; index < taskCount; index++) { submitTask(begin, exec); } System.out.println("开始执行"); begin.countDown(); exec.shutdown(); }
private void submitTask(CountDownLatch begin, ExecutorService exec) { Runnable run = () -> { try { begin.await(); Singleton singleton = new Singleton(); Singleton instance = singleton.getInstance(); System.out.println(Objects.isNull(instance)); } catch (InterruptedException e) { e.printStackTrace(); } }; exec.submit(run); } }
|
2.2 验证结果
带 static 的结果符合预期
对象只创建一次,并且返回结果均不为 null
2.2.1 不带 static
开始执行
new:1
new:2
new:3
false
true
省略几十行….
2.2.2 带 static
开始执行
new:1
false
三、原因分析
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。
而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
关键:static 变量在内存中只有一个副本,由所有对象共享。
四、参考