由于同一进程内的多个线程共享内存空间在Java中就是共享实例当多个线程试图同时修改某个实例的内容时就会造成沖突因此线程必须实现共享互斥使多线程同步
最简单的同步是将一个方法标记为synchronized对同一个实例来说任一时刻只能有一个synchronized方法在执行当一个方法正在执行某个synchronized方法时其他线程如果想要执行这个实例的任意一个synchronized方法都必须等待当前执行 synchronized方法的线程退出此方法后才能依次执行
但是非synchronized方法不受影响不管当前有没有执行synchronized方法非synchronized方法都可以被多个线程同时执行
此外必须注意只有同一实例的synchronized方法同一时间只能被一个线程执行不同实例的synchronized方法是可以并发的例如class A定义了synchronized方法sync()则不同实例async()和async()可以同时由两个线程来执行
多线程同步的实现最终依赖锁机制我们可以想象某一共享资源是一间屋子每个人都是一个线程当A希望进入房间时他必须获得门锁一旦A获得门锁他进去后就立刻将门锁上于是BCD就不得不在门外等待直到A释放锁出来后BCD中的某一人抢到了该锁(具体抢法依赖于 JVM的实现可以先到先得也可以随机挑选)然后进屋又将门锁上这样任一时刻最多有一人在屋内(使用共享资源)
Java语言规范内置了对多线程的支持对于Java程序来说每一个对象实例都有一把锁一旦某个线程获得了该锁别的线程如果希望获得该锁只能等待这个线程释放锁之后获得锁的方法只有一个就是synchronized关键字例如
public class SharedResource {
private int count = ;
public int getCount() { return count; }
public synchronized void setCount(int count) { unt = count; }
}
同步方法public synchronized void setCount(int count) { unt = count; } 事实上相当于
public void setCount(int count) {
synchronized(this) { // 在此获得this锁
unt = count;
} // 在此释放this锁
}
红色部分表示需要同步的代码段该区域为危险区域如果两个以上的线程同时执行会引发沖突因此要更改SharedResource的内部状态必须先获得SharedResource实例的锁
退出synchronized块时线程拥有的锁自动释放于是别的线程又可以获取该锁了
为了提高性能不一定要锁定this例如SharedResource有两个独立变化的变量
public class SharedResouce {
private int a = ;
private int b = ;
public synchronized void setA(int a) { thisa = a; }
public synchronized void setB(int b) { thisb = b; }
}
若同步整个方法则setA()的时候无法setB()setB()时无法setA()为了提高性能可以使用不同对象的锁
public class SharedResouce {
private int a = ;
private int b = ;
private Object sync_a = new Object()
private Object sync_b = new Object()
public void setA(int a) {
synchronized(sync_a) {
thisa = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
thisb = b;
}
}