使用ReentrantLock,最大的感觸就是,一定要嚴格按照使用規範去寫,否則各種多線程問題讓人防不勝防。
大概幾點吧:
1、無論是ReentrantLock還是Condition都是爲了鎖資源而存在的,沒有資源的話,這些東西就沒有意義。
2、ReentrantLock是爲了保證操作資源的互斥性,Condition是爲了消除等待資源的時間浪費。
3、ReentrantLock一定要lock和unlock配對出現,並且一定要保證不管走任何代碼分支,都要有unlock操作。(寫示例中,就在while循環內部寫了lock,在外部finally寫了unlock,結果死鎖了)
4、必須是先增加資源,然後才釋放出signal信號。
小小示例,隨意看看吧~~
package lock;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestCondition {
static ReentrantLock lock = new ReentrantLock();
static int[] resource = new int[] { 0, 0, 0, 0, 0 };
public static void main(String[] args) {
// test1();
test2();
}
private static void test1() {
int[][][] testcase1 = new int[][][] {
new int[][] { new int[] { 0, 1, 2, 3 }, new int[] { 4 } },
new int[][] { new int[] { 0, 1, 2 }, new int[] { 3, 4 } },
new int[][] { new int[] { 0, 1 }, new int[] { 2, 3, 4 } },
new int[][] { new int[] { 0 }, new int[] { 1, 2, 3, 4 } },
new int[][] { new int[] {}, new int[] { 0 } } };
test(testcase1);
}
private static void test2() {
int[][][] testcase2 = new int[][][] {
new int[][] { new int[] { 0 }, new int[] { 1 } },
new int[][] { new int[] { 1 }, new int[] { 2 } },
new int[][] { new int[] { 2 }, new int[] { 3 } },
new int[][] { new int[] { 3 }, new int[] { 4 } },
new int[][] { new int[] { 4 }, new int[] { 0 } } };
Condition start = test(testcase2)[0];
lock.lock();
resource[0]++;
start.signal();
lock.unlock();
}
static Condition[] test(int[][][] testcase) {
Condition[] conditions = new Condition[resource.length];
for (int i = 0; i < conditions.length; i++) {
conditions[i] = lock.newCondition();
}
List<Thread> list = new LinkedList<Thread>();
for (int[][] worker : testcase) {
list.add(new Thread(new Runit(conditions, worker[0], worker[1])));
}
int i = 0;
for (Thread t : list) {
t.setName("t" + i++);
t.start();
}
return conditions;
}
static class Runit implements Runnable {
Condition[] cons;
int[] waits;
int[] signals;
/**
* 模擬生產線
*
* @param cons 對應resource的Condition數組
* @param wait 該生產線的原料
* @param signal 該生產線的產出
*/
public Runit(Condition[] cons, int[] wait, int[] signal) {
this.cons = cons;
this.waits = wait;
this.signals = signal;
}
@Override
public void run() {
while (true) {
try {
sleep();
lock.lock(); // 鎖住全部資源,不允許其他線程操作resource
if (waits != null) {
for (int c : waits) {
if (resource[c] <= 0) {
cons[c].await(); // 發現某個資源欠缺,釋放鎖,等待喚醒重新競爭鎖
}
resource[c]--; // 消耗資源
}
}
if (signals != null) {
for (int c : signals) {
resource[c]++; // 產出資源
System.out.println("Thread "
+ Thread.currentThread().getName()
+ " make a product -- " + c);
cons[c].signal(); // 通知本生產線某產品製成
}
}
} catch (Exception e) {
System.out.println(e);
} finally {
lock.unlock(); // 釋放資源鎖,允許其他線程競爭resource處理資格
}
}
}
private void sleep() throws InterruptedException {
Random rad = new Random();
int r = rad.nextInt(4000);
if (r < 0) {
r = -r;
}
Thread.sleep(1000 + r);
}
}
}