//Gate类表示人通过的门,counter记录人数,pass表示通过方法public class Gate {
private int counter = 0;
private String name = "Nobody";
private String address = "Nowhere";
public synchronized void pass(String name,String address){
this.counter++;
this.name = name;
this.address = address;
check();
}
public synchronized String toString(){
return "No." + counter + ":" +name +","+address;
}
private void check(){
if(name.charAt(0)!=address.charAt(0)){
System.out.println("********"+toString());
}
}
}
思考一:
看到代码中的synchronized,一定要思考“这个synchronized在保护什么呢”
在明确了“保护着什么”之后,接下来就需要思考“其他地方也妥善保护了吗”
当多个地方都使用字段时,虽然这边使用了synchronized保护,但是如果过那边没有保护,那么这个字段最终还是没有被保护。就像虽然把前门和后门都锁好了。但是如果窗户开着,那还是没有任何意义
在这段代码中,pass()与toString()方法都加上了synchronized,保护着字段,但是check()方法却没有加,这是否会成为那扇窗呢?
注意check()方法是private的哦,只被pass()方法调用,而pass()已经声明成了synchronized的,当然check()照样可以声明为synchronized,但可能会降低性能
思考二:
synchronized的保护粒度,如果加上代码如下:
public synchronized void setName(String name){
this.name = name;
}
public synchronized void setAddress(String address){
this.address = address;
}
}
这两个方法确实是synchronized方法,但是如果加上这两个方法,Gate类就不安全了
为什么呢?因为在Gate类中,姓名和方法必须一起赋值。我们将pass方法声明为synchronized,就是为了防止多个线程交错执行赋值操作 。如果定义像setName和setAddress这样的方法,线程就会分别给字段赋值。在保护Gate类时,如果不将字段合在一起保护就没有意义了。
思考三:
不在Gate类中使用synchronized,该如何实现Single Threaded Execution模式呢
编写Mutex类执行互斥处理
public class Mutex {
private boolean busy = false;
public synchronized void lock(){
while(busy){
try {
wait();//busy为true,锁在别的线程,进入entry set
} catch (Exception e) {
// TODO: handle exception
}
}
busy = true;
}
public synchronized void unlock(){
busy = false;
notifyAll();
}
}
限制一:不可以重入
假如某个线程连续连续两个调用lock方法,那么在第二次调用时,由于busy字段已经变为true,所以会执行wait。这就好像自己把自己锁在了门外,称为"非reentrant"
限制二:任何人都可以unlock,即使线程没有调用lock方法,也可以调用unlock方法。这就好像不是自己上的锁,自己也可以打开一样
改良Mutex:
public class Mutex {
private long locks = 0;//当前锁的个数 = lock的调用次数 - unlock的调用次数
private Thread owner = null;
public synchronized void lock(){
Thread me = Thread.currentThread();
while(locks > 0 && owner != me){
try {
wait();
} catch (Exception e) {
// TODO: handle exception
}
}
assert locks == 0 || owner == me;
//lock个数为0,或者自己已经被lock了
owner = me;
locks++;
}
public synchronized void unlock(){
Thread me = Thread.currentThread();
if(locks == 0 || owner != me){
return;
}
assert locks > 0 && owner == me;
//lock个数大于0,而且是自己lock的
locks--;
if(locks == 0){
owner = null;
notifyAll();
}
}
}
改良二:使用ReentrantLock的Mutex类
import java.util.concurrent.locks.ReentrantLock;
public class Mutex extends ReentrantLock{
}