我们知道,在JAVA中用关键字synchronized控制对临界区的互斥访问,以此保障线程安全性,有synchronized方法和synchronized块。
如果是成对的lock(),unlock()方法,也可以达到同样的效果,如下面代码:
void method()
{
mutex.lock();
try
{
//critical section
do something;
}
finally
{
mutex.unlock();
}
}
这种finally的使用方法,是Before/After Pattern(事前/事后 Pattern)的一种实现方式。
那我们如何编写自己的Mutex类,保障线程的安全性呢?
一、单纯的Mutex 类
添加一字段标识是否已加锁。
public final class Mutex{
private boolean busy = false;
public synchronized void lock()
{
while(busy)
{
try{
//if the lock has been occupied, suspend the current thread
wait();
}
catch(InterruptedException e){}
}
//hold the lock
busy = true;
}
public synchronized void unlock()
{
//release the lock
busy = false;
//notify all of the waiting threads
notifyAll();
}
}
该Mutex类在上边method的情况会正确的运行,但在其它情况下,如Mutex类的重复使用就会有问题,这也是对其使用的限制:
问题1:假设有某个线程连续两次调用 lock方法,在第2次调用时,由于busy字段已经变成true,因此会wait,线程挂起,这就如同将自己锁在外边,进不了门一样。
问题2:即使是尚未调用lock()方法的线程,也可以调用unlock()方法。就如同即使不是自己上的锁,自己还是可以将门打开一样。
二、改良的Mutex类
针对上述问题,使用字段 locks、owner解决重复加锁,并在unlock() 中处理未加锁便解锁的情况。
public final class Mutex{
private long locks = 0; //number of locks
private Thread owner = null; //the owner who holds the lock
public synchronized void lock()
{
Thread me = Thread.currentThread();
while(locks > 0 && owner != me)
{
try{
wait();
}
catch(InterruptedException e){}
}
//locks == 0 || owner = me
owner = me;
locks ++;
}
public synchronized void unlock()
{
Thread me = Thread.currentThread();
//never happens
assert(locks == 0 || owner != me);
//locks > 0 && owner == me
locks--;
if(locks == 0)
{
owner = null;
notifyAll();
}
}
}