多線程下的單例模式、生產者、消費者模式

單例模式

  • 之前有一個博客簡述了單例模式,這裏就不具體介紹了
  • 單例模式的實現方式:懶漢式和餓漢式
  • 其中,懶漢式是線程不安全的,當有多條線程同時訪問單例對象時,則會出現多線程臨界資源問題
多線程下的懶漢式
package waking.test.xcms;
/**
 * 多線程下的單例模式
 * 懶漢式
 * @author waking
 *
 */

import java.util.HashSet;

public class Demo01 {
	//創建set集合
	static HashSet<Kind> hs = new HashSet<Kind>();
	//靜態Runnable
	static Runnable r = new Runnable() {
		
		@Override
		public void run() {
			Kind kind = Kind.getKindInstance();
			hs.add(kind);
		}
	};
	
	public static void main(String[] args) {
		for (int i = 0; i < 1000; i++) {
			Thread t = new Thread(r);
			t.start();
		}
		
		System.out.println(hs);
		//[waking.test.xcms.Kind@5acbf04a, waking.test.xcms.Kind@5162c257, waking.test.xcms.Kind@5e05618a]

	}

}
class Kind {
	//私有化對象
	private static Kind kind = null;
	
	//私有化構造方法
	private Kind() {
		
	}
	
	//向外提供公開的靜態的實例對象
	public static Kind getKindInstance() {
		if(kind==null) {
			kind = new Kind();
		}
		
		return kind;
	}
}
  • 由此可見線程不安全
多線程下的餓漢式
package waking.test.xcms;
/**
 * 多線程下的單例模式
 * 餓漢式
 * @author waking
 *
 */

import java.util.HashSet;

public class Demo02 {
	//set集合
	static HashSet<Hungry> hs = new HashSet<Hungry>();
	
	//Runnable
	static Runnable r = new Runnable() {

		@Override
		public void run() {
			Hungry h = Hungry.getHungryInstance();
			hs.add(h);
		}
		
	};
	
	public static void main(String[] args) {
		for (int i = 0; i < 1000; i++) {
			Thread t = new Thread(r);
			t.start();
		}
		
		System.out.println(hs);
		//[waking.test.xcms.Hungry@30535167]
	}
}

/**
 * 餓漢式
 * @author waking
 *
 */
class Hungry{
	//私有化靜態對象
	private static Hungry hungry = new Hungry();
	
	//私有化靜態方法
	private Hungry() {
		
	}
	
	//提供公開的靜態的實例方法
	public static Hungry getHungryInstance() {
		return hungry;
	}
	
}
  • 由此可以看出,餓漢式相對線程安全

生產者、消費者模式

原理
  • 它描述的是有一塊緩衝區作爲倉庫,生產者可以將產品放入倉庫,消費者可以從倉庫中取走產品,解決生產者、消費者問題,我,們需要採用某種機制保護生產者和消費者的同步
  • 同步的問題核心在於:如何保證同一個資源被多個線程併發訪問時的完整性,常用的方法就是加鎖,保證資源在任意時刻只被一份線程訪問
實現
  • 方式一:採用wait()、notify()和notifyAll()方法
wait():當緩衝區已滿或空時,生產者、消費者線程停止自己的執行,放棄鎖,使用自己處於等待狀態,讓其他線程執行
說明:
a.是Object的方法
b.調用方式:對象.wait();
c.表示釋放、對象這個鎖標記,然後在鎖外面等待(對比sleep(),sleep()是抱着鎖休眠的)
d.等待,必須放到同步代碼中執行
notify():當生產者、消費者向緩衝區放入、取出一個產品時,向其他等待的線程發出可執行的通知,同時放棄鎖,使自己處於等待狀態
說明:
a.是object的方法
b.調用方式:對象.notify();
c.表示喚醒對象所標記外邊在等待的一個線程
notifyAll():全部喚醒
a.是objectde 方法
b.調用方式:對象.notifyAll()
c.表示喚醒對象所標記外邊等待的所有線程
package waking.test.xcms;
/**
 * 生產者消費者模式
 * @author waking
 *
 */
public class Demo03 {
	//標誌
	static boolean shouldProduct = true;
	
	//生產者線程
	static class Productor implements Runnable{
		
		private Product product;

		public Productor(Product product) {
			this.product = product;
		}

		@Override
		public void run() {
			while(true) {
				synchronized ("") {
					if(shouldProduct) {
						this.product.setName("waking");
						System.out.println("生產者:"+Thread.currentThread().getName()+"生產了,一個"+this.product);
						shouldProduct=false;
						
						"".notifyAll();
					}else {
						try {
							"".wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}
				}
			}
		}
		
	}
	//消費者線程
	
	static class Consumer implements Runnable{

		private Product P;
		
		

		public Consumer(Product p) {
			this.P = p;
		}



		@Override
		public void run() {
			while(true) {
				synchronized ("") {
					if(shouldProduct) {
						try {
							"".wait();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					}else {
						System.out.println("消費者:"+Thread.currentThread().getName()+"消費了,一個產品"+this.P);
						shouldProduct=true;
						"".notifyAll();
					}
				}
			}
		}
		
	}
	
	public static void main(String[] args) {
		Product p = new Product();
		Productor por = new Productor(p);
		Thread t = new Thread(por,"A");
		Thread t2 = new Thread(por,"B");
		Thread t3 = new Thread(por,"C");
		Thread t4 = new Thread(por,"D");
		t.start();
		t2.start();
		t3.start();
		t4.start();
		
		Consumer c = new Consumer(p);
		Thread t5 = new Thread(c,"a");
		Thread t6 = new Thread(c,"b");
		Thread t7 = new Thread(c,"c");
		Thread t8 = new Thread(c,"d");
		t5.start();
		t6.start();
		t7.start();
		t8.start();
	}

}
class Product{
	private String name;
	int id;
	public String getName() {
		id--;
		return name;
	}
	public void setName(String name) {
		this.name = name;
		id++;
	}
	@Override
	public String toString() {
		return "Product [name=" + name + ", id=" + id + "]";
	}
	
	
}
  • 採用ReentrantLock類中的newCondition()方法結合Condition類中的await()、signal()和signalAll()方法
package waking.test.xcms;
/**
 * 生產者消費者模式二
 * @author waking
 *
 */

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Demo04 {
	//標誌
	static boolean flage = true;
	
	static ReentrantLock lock = new ReentrantLock();
	//生產者標誌
	static Condition c1 = lock.newCondition();
	//消費者標誌
	static Condition c2 = lock.newCondition();
	
	//生產者線程
	static class Pro implements Runnable{

		private S s;
		
		
		public Pro(S s) {
			super();
			this.s = s;
		}


		@Override
		public void run() {
			while(true) {
				try {
					//加鎖
					lock.lock();
					if (flage) {
						this.s.setName("waking");
						System.out.println("生產者:" + Thread.currentThread().getName() + "生產了" + this.s);
						flage = false;
						//喚醒
						c2.signalAll();
					} else {
						try {
							c1.await();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					} 
				} finally {
					lock.unlock();
				}
			}
		}
		
	} 
	//消費者線程
	static class Con implements Runnable{

		private S ss;
		
		
		public Con(S ss) {
			super();
			this.ss = ss;
		}


		@Override
		public void run() {
			while(true) {
				try {
					lock.lock();
					if (flage) {
						try {
							c2.await();
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
					} else {
						System.out.println("消費者:" + Thread.currentThread().getName() + "消費了" + this.ss);
						flage = true;
						c1.signalAll();
					} 
				} finally {
					lock.unlock();
				}
			}
		}
		public static void main(String[] args) {
			S s = new S();
			Pro p = new Pro(s);
			new Thread(p,"A").start();
			new Thread(p,"B").start();
			new Thread(p,"C").start();
			new Thread(p,"D").start();
			
			Con c = new Con(s);
			new Thread(c, "a").start();
			new Thread(c, "b").start();
			new Thread(c, "c").start();
			new Thread(c, "d").start();
			
		}
		
	}
	

}
class S{
	private String name;
	int id;
	
	public String getName() {
		id--;
		return name;
	}
	public void setName(String name) {
		this.name = name;
		id++;
	}
	@Override
	public String toString() {
		return "S [name=" + name + ", id=" + id + "]";
	}
	
}
以上是多線程下的單例模式、生產者、消費者模式的情況,感謝您的觀看
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章