0%

谈谈 Java-voliatile-and-synchronized

前言

我们知道,在多线程环境下使用竞争资源,为了保证程序正确性,进入临界区之前需要加锁保护。Java中的volatile和synchonized两个关键字作为同步手段常常令人疑惑。什么时候该用volatile,什么时候该用synchonized,一直不太明确,今天就来捋捋吧。

区别

关于两者的区别,老外有篇文章列了个表,此处引用一下(出处:https://www.javamex.com/tutorials/synchronization_volatile.shtml):

volatile用法

用法1

在多线程环境下改变标志位,这也是volatile关键字最典型的用法。

1
2
3
4
5
6
7
8
9
10
11
12
public class StoppableTask extends Thread {
//If the variable were not declared volatile (and without other synchronization), then it would be legal for the thread running the loop to cache the value of the variable at the start of the loop and never read it again.
private volatile boolean pleaseStop;
public void run() {
while (!pleaseStop) {
// do some stuff...
}
}
public void tellMeToStop() {
pleaseStop = true;
}
}

用法2

在单例模式中使用。至于为何要加volatile关键字,可以看此处:https://www.javamex.com/tutorials/double_checked_locking_fixing.shtml#。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton {
private static volatile Singleton instance;
private int field1, field2 ...
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}

synchronized用法

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
public class Test {
private Object count = new Object();
private final Object obj = new Object();
public synchronized void work1() {
// do something
}

public synchronized static void work2() {
// do something
}

public void work3() {
synchronized (this) {
// do something
}
}

public void work4() {
// 效果同work2
synchronized (Test.class) {
// do something
}
}

public void work5() {
synchronized (obj) {
// do something
}
}

public void work6() {
// 不推荐使用
synchronized (count) {
//因为锁对象在锁代码块中有可能发生改变,会导致锁的对象不是同一个如 count = new Object();
//所以应该把锁对象声明成final
// 当然,如果可以确定count对象只是修改对象属性值,不会修改对象引用,也是可以这么做的。

// do something
}
}
}

总结

下方是几种比较常见加锁的方案。自己使用锁(work1())可能会比synchronized快一丢丢,但是Java中synchronized关键字大部分情况下已满足需求,不到万不得已,没必要自己写锁保护。

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
public class Test {
//说明:零长度的byte数组对象创建起来将比任何对象都经济――查看编译后的字节码:生成零长度的byte[]对象只需3条操作码,而Object lock = new Object()则需要7行操作码。
private byte[] obj = new byte[0];
private final Lock lock = new ReentrantLock();
public void work1() {
lock.lock();
try {
// do something
} finally {
lock.unlock();
}
}

public void work2() {
synchronized (obj) {
// update count value
}
}

public void work3() {
synchronized (this) {
// do something
}
}
// 适用场景:http://blog.csdn.net/xiao__gui/article/details/8188833
public void work4() {
synchronized (Test.class) {
// do something
}
}

}

参考文章