多線程最爲常見的應用案例

多線程最爲常見的應用案例——生產者消費者問題。

生產和消費同時進行,需要多線程。但是,執行的任務卻不相同,處理的資源卻是相同的,線程間的通信。

1. 描述一下資源。

2. 描述生產者,因爲具備着自己的任務。

3. 描述消費者,因爲具備着自己的任務。

public class Resource {
	private String name;
	private int count=1;
	
    //定義標記
	private boolean flag=false;
	
	//1. 提供設置的方法
	public synchronized void set(String name)
	{
		if(flag)
			try{wait();}catch(InterruptedException e) {    }
		//給成員變量賦值並加上編號
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"..."+"生產"+this.name);
		//標記改爲true
		flag=true;
		//喚醒消費者
		notify();
	}
    public synchronized void out()
    {
    	if(!flag)
    		try{wait();}catch(InterruptedException e) {    }
        	System.out.println(Thread.currentThread().getName()+"..."+"消費"+this.name);
        	flag=false;
        	notify();
    }
}


//描述生產者
public class Production implements Runnable {
	private Resource r;
	Production(Resource r)
	{
		this.r=r;
	}
    public void run()
    {
    	while(true)
    	{
    		r.set("麪包");
    	}
    }
}


//描述消費者
public class Consume implements Runnable{
	private Resource r;
	public Consume(Resource r) {
		this.r=r;
	}
	public void run()
	{
		while(true)
		{
			r.out();
		}
	}
	

public class ThraedDemo {

	public static void main(String[] args) {
		//創建資源對象
		Resource r=new Resource();
		
		//創建線程任務
		Production p=new Production(r);
		Consume c=new Consume(r);
		
		//創建線程
		Thread t1=new Thread(p);
		Thread t2=new Thread(c);
		
		//開啓線程
		t1.start();
		t2.start();	
	}
}

結果:
       ......
Thread-0...生產麪包82372
Thread-1...消費麪包82372
Thread-0...生產麪包82373
Thread-1...消費麪包82373
Thread-0...生產麪包82374
Thread-1...消費麪包82374
Thread-0...生產麪包82375
Thread-1...消費麪包82375
       ......

當我們使其變成多消費多生產時,又會發生錯誤。

其他的類不變,就加入了消費者和生產者。

public class ThraedDemo {

	public static void main(String[] args) {
		//創建資源對象
		Resource r=new Resource();
		
		//創建線程任務
		Production p1=new Production(r);
		Consume c1=new Consume(r);
		Production p2=new Production(r);
		Consume c2=new Consume(r);
		
		
		//創建線程
		Thread t1=new Thread(p1);
		Thread t2=new Thread(c1);
		Thread t3=new Thread(p2);
		Thread t4=new Thread(c2);
		
		//開啓線程
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

會出現這樣的結果:

Thread-0...生產麪包82372//沒有被消費
Thread-1...生產麪包82373
Thread-3...消費麪包82373

被喚醒的線程沒有判斷標記,造成問題1的產生。

解決:只要讓被喚醒的線程必須判斷標記就可以了,將if判斷標記的方式改爲while判斷標記。記住,多生產多消費,必須while判斷條件。

public class Resource {
	private String name;
	private int count=1;
	
    //定義標記
	private boolean flag=false;
	
	//1. 提供設置的方法
	public synchronized void set(String name)
	{
		while(flag)
			try{wait();}catch(InterruptedException e) {    }
		//給成員變量賦值並加上編號
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"..."+"生產"+this.name);
		//標記改爲true
		flag=true;
		//喚醒消費者
		notify();
	}
    public synchronized void out()
    {
    	while(!flag)
    		try{wait();}catch(InterruptedException e) {    }
        	System.out.println(Thread.currentThread().getName()+"..."+"消費"+this.name);
        	flag=false;
        	notify();
    }
}

有出現問題2:發現while判斷後,死鎖了。

原因:生產喚醒了線程池中生產方的線程,本方喚醒了本方。

解決:希望本方法一定要喚醒對方,沒有對應的方法,所以只能喚醒所有。將notify改成notifyAll。

但是,這樣效率低了。

public class Resource {
	private String name;
	private int count=1;
	
    //定義標記
	private boolean flag=false;
	
	//1. 提供設置的方法
	public synchronized void set(String name)
	{
		while(flag)
			try{wait();}catch(InterruptedException e) {    }
		//給成員變量賦值並加上編號
		this.name=name+count;
		count++;
		System.out.println(Thread.currentThread().getName()+"..."+"生產"+this.name);
		//標記改爲true
		flag=true;
		//喚醒消費者
		notifyAll();
	}
    public synchronized void out()
    {
    	while(!flag)
    		try{wait();}catch(InterruptedException e) {    }
        	System.out.println(Thread.currentThread().getName()+"..."+"消費"+this.name);
        	flag=false;
        	notifyAll();
    }
}

希望本方可以喚醒對方的一個:

import java.util.concurrent.locks.*;

public class Resource {
	private String name;
	private int count=1;
	
	//定義一個鎖對象
	private Lock lock=new ReentrantLock();
	//獲取鎖上的Condition對象,爲了解決喚醒對方的問題,可以一個鎖創建兩個監視器對象。
	private Condition con=lock.newCondition();//負責消費
	private Condition pro=lock.newCondition();//負責生產
	   
    //定義標記
	private boolean flag=false;
	
	public void set(String name)
	{
		//獲取鎖
		lock.lock();
		
		try{
			 while(flag)
			     try{pro.await();}catch(InterruptedException e) {    }
	 	       //給成員變量賦值並加上編號
		       this.name=name+count;
	           count++;
	           System.out.println(Thread.currentThread().getName()+"..."+"生產"+this.name);
	       	   //標記改爲true
		       flag=true;
		      con.signal();
		    }
		    finally {
		    lock.unlock();//釋放鎖。一定要執行,所以放在finally裏
		    }
	 }
    public void out()
    {
    	    lock.lock();
    	    try {
    	      while(!flag)
    		      try{con.await();}catch(InterruptedException e) {    }
        	    System.out.println(Thread.currentThread().getName()+"..."+"消費"+this.name);
        	    flag=false;
        	    pro.signal();
    	    }finally{
    		    lock.unlock();
    	    }
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章