---------------------------------------- android培訓、java培訓、期待與您交流! ------------------------------------
線程間通訊:
多個線程在處理同一資源,但是任務卻不同;
一:wait() notify() notifyAll()用來操作線程的,定義在Object類中
1.這些方法存在同步中
2.使用這些方法必須要標示所屬的同步鎖
3.這些都是監視器的方法,監視器其實就是鎖,鎖可以是任意對象,任意的對象調用的方式一定定義在Object中。
二:wait和sleep的區別
都會拋出異常,需要處理,都會使線程進入凍結狀態
1.wait():釋放資源,釋放鎖,需要被notify()喚醒。
2.sleep():釋放資源,不釋放鎖,休息一段時間醒來後繼續執行。
3.yield():釋放鎖,使當前線程馬上回到就緒狀態,也可以馬上執行。
暫停當前正在執行的線程,並執行其他線程
在同步代碼塊中,只要有特有對象都可以作爲同步鎖,因爲要保證鎖的唯一性。
等待喚醒機制
使用標記flag,使生產線程和消費線程交替執行並執行完畢後喚醒對方
單一同步只能保證對象屬性的正確,不能保證輸入與輸出一對一,加入等待喚醒後才能夠實現單輸入與單輸出一對一,但不能解決多輸入多輸出的協調。
單生產和單消費實例:
package thread;
public class ProducerConsumer {
public static void main(String[] args) {
//創建資源對象
Resource r = new Resource();
Producer producer = new Producer(r);
Consumer consumer = new Consumer(r);
Thread t = new Thread(producer);
Thread t2 = new Thread(consumer);
t.start();
t2.start();
}
}
class Resource {
private String name;//要生產的東西的名字
private int count = 1;//初始值爲1
private boolean flag = false;//默認爲沒有
public synchronized void set(String name) {
// 如果有饅頭就進入等待狀態,通知消費者來吃
if (flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//如果flag = flase就生產饅頭
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName() + ".....生產者....."
+ this.name);
//生產完後設置flag爲true,則自己進入等待狀態,然後喚醒對方起來吃
flag = true;
this.notifyAll();
}
public synchronized void out() {
// 如果沒有饅頭就等待
if (!flag) {
try {
this.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//flag等於true等於有饅頭,我們就起來吃
System.out.println(Thread.currentThread().getName()
+ "..........消費者..........." + this.name);
//吃完設置flag = false,自己進入等待狀態,喚醒消費者,等饅頭出來了在起來吃。
flag = false;
this.notify();
}
}
class Producer implements Runnable {
private Resource r;
public Producer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.set("饅頭");
}
}
}
class Consumer implements Runnable {
private Resource r;
public Consumer(Resource r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.out();
}
}
}
多生產者 多消費者的問題
while...notifyAll()
相對於單生產單消費的問題,多生產多消費的情況會每次notify()喚醒的線程都不確定,有可能是本方也有可能是對方,會發生 凍結狀態導致的不同步,不能對應輸出
所以每次線程被喚醒後執行時都要對標記進行判斷,如果喚醒的是本方線程,由於標記未改變仍然不能執行。從而保證雙方線程交替執行
但是使用while標記判斷如果只隨機notify()隨機喚醒了一個線程,一旦喚醒的是本方線程,就會導致線程全部處於臨時阻塞狀態(沒有了執行權),所以使用while時要使用notifyAll()
示例:
while(flag)//標記判斷
try{this.wait();}
catch (Exception e){
}
要執行的程序方法體;
flag = true;//改變標記
this.notifyAll();//喚醒全部
JDK5.0新增的兩個接口
功能更加強大的鎖,更加直觀的顯式鎖,將鎖進行的對象的封裝,並且將鎖的操作也封裝成對象。
lock接口替代synchronized同步鎖,
Condition接口替代Object監視器方法
wait() notify() notifyAll()
await() signal() signalAll()
從Lock 創建鎖從lock接口的方法newCondition()獲取實例來操作鎖,Lock的強大之處在於能夠獲取多個實例來操作同一個鎖,可以用來對指定任務進行控制。
await() 或者signal()對方(指定的任務),不用喚醒全部,本方只喚醒對方
private Lock lock = new ReentrantLock();// 創建鎖
private Condition condition_pro = lock.newCondition();// 通過鎖獲取Condition實例
private Condition condition_con = lock.newCondition();// 一鎖多用,相互喚醒
多生產多消費升級方案:
package thread;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource1 {
private Lock lock = new ReentrantLock();// 創建鎖
private Condition condition_pro = lock.newCondition();// 通過鎖獲取Condition實例
private Condition condition_con = lock.newCondition();// 一鎖多用,相互喚醒
private String name;
private int count = 1;
private boolean flag = false;
public void set(String name) throws InterruptedException {
lock.lock();// 加鎖,相當於同步塊或同步函數
try {
while (flag) {
// 如果有饅頭,生產者進入等待狀態
condition_pro.await();
}
this.name = name + count++;
System.out.println(Thread.currentThread().getName()
+ "...............生產了................" + this.name);
// 生產完饅頭以後將flag設置爲true,歡迎消費者。
flag = true;
condition_con.signal();// 這樣的結構清晰多了
} finally {
lock.unlock();// 在finally中施放鎖
}
}
public void out() throws InterruptedException {
lock.lock();
try {
while (!flag) {// 如果沒有饅頭.消費者進入等待狀態
condition_con.await();
}
System.out.println(Thread.currentThread().getName()
+ ".........消費了........" + this.name);
flag = false;
condition_pro.signal();
} finally {
lock.unlock();
}
}
}
class Producer1 implements Runnable {
private Resource1 r;
public Producer1(Resource1 r) {
super();
this.r = r;
}
@Override
public void run() {
while (true) {
try {
r.set("包子");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class Consumer1 implements Runnable {
private Resource1 r;
public Consumer1(Resource1 r) {
super();
this.r = r;
}
public Resource1 getR() {
return r;
}
public void setR(Resource1 r) {
this.r = r;
}
@Override
public void run() {
while (true) {
try {
r.out();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) {
Resource1 r = new Resource1();
Producer1 producer = new Producer1(r);
Consumer1 consumer = new Consumer1(r);
Thread t = new Thread(producer);
Thread t2 = new Thread(producer);
Thread t3 = new Thread(consumer);
Thread t4 = new Thread(consumer);
t.start();
t2.start();
t3.start();
t4.start();
}
}
一定在要finally{}中釋放鎖
【代碼優化小結】:
應用封裝,即將有共性的內容封裝起來,在需要的時候調用。
善用匿名對象,對於直接使用的對象參數採用匿名減少代碼量
善用接口類型和類類型的參數傳遞
請問java中的lock和synchronized區別是什麼?
主要相同點:Lock能完成synchronized所實現的所有功能
主要不同點:Lock能有比synchronized更精確的語義和更好的性能。synchronized會自動釋放鎖,而Lock要求程序員手工釋放,在finally{}快中