【
線程間的通訊
wait()在對象上等待,等待通知(在等待過程中釋放對象鎖、等待必須在同步塊內、這個對象就是同步鎖)《讓線程進入阻塞狀態,將線程放入等待池中》
notify()通知在這個對象上等待的一個線程,喚醒它,讓它不再等下去(必須在同步塊內調用,同步鎖必須是調用這個方法的對象)《》
notifyAll()和notify()差不多,區別在于是通知在這個對象上等待的所有線程
注意:線程間通訊的幾個方法()
總結:wait、notify、notifyAll必須是同步代碼塊中被調用,
】
wait()在對象上等待,等待通知(在等待過程中釋放對象鎖、等待必須在同步塊內、這個對象就是同步鎖)
【
//要在這個線程中等待
public class Wifeextends Thread {
private Object message;//等待對象,同時也是同步鎖
public Wife(Object message) {
this.message = message;???? //初始化,從外部傳入
}
public void run(){
synchronized (message){//wait動作必須在一個同步塊內部,而且對象鎖必須是等待的對象
System.out.println("開始等待");
try {
message.wait();//在對象進行等待,等待過程中釋放鎖,另一線程的同步塊得以執行
System.out.println("收到音訊,結束等待");
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
}
public class Husband{
public static void main(String[] args){
Object message? = new Object();? //創建對象
Wife t = newWife(message);????? //新建線程,傳入對象
t.start();//啟動
System.out.println("上京趕考");
try {
Thread.sleep(5000);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}
System.out.println("考上狀元");
System.out.println("飛鴿傳書");
synchronized (message){//notify動作必須在同步塊內,而且對象鎖必須是調用通知的對象
message.notify();//通知在這個對象上等待的線程,當這個同步塊結束,通知的那個線程的wait就返回并繼續執行
}
}
}
】
notify()通知在這個對象上等待的一個線程,喚醒它,讓它不再等下去(必須在同步塊內調用,同步鎖必須是調用這個方法的對象)
notifyAll()和notify()差不多,區別在于是通知在這個對象上等待的所有線程
【
public class CowManextends Thread {
private Object message;
public CowMan(Object message) {
this.message = message;
}
public void run(){
synchronized (message) {
System.out.println("牛郎等待");
try {
message.wait();
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
System.out.println("牛郎去鵲橋");
}
}
}
public class Fairyextends Thread {
private Object message;
public Fairy(Object message) {
this.message = message;
}
public void run(){
synchronized (message) {
System.out.println("織女等待");
try {
message.wait();
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
System.out.println("興沖沖跑去鵲橋相會");
}
}
}
public static voidmain(String[] args) throws Exception {
Object message = newObject();//創建共同使用的信息對象
CowMan cowMan = newCowMan(message);//牛郎和織女都等待同一個信息,七月初七到來的信息
Fairy fairy = newFairy(message);
cowMan.start();
fairy.start();
for(int i=1;i<=7;i++){
System.out.println("七月初"+i);
Thread.sleep(1000);
}
synchronized (message) {
message.notifyAll();//通知在這個對象上等待著的所有線程,牛郎和織女
}
}
】
【
public class Demo {
publicstatic void main(String[] args) {
Resouser = new Resouse("蘋果");
newProduce("生產者", r).start();
newConsumer("消費者", r).start();
}
}
/**
* 消費者類
*
*/
class Consumer extends Thread {
Resouser;
publicConsumer(String name, Resouse r) {
super(name);
this.r= r;
}
@Override
publicvoid run() {
//不停拿資源
while(true) {
r.get();
}
}
}
class Produce extends Thread {
Resouser;
publicProduce(String name, Resouse r) {
super(name);
this.r= r;
}
@Override
publicvoid run() {
//不停生產
while(true) {
r.put();
}
}
}
class Resouse {
Stringname;
intid;
booleanflag; // 記錄有沒有資源
publicResouse(String name) {
this.name= name;
}
/**
* 放資源
*/
publicsynchronized void put() {
try{
if(flag) {
wait();//讓線程進入等待狀態(阻塞狀態的一種),直到被notify()方法喚醒
}
//生產資源
id++;
System.out.println(Thread.currentThread().getName()+ "...生產了....."
+name + id);
flag= true;
//通知消費者
notify();
Thread.sleep(500);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
//【同步方法的實質是同步代碼塊,同一個類中的不同行為<方法>的同步,是可行的。同步鎖是當前對象,
//即在多線程中同一時間片段中,對象只會調用類中同步的其中一個同步方法】
/**
* 取資源
*/
public/*synchronized */void get() {
synchronized(this) {//同步鎖就是對象監視器,他只會監控同步鎖對應的代碼塊中的代碼
try{
if(!flag) { // 沒有資源就等待生產
wait();
}
System.out.println(Thread.currentThread().getName()
+"...消費了...." + name + id);
flag= false; // 消費完得告訴生產者沒有資源了
//通知生產者
notify();
Thread.sleep(500);
}catch (InterruptedException e) {
//TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
】
【
publicclass Demo {
public static void main(String[] args){
Resouse r = newResouse("蘋果");
new Produce("生產者1號",r).start();
new Consumer("消費者1號",r).start();
new Produce("生產者2號", r).start();
new Consumer("消費者2號",r).start();
new Produce("生產者3號",r).start();
}
}
/**
* 消費者類
*
*/
classConsumer extends Thread {
Resouse r;
public Consumer(String name, Resouse r){
super(name);
this.r = r;
}
@Override
public void run() {
// 不停拿資源
while (true) {
r.get();
}
}
}
classProduce extends Thread {
Resouse r;
public Produce(String name, Resouse r){
super(name);
this.r = r;
}
@Override
public void run() {
// 不停生產
while (true) {
r.put();
}
}
}
classResouse {
String name;
int id;
boolean flag; // 記錄有沒有資源
public Resouse(String name) {
this.name = name;
}
/**
* 放資源
*/
public synchronized void put() {
try {
while (flag) {
wait();// 讓線程進入等待狀態(阻塞狀態的一種),直到被notify()方法喚醒
}
// 生產資源
id++;
System.out.println(Thread.currentThread().getName()+ "...生產了....."
+name + id);
flag = true;
// 通知消費者
notifyAll();
Thread.sleep(500);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
// 【同步方法的實質是同步代碼塊,同一個類中的不同行為<方法>的同步,是可行的。同步鎖是當前對象,
// 即在多線程中同一時間片段中,對象只會調用類中同步的其中一個同步方法】
/**
* 取資源
*/
public/* synchronized */void get() {
synchronized (this) {//同步鎖就是對象監視器,他只會監控同步鎖對應的代碼塊中的代碼
try {
while(!flag) { // 沒有資源就等待生產
wait();
}
System.out.println(Thread.currentThread().getName()
+"...消費了...."+ name + id);
flag =false; // 消費完得告訴生產者沒有資源了
// 通知生產者
notifyAll();
Thread.sleep(500);
} catch(InterruptedException e) {
// TODOAuto-generated catch block
e.printStackTrace();
}
}
}
}
】
【
多生產者多消費者的問題
1、? if語句換成while語句
可以保證在生產或消費之前都先判斷一次資源的情況
2、? notity方法換成notifyAll方法
可以保證每次都能喚醒對方的線程
在JDK1.5之后,可以使用Lock接口和Condition接口來解決以上問題
通過Lock接口也可以實現線程的同步處理,并且可以讓同步處理變得更加靈活(需要手動實現獲取鎖和釋放鎖的操作)
//示例代碼
//創建Lock對象(可以將Lock對象理解為原來的鎖對象)
Locklock = new ReentrantLock();
try{
//被同步的代碼
}finally {
}
Condition對象將原先在Object類中的監視器方法(wait、notify、notifyAll)抽取出來進行封裝,每一個Condition對象都是一個等待池,一個Lock對象可以綁定多個Condition對象,這樣一來可以讓線程間的通訊操作變得更加靈活。
//創建Condition對象,并和指定的Lock對象綁定
Lock lock = ...;
Condition condition = lock.newCondition();
線程間通訊的三個方法
1)await():替代wait方法
2)signal():替代notify方法
3)signalAll():替代notifyAll方法
】
【
/*
* 有5輛火車要過山洞,但確保山洞同時只能有一輛火車通過(過山洞需要1秒),打印輸出火車通過的順序。
* (過山洞的順序是不可控的,只要保證同一時間只有一輛火車能通過山洞即可)
* 提示:使用線程同步,一輛火車就是一個線程
*/
publicclass LockDemo {
public static void main(String[] args){
// TODO Auto-generated methodstub
newTrain("火車1").start();
new Train("火車2").start();
new Train("火車3").start();
new Train("火車4").start();
new Train("火車5").start();
}
}
classTrain extends Thread{
//創建Lock對象
static Lock lock = new ReentrantLock();
public Train(String name){
super(name);
}
@Override
public void run() {
//獲取鎖
lock.lock();
try {
System.out.println(getName()+"過山洞.....");
Thread.sleep(1000);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}finally{
//釋放鎖
lock.unlock();
}
}
}
】
【
publicclass Demo {
public static void main(String[] args){
// TODO Auto-generated methodstub
Resource r = newResource("蘋果");
new Producer("生產者1號",r).start();
new Producer("生產者2號",r).start();
new Consumer("消費者1號",r).start();
new Consumer("消費者2號",r).start();
new Consumer("消費者三號", r).start();
}
}
/*
* 消費者類
*/
classConsumer extends Thread {
Resource r;
public Consumer(String name, Resourcer) {
super(name);
this.r = r;
}
@Override
public void run() {
while (true) {
r.get();
}
}
}
/*
* 生產者類
*/
classProducer extends Thread {
Resource r;
public Producer(String name, Resourcer) {
super(name);
this.r = r;
}
@Override
public void run() {
while (true) {
r.put();
}
}
}
/*
* 資源類
*/
classResource {
String name;
int id;
boolean flag; // 記錄有沒有資源
// Lock對象,用于替代同步代碼塊
Lock lock = new ReentrantLock();
// 創建兩個等待池
// 生產者線程用的等待池
Condition producerCon =lock.newCondition();
// 消費者線程用的等待池
Condition consumCon =lock.newCondition();
public Resource(String name) {
this.name = name;
}
// 放資源
public void put() {
lock.lock();
try {
while (flag) {
// 將當前線程(生產者線程放入等待池)
producerCon.await();// 替代了wait方法
}
// 生產資源
id++;
System.out.println(Thread.currentThread().getName()+ "...生產了...."
+name + id);
flag = true;
// 通知消費者消費
// 喚醒消費者等待池中的線程
consumCon.signal();
Thread.sleep(500);
} catch (InterruptedExceptione) {
// TODO Auto-generatedcatch block
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void get() {
lock.lock();
try {
while (!flag) {
consumCon.await();
}
// 消費資源
System.out.println(Thread.currentThread().getName()+ "....消費了...."
+name + id);
flag = false;
//通知生產者生產
producerCon.signal();
Thread.sleep(500);
} catch (InterruptedExceptione) {
// TODOAuto-generated catch block
e.printStackTrace();
}finally{
lock.unlock();
}
}
}
】