看到了详细的方法实现 ^_^
1. 两三个线程同时修改某个对象,如果仅由访问先后来决定结果的话,会出现各种结果。这种情况被称为race condition。
2. 防止这种情况的发生,必须知道如何同步访问(synchronize the access)。
3. javap -c -v ClassName 可以反编译一个.class文件。
4. 锁住对象
早期版本的Java使用synchronized关键词,JDK5以后引入了ReentrantLock类。
使用ReentrantLock类保护代码块的基本轮廓:
myLock.lock(); // a ReentrantLock object
try
{
critical section
}
finally
{
myLock.unlock(); // make sure the lock is unlocked even if an exception is thrown
}
这样第一个线程调用lock方法锁住myLock后,第二个线程调用lock方法就会被block,并且得等到第一个线程调用myLock.unlock()后才能继续。
ReentrantLock类允许被多次锁定,它记录了呼叫的嵌套形式。大致是这个意思,原文是
The lock is called reentrant because a thread can repeatedly acquire a lock that it already owns. The lock keeps a hold count that keeps track of the nested calls to the lock method. The thread has to call unlock for every call to lock in order to relinquish the lock. Because of this feature, code that is protected by a lock can call another method that uses the same locks.
For example, the TRansfer method calls the getTotalBalance method, which also locks the bankLock object, which now has a hold count of 2. When the getTotalBalance method exits, the hold count is back to 1. When the transfer method exits, the hold count is 0, and the thread relinquishes the lock.
In general, you will want to protect blocks of code that require multiple operations to update or inspect a data structure. You are then assured that these operations run to completion before another thread can use the same object.
5. Condition Objects
Conditon Objects用来管理得了访问权,却实际并不能做有用功的线程。
以银行帐户转帐为例,转帐时要确定转出源的资金数不少于要转出的金额。
首先不能这样简单的写代码:
if (bank.getBalance(from) <= amount)
bank.transfer(from, to, amount);
这种代码完全有可能在if语句判断完成后,transfer之前停滞,这样在调用transfer的时候,可能帐户当前的资金已经不是if语句判断那个时候的数目了。
也就是说,测试可行性和实际操作必须一起进行,之间不能有中断。
可以用一个lock把测试和操作绑定起来:
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] > amount)
{
// wait
. . .
}
// transfer funds
. . .
}
finally
{
bankLock.unlock();
}
}
这样还是有问题,帐户资金不够移出时,会困在while块中等待资金拨入这个帐户,但由于bankLock已经被锁定,所以其他线程不能进行拨入操作,进入了死循环。这就是condition object产生的原因。
一个lock对象可以有一个或多个相关的condition object,可以通过newCondition方法获得一个条件对象,通常用实际条件命名每个条件对象,如
class Bank
{
public Bank()
{
. . .
sufficientFunds = bankLock.newCondition();
}
. . .
private Condition sufficientFunds;
}
当transfer方法发现当前资金不够时,调用
sufficientFunds.await();
这样当前线程就会停滞,并解除lock。
await方法调用后,线程进入对应Condition的等待区,直到另一个线程调用同一Condition的signalAll方法才会解除block状态,并等待再次被线程管理器激活。
如果一个线程调用了condition.await方法,却没有其他线程调用condition.signalAll,这个线程就进入了deadlock情况。如果所有其他的线程都进入了等待区,而最后一个线程也调用了condition.await,那么整个程序就挂起了。
因此最好的调用signalAll的时机是在每次对象的状态被改变,而这个改变有可能使得等待区的线程有进展的时候。如Bank中每次成功转帐之后。
另一个方法,signal,仅仅从等待区中随机选择一个进程并释放。
注意:线程只能在获得了lock权以后才能调用condition的await, signal, 和signalAll。