问题:如何在多线程环境下安全地共享变量
首先理解一个概念:临界区
临界区是一个用来访问共享资源的代码块,这个代码块在同一个时间允许一个线程执行
JAVA中使用synchronized来声明一个方法或者一段代码块为临界区
每个对象只有一个锁,当它被某线程占有了,其他想获取锁的线程就只能阻塞
synchronized的语义:
1.对象锁
在同一个对象的临界区,同一时间只能有一个允许被访问
public synchronized void a() {
}
public void b() {
//等同于在方法上加sync,都是锁住当前对象
synchronized (this) {
}
}<pre name="code" class="java">
//没有明确的锁对象,自己创建一个
private final Object obj = new Object();
public void c() {
synchronized(obj) {
}
}
2.类锁
2.静态方法 -- 所有对象使用同一个类锁
但是其他线程可以访问这个对象的其他非静态方法,
即只会锁住类里面所有的静态方法
此时会出现这种情况:
一个线程访问对象的非静态synchronized方法,一个线程访问类的静态synchronized
如果这两个方法修改了相同的数据,那么就会出现数据不同步
public synchronized static void statSync() {
}
//针对类的锁,该类的所有对象都是用这个锁
public void f() {
synchronized (this.getClass()) {
}
}
同时访问静态和非静态方法测试:
创建共享变量
public class SyncModel {
public synchronized void a() {
System.out.println("nonstatic synchronized");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static synchronized void b() {
System.out.println("static synchronized");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
创建测试类
public class TestSync implements Runnable {
private int id;
private SyncModel model;
public TestSync(SyncModel model, int id) {
this.model = model;
this.id = id;
}
public static void main(String[] args) {
SyncModel model = new SyncModel();
new Thread(new TestSync(model, 0)).start();
new Thread(new TestSync(model, 1)).start();
}
@Override
public void run() {
if (id == 0) {
model.a();
} else {
model.b();
}
}
}
把方法b改为非静态即可发现有一个线程处于阻塞状态
以下为阻塞时的部分线程堆栈信息 —— 通过 ctrl + break 或者 jstack -l jvmpid 可以查看
2016-03-28 21:05:44
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x000000000241e800 nid=0x2514 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Thread-1" #13 prio=5 os_prio=0 tid=0x000000001b2a9800 nid=0x9d0 waiting for monitor entry [0x000000001d3bf000]
java.lang.Thread.State: BLOCKED (on object monitor)
at zj.SyncModel.b(SyncModel.java:19)
<span style="color:#ff0000;">- waiting to lock <0x00000007807fa3b0> (a zj.SyncModel)</span>
at zj.TestSync.run(TestSync.java:27)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" #12 prio=5 os_prio=0 tid=0x000000001b2a8800 nid=0x1c58 waiting on condition [0x000000001d6ae000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at java.lang.Thread.sleep(Thread.java:340)
at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
at zj.SyncModel.a(SyncModel.java:12)
- locked <span style="color:#ff0000;"><0x00000007807fa3b0></span> (a zj.SyncModel)
at zj.TestSync.run(TestSync.java:25)
at java.lang.Thread.run(Thread.java:745)
可以看到执行方法b的线程在等待执行方法a的线程释放锁