Java多線程併發筆記05 volitile、Automotic wait、notify模擬隊列

示例程序1:volatile List 屬性多線程操作

import java.util.ArrayList;
import java.util.List;

/**
 * 使用volatile關鍵字使得多個線程操作同一個
 * 2個線程,
 * 	一個線程不斷往集合裏添加元素
 * 	一個線程監聽集合的元素個數
 * 		當個數等於某個數值時停止此線程
 * 		while(true)監聽,性能不好,待改進
 * @author Administrator
 *
 */
public class ListAdd01 {
	
	/*volatile關鍵字修飾下才能在多個線程之間共享成員變量,要不然每個線程的方法體中都是當前成員變量的一個副本(在方法體中)*/
	private  static volatile List<String> list = new ArrayList<String>();
	
	public void add() {
		list.add("bjsxt");
	}
	
	public int size(){
		return list.size();
	}
	
	public static void main(String[] args) {
		final ListAdd01 list1 = new ListAdd01();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					for(int i = 0; i < 10; i++) {
						list1.add();
						System.out.println("當前線程:"+ Thread.currentThread().getName() + "添加了一個元素...");
						Thread.sleep(100);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				/*以這樣的方式在這裏等待需要消耗太多的系統資源,不推薦*/
				while(true) {
					if(list1.size() == 5) {
						System.out.println("當前線程:"+ Thread.currentThread().getName() + "收到了一個通知,listSize = 5線程停止...");
						throw new RuntimeException();
					}
				}
			}
		},"t2");
		
		t1.start();
		t2.start();
	}
}

示例程序2: 一線程監聽,被喚醒,觸發用計數器要優於wait,notify,因爲notify不釋放鎖

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * wait、notify方法,wait釋放鎖,notify不釋放鎖
 * 
 * 
 * 採用wait,notify的輸出結果
t2進入...
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
已經發出通知:notify
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t1 添加了一個元素
當前線程:t2收到了一個通知,listSize = 5線程停止...
Exception in thread "t2" java.lang.RuntimeException
	at com.bjsxt.thread.ListAdd02$2.run(ListAdd02.java:62)
	at java.lang.Thread.run(Thread.java:745)

直到最後一刻才輸出,所以這種方式無法及時被執行

那怎麼辦呢?
用CountDownLatch:計數器,是一個阻塞flag,和同步不同步沒關係,和鎖沒有任何關係
多用於遠程鏈接的實現,比如zookeeper客戶端調用遠程連接(舉得小例子)

 *
 */
public class ListAdd02 {
	public volatile List<String> list = new ArrayList<String>();
	
	void add(){
		list.add("bjsxt");
	}
	
	public int size() {
		return list.size();
	}
	
	public static void main(String[] args) {
		final ListAdd02 list2 = new ListAdd02();
		
		//實例化一個lock
		//當使用wait和notify的時候,一定要配合着synchronized使用
		final Object lock = new Object();
		final CountDownLatch count = new CountDownLatch(1);
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					//不要放到for循環外,等到天荒地老啊,若是放到for循環裏面就更細粒度了,沒問題(待測)。
					//若是這裏的目的僅僅是保證10次for循環的原子性,那用synchronized完全OK,沒問題,
					//但若目的是爲了通知另外一個進程,並使另外的接到統治的 進程即時處理,則不適宜用synchronized。
//					synchronized (lock) {
						for(int i = 0; i < 10; i++) {
							list2.add();
							System.out.println("當前線程:"+ Thread.currentThread().getName() + " 添加了一個元素");
							Thread.sleep(500);
							if(list2.size() == 5) {
								System.out.println("已經發出通知:notify"); 
								//notify雖然通知了,但是並沒有釋放鎖,
								//所以lock.wait()那裏收到通知了也繼續執行了,但因爲鎖被notify這佔據着,所以線程被阻塞了,卡在那了
								//只要等到notify()的線程執行完畢釋放了鎖後,wait()重新獲取到了鎖後才能繼續執行下去,不被卡住
//								lock.notify();
								count.countDown();
							}
						}
//					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		},"t1");
		
		
		
		/**
		 * 監聽等待,
		 */
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
//				synchronized (lock) {
					if(list2.size() != 5) {
						try {
							System.out.println("t2進入...");
//							lock.wait();//等待並且釋放鎖
							count.await();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						System.out.println("當前線程:"+ Thread.currentThread().getName() + "收到了一個通知,listSize = 5線程停止...");
						throw new RuntimeException();
					}
//				}
			}
		},"t2");
		
		t2.start();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t1.start();
		
	}
}

 

 

import java.util.LinkedList;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 我的模擬隊列
 * @author Administrator
 *
 */
public class MyQueue {
	//隊列
	LinkedList<Object> list = new LinkedList<Object>();
	
	//計數器
	private AtomicInteger count = new AtomicInteger(0);
	
	//同步鎖
	private Object lock = new Object();
	
	
	private final int minSize = 0; 
	
	private final int maxSize;
	
	public MyQueue(int maxSize){
		this.maxSize = maxSize;
	}
	
	public void add(Object obj){
		synchronized(lock) {
			try {
				while(count.get() == maxSize) {
					lock.wait();
				}
				//加入元素
				list.add(obj);
				//計數器加1
				count.incrementAndGet();
				
				//喚醒另外一個線程
				lock.notify();
				System.out.println("新插入的元素爲:" + obj);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
	}
	
	public Object take() {
		synchronized(lock) {
			Object firstInObject = null;
			try {
				while(count.get() == minSize){
					lock.wait();
				}
				
				//數量減減
				firstInObject = list.removeFirst();
				//計數器減1
				count.decrementAndGet();
				//通知另外一個線程(喚醒)
				lock.notify();
				System.out.println("拿出的元素爲:" + firstInObject);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return firstInObject;
		}
	}
	
	public int size() {
		return count.get();
	}
	
	public static void main(String[] args) {
		final MyQueue queue = new MyQueue(5);
		//插入5個元素,已滿
		queue.add("a");
		queue.add("b");
		queue.add("c");
		queue.add("d");
		queue.add("e");
		
		System.out.println("當前容量長度爲:" + queue.size());
		
		//再次插入,阻塞
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				queue.add("f");
				queue.add("g");
			}
		},"t1");
		t1.start();//t1的執行結果在t2之後,因爲已經滿了,被阻塞了,
	
		//拿出
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for(int i = 0; i<3; i++) {
					queue.take();
				}
			}
		},"t2");
		
		//隔3秒再拿出
		try {
			Thread.sleep(3000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		t2.start();
	}
}

 

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