Local鎖:
Local鎖也是在java併發庫java.util.concurrent中,不過Lock是一個接口,需要使用時必須new出Lock的具體實現類。
那麼,Local鎖是怎樣使用呢,還是以賬戶存取款爲例子,通過和synchronized鎖的對比一看就會明白:
public class Test {
public static int cash = 100;
public static void main(String[] args) {
Thread thread2 = new Thread(new Runnable() {
public void run() {
//synchronized(this)//此時鎖不起作用
synchronized(Test.class){
System.out.println("run查看賬戶,餘額爲" + cash);
cash += 1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cash2 = " + cash);
}
}
}
);
thread2.start();
Thread thread1 = new Thread(new Runnable() {
public void run() {
//synchronized(this)此時鎖不起作用,必須用Test1.class才起作用
synchronized(Test.class){
System.out.println("m查看賬戶,餘額爲" + cash);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cash = cash - 100;
System.out.println("cash1 = " + cash);
}
}
});
thread1.start();
}
}
在學習synchronized關鍵字的時我們討論了這個小例子,當synchronized()中的參數爲this時,是不起作用的。因爲兩個
Thread中this指的不是同一個東西。只有使用Test..class的時候,鎖才起作用。
我們將synchronized關鍵字換爲Lock鎖:
因爲Lock是一個接口,所以我們使用時就要new出Lock鎖的具體實現:
Lock lock = new ReentrantLock();
lock.lock();//上鎖
lock.unlock();//解鎖
修改後:
public class Test {
public static int cash = 100;
public static void main(String[] args) {
final Lock lock = new ReentrantLock();
Thread thread2 = new Thread(new Runnable() {
public void run() {
try{
lock.lock();//加鎖
{
System.out.println("run查看賬戶,餘額爲" + cash);
cash += 1000;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("cash2 = " + cash);
}
}catch (Exception e) {
e.printStackTrace();
}finally{
//關閉鎖
lock.unlock();
}
}
}
);
thread2.start();
Thread thread1 = new Thread(new Runnable() {
public void run() {
try{
lock.lock();//加鎖
{
System.out.println("m查看賬戶,餘額爲" + cash);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cash = cash - 100;
System.out.println("cash1 = " + cash);
}
}catch(Exception e){
e.printStackTrace();
}
lock.unlock();//關閉鎖
}
});
thread1.start();
}
}
此時的輸出結果:run查看賬戶,餘額爲100
cash2 = 1100
m查看賬戶,餘額爲1100
cash1 = 1000
結論:
程序運行正常。通過這個例子我們就能看出,Lock鎖通過new出來,加了這把鎖的所有方法不論在哪,都實現了線程阻塞。
相當於synchronized(常量),是個萬能鎖。
一個線程要加不同的鎖,那麼多new幾個Lock,相同的lock加在需要阻塞的方法上就行了。
和synchronized不同的是,在線程執行完以後,要關閉鎖unlock(),如果不關閉,其他在等待的線程就永遠被鎖在外面了。
比如new出lock和lock1,lock加在了方法A()和B()上,lock1加在了方法C()和D()上,那麼,一旦有線程進入方法A(),就不會有線程再進入方法B(),同理,一旦有線程進入方法C(),就不會有線程進入方法D();但是A()和C(),B()和D()之間並沒有形成阻塞。
Lock鎖更加面向對象,因爲lock本身就是一個對象。
Condition
Condition,Condition 將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的對象,以便通過將這些對象與任意 Lock 實現組合使用,爲每個對象提供多個等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用。
例子1:
啓動兩個線程,子線程運行10次,主線程運行10次,然後又子線程運行10次,主線程運行10次,如此反覆50次:
public class Test{
public static void main(String[] args) {
final Busniess test = new Test().new Busniess();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i < 51; i++) {
test.sub(i);
}
}
}).start();
for (int i = 1; i < 51; i++) {
test.main(i);
}
}
class Busniess {
boolean isSub = true;
Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void sub(int i) {
lock.lock();
try {
while (!isSub) {
//this.wait();線程等待,保證子線程先運行
condition.await();
}
for (int j = 1; j < 11; j++) {
System.out.println("sub運行第" + i + "輪,第" + j + "次");
}
isSub = false;
//this.notifyAll();線程喚醒
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public synchronized void main(int i) {
try {
lock.lock();
while (isSub) {
//this.wait();
condition.await();
for (int j = 1; j < 11; j++) {
System.out.println("main運行第" + i + "輪,第" + j + "次");
}
isSub = true;
//this.notifyAll();
condition.signalAll();
}
} catch (Exception e) {
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
}
打印結果:
sub運行第1輪,第1次
sub運行第1輪,第2次
sub運行第1輪,第3次
sub運行第1輪,第4次
sub運行第1輪,第5次
sub運行第1輪,第6次
sub運行第1輪,第7次
sub運行第1輪,第8次
sub運行第1輪,第9次
sub運行第1輪,第10次
main運行第1輪,第1次
main運行第1輪,第2次
main運行第1輪,第3次
main運行第1輪,第4次
main運行第1輪,第5次
main運行第1輪,第6次
main運行第1輪,第7次
main運行第1輪,第8次
main運行第1輪,第9次
main運行第1輪,第10次
......
在Condition中,用await()替換wait(),用signal()替換notify(),用signalAll()替換notifyAll(),傳統線程的通信方式,Condition都可以實現。
Condition
實例實質上被綁定到一個鎖上。要爲特定Lock
實例獲得Condition
實例,請使用其newCondition()
方法。
Condition和傳統的線程通信沒什麼區別,Condition的強大之處在於它可以爲多個線程間建立不同的Condition,具體看下面的例子:
} 假定有一個綁定的緩衝區,它支持
put 和 take 方法。如果試圖在空的緩衝區上執行take 操作,則在某一個項變得可用之前,線程將一直阻塞;如果試圖在滿的緩衝區上執行put 操作,則在有空間變得可用之前,線程將一直阻塞。我們喜歡在單獨的等待 set 中保存put 線程和take 線程,這樣就可以在緩衝區中的項或空間變得可用時利用最佳規劃,一次只通知一個線程。可以使用兩個Condition
實例來做到這一點。
class BoundedBuffer {
final Lock lock = new ReentrantLock();//鎖對象
final Condition notFull = lock.newCondition();//寫線程條件
final Condition notEmpty = lock.newCondition();//讀線程條件
final Object[] items = new Object[100];//緩存隊列
int putptr/*寫索引*/, takeptr/*讀索引*/, count/*隊列中存在的數據個數*/;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)//如果隊列滿了
notFull.await();//阻塞寫線程
items[putptr] = x;//賦值
if (++putptr == items.length) putptr = 0;//如果寫索引寫到隊列的最後一個位置了,那麼置爲0
++count;//個數++
notEmpty.signal();//喚醒讀線程
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)//如果隊列爲空
notEmpty.await();//阻塞讀線程
Object x = items[takeptr];//取值
if (++takeptr == items.length) takeptr = 0;//如果讀索引讀到隊列的最後一個位置了,那麼置爲0
--count;//個數--
notFull.signal();//喚醒寫線程
return x;
} finally {
lock.unlock();
}
}
}
例子2:
以此類推,如果有三個線程,想讓線程1運行完以後運行線程2,線程2運行完以後運行線程3,線程3運行完以後又運行線程1,那麼該如何來實現呢?
public class ThreeConditionCommunication {
public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i);
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub3(i);
}
}
}).start();
for (int i = 1; i <= 50; i++) {
business.main(i);
}
}
static class Business {
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int shouldSub = 1;
public void sub2(int i) {
lock.lock();
try {
while (shouldSub != 2) {
try {
condition2.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub2 thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void sub3(int i) {
lock.lock();
try {
while (shouldSub != 3) {
try {
condition3.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 20; j++) {
System.out.println("sub3 thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 1;
condition1.signal();
} finally {
lock.unlock();
}
}
public void main(int i) {
lock.lock();
try {
while (shouldSub != 1) {
try {
condition1.await();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("main thread sequence of " + j
+ ",loop of " + i);
}
shouldSub = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
}
}
也就是說:Condition可以控制多個線程之間的運行順序。