在并发编程中经常会碰到多个执行线程共享资源的问题。例如多个线程同时读写文件,共用数据库连接,全局的计数器等。如果不处理好多线程之间的同步问题很容易引起状态不一致或者其他的错误。
同步不仅可以阻止一个线程看到对象处于不一致的状态,它还可以保证进入同步方法或者块的每个线程,都看到由同一锁保护的之前所有的修改结果。处理同步的关键就是要正确的识别临界条件(critical section),即多线程访问/修改共享资源的代码块。
关键字synchronized可以保证同一时刻,只有一个线程可以执行某个方法或者某个代码块。 此外Java语言规范保证读写一个变量是原子的,除非long或者double.虽然语言规范保证了线程在读取原子数据的时候,不会看到任意的数值,但是并不保证一个线程写入的值对于另一个线程是可见的,线程间的通信,同步是必要的。
synchronized同步方法:
我们可以把synchronized关键字放在方法前,保证同时只有一个线程执行该方法。需要注意的是如果一个类的静态方法和实例方法都有synchronized,那么同意时刻允许一个线程访问静态方法,另一个线程访问实例方法。例如:
private static synchronized void stopRequested(){
stopRequest = true;
}
synchronized同步块:
synchronized (this) {
// Java code
}
public class Cinema {
private long vacanciesCinema1;
private long vacanciesCinema2;
private final Object controlCinema1, controlCinema2;
public Cinema() {
controlCinema1 = new Object();
controlCinema2 = new Object();
vacanciesCinema1 = 20;
vacanciesCinema2 = 20;
}
public boolean sellTickets1(int number) {
synchronized (controlCinema1) {
if (number < vacanciesCinema1) {
vacanciesCinema1 -= number;
return true;
} else {
return false;
}
}
}
public boolean sellTickets2(int number) {
synchronized (controlCinema2) {
if (number < vacanciesCinema2) {
vacanciesCinema2 -= number;
return true;
} else {
return false;
}
}
}
}
public class TestStop {
private static boolean stopRequest;
public static void main(String[]args) throws InterruptedException{
Thread t = new Thread( new Runnable(){
public void run(){
int i = 0;
while(!stopRequest){
i++;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(2);
stopRequest = true;
}
}
public class TestStop {
private static boolean stopRequest;
private static synchronized void stopRequested(){
stopRequest = true;
}
private static synchronized boolean getStopRequest(){
return stopRequest;
}
public static void main(String[]args) throws InterruptedException{
Thread t = new Thread( new Runnable(){
public void run(){
int i = 0;
while(!getStopRequest()){
i++;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(2);
stopRequested();
}
}
在后续章节中,我们将介绍更多关于synchronize及Lock的内容。