java多線程 例子

lock 鎖

:只允許一個線程訪問一段代碼或資源,只有一個線程從一個資源退出時,才允許其他線程訪問,形成同步
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class MyThread1 {

	public static void main(String[] args) {
		
		Humberg h = new Humberg();
		Thread t1 = new Thread(h);
		Thread t2 = new Thread(h);
		System.out.println(Thread.currentThread().getName());
		t1.start();// thread 0
		t2.start(); //thread 1
	}
}

class Humberg implements Runnable{

	private Lock l = new ReentrantLock();
	private int humbger = 100;
	private int count = 0;
	@Override
	public void run() {
		while(true){
			l.lock();
			try {
				Thread.sleep(200);
				if(humbger > 0){
					System.out.println(Thread.currentThread().getName()+"-----"+(++count));
					humbger --;
				}else{
					break;
				}
			} catch (Exception e) {
				e.printStackTrace();
			}finally {
				l.unlock();
			}
		}
	}
}

yield()  

屈服 投降 禮讓 

public class YieldExample {
	   public static void main(String[] args) {
	      Thread producer = new Producer();
	      Thread consumer = new Consumer();
	      producer.setPriority(Thread.MIN_PRIORITY); 
	      consumer.setPriority(Thread.MAX_PRIORITY); 
	      producer.start();
	      consumer.start();
	   }
	}
	class Producer extends Thread{
	   public void run()   {
	      for (int i = 0; i < 5; i++)     {
	         System.out.println("I am Producer : Produced Item " + i);
	         Thread.yield();
	      }
	   }
	} 
	class Consumer extends Thread{
	   public void run()   {
	      for (int i = 0; i < 5; i++)      {
	         System.out.println("I am Consumer : Consumed Item " + i);
	         Thread.yield();
	      }
	   }
	}
結果: 結果並不絕對

I am Producer : Produced Item 0
I am Consumer : Consumed Item 0
I am Producer : Produced Item 1
I am Consumer : Consumed Item 1
I am Producer : Produced Item 2
I am Consumer : Consumed Item 2
I am Producer : Produced Item 3
I am Consumer : Consumed Item 3
I am Consumer : Consumed Item 4
I am Producer : Produced Item 4

join()

public class TestJoin {

	public static void main(String[] args) {

		MyThread5 t1 = new MyThread5("TestJoin");
		t1.start();
		try {
			t1.join(); // join()合併線程,子線程運行完之後,主線程纔開始執行
		} catch (InterruptedException e) {
		}

		for (int i = 0; i < 10; i++)
			System.out.println("I am Main Thread");
	}
}

class MyThread5 extends Thread {

	MyThread5(String s) {
		super(s);
	}

	public void run() {
		for (int i = 1; i <= 10; i++) {
			System.out.println("I am " + getName());
			try {
				sleep(1000); // 暫停,每一秒輸出一次
			} catch (InterruptedException e) {
				return;
			}
		}
	}
}
結果:
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am TestJoin
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread
I am Main Thread

synchronized 同步鎖

synchronized是Java中的關鍵字,是一種同步鎖。它修飾的對象有以下幾種: 
1. 修飾一個代碼塊,被修飾的代碼塊稱爲同步語句塊,其作用的範圍是大括號{}括起來的代碼,作用的對象是調用這個代碼塊的對象; 
2. 修飾一個方法,被修飾的方法稱爲同步方法,其作用的範圍是整個方法,作用的對象是調用這個方法的對象; 
3. 修改一個靜態的方法,其作用的範圍是整個靜態方法,作用的對象是這個類的所有對象; 
4. 修改一個類,其作用的範圍是synchronized後面括號括起來的部分,作用主的對象是這個類的所有對象。
public class Demo {
	public static void main(String[] args) {
		MyThread8 my1 = new MyThread8();
		Thread t1 = new Thread(my1, "窗口一");
		Thread t2 = new Thread(my1, "窗口2");
		t1.start();
		t2.start();
	}
}

class MyThread8  implements Runnable{
	private int staticks = 100;
	private int count = 0;
	
	@Override
	public  void run() {
			while(true){
				synchronized (this) {
					if(staticks > 0 ){
						try {
							Thread.sleep(200);
							System.out.println(Thread.currentThread().getName()+"賣了第"+(++count)+"張票");
							staticks --;
						} catch (Exception e) {
							e.printStackTrace();
						}
					}else{
						break;
					}
				}
		}
	}
}

wait ,notify,notifyAll,synchronized   

在多線程的情況下,由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問衝突這個嚴重的問題。Java語言提供了專門機制以解決這種衝突,有效避免了同一個數據對象被多個線程同時訪問。

   wait與notify是java同步機制中重要的組成部分。結合與synchronized關鍵字使用,可以建立很多優秀的同步模型。
  synchronized(this){ }等價於publicsynchronized void method(){.....}
   同步分爲類級別和對象級別,分別對應着類鎖和對象鎖。類鎖是每個類只有一個,如果static的方法被synchronized關鍵字修飾,則在這個方法被執行前必須獲得類鎖;對象鎖類同。
   首先,調用一個Object的wait與notify/notifyAll的時候,必須保證調用代碼對該Object是同步的,也就是說必須在作用等同於synchronized(obj){......}的內部才能夠去調用obj的wait與notify/notifyAll三個方法,否則就會報錯:
  java.lang.IllegalMonitorStateException:current thread not owner
  在調用wait的時候,線程自動釋放其佔有的對象鎖,同時不會去申請對象鎖。當線程被喚醒的時候,它纔再次獲得了去獲得對象鎖的權利。
  所以,notify與notifyAll沒有太多的區別,只是notify僅喚醒一個線程並允許它去獲得鎖,notifyAll是喚醒所有等待這個對象的線程並允許它們去獲得對象鎖,只要是在synchronied塊中的代碼,沒有對象鎖是寸步難行的。其實喚醒一個線程就是重新允許這個線程去獲得對象鎖並向下運行。

   notifyAll,雖然是對每個wait的對象都調用一次notify,但是這個還是有順序的,每個對象都保存這一個等待對象鏈,調用的順序就是這個鏈的順序。其實啓動等待對象鏈中各個線程的也是一個線程,在具體應用的時候,需要注意一下。

  wait(),notify(),notifyAll()不屬於Thread類,而是屬於Object基礎類,也就是說每個對像都有wait(),notify(),notifyAll()的功能。因爲都個對像都有鎖,鎖是每個對像的基礎,當然操作鎖的方法也是最基礎了。

wait():

等待對象的同步鎖,需要獲得該對象的同步鎖纔可以調用這個方法,否則編譯可以通過,但運行時會收到一個異常:IllegalMonitorStateException。

調用任意對象的 wait() 方法導致該線程阻塞,該線程不可繼續執行,並且該對象上的鎖被釋放。

notify():

喚醒在等待該對象同步鎖的線程(只喚醒一個,如果有多個在等待),注意的是在調用此方法的時候,並不能確切的喚醒某一個等待狀態的線程,而是由JVM確定喚醒哪個線程,而且不是按優先級。

調用任意對象的notify()方法則導致因調用該對象的 wait()方法而阻塞的線程中隨機選擇的一個解除阻塞(但要等到獲得鎖後才真正可執行)。

notifyAll():

喚醒所有等待的線程,注意喚醒的是notify之前wait的線程,對於notify之後的wait線程是沒有效果的。

 

通常,多線程之間需要協調工作:如果條件不滿足,則等待;當條件滿足時,等待該條件的線程將被喚醒。在Java中,這個機制的實現依賴於wait/notify。等待機制與鎖機制是密切關聯的。

例如:
  synchronized(obj) {
  while(!condition) {
  obj.wait();
  }
  obj.doSomething();
  }
  
  當線程A獲得了obj鎖後,發現條件condition不滿足,無法繼續下一處理,於是線程A就wait()。
  在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿足了,就可以喚醒線程A :
  
  synchronized(obj) {
  condition = true;
  obj.notify();
  }
  
  需要注意的概念是:
  
  # 調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj){...} 代碼段內。

  
  # 調用obj.wait()後,線程A就釋放了obj的鎖,否則線程B無法獲得obj鎖,也就無法在synchronized(obj){...} 代碼段內喚醒A。
  
  # 當obj.wait()方法返回後,線程A需要再次獲得obj鎖,才能繼續執行。
  
  #如果A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)。
  
  #obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續執行obj.wait()的下一條語句,必須獲得obj鎖,因此,A1,A2,A3只有一個有機會獲得鎖繼續執行,例如A1,其餘的需要等待A1釋放obj鎖之後才能繼續執行。
  
  #
 當B調用obj.notify/notifyAll的時候,B正持有obj鎖,因此,A1,A2,A3雖被喚醒,但是仍無法獲得obj鎖。直到B退出synchronized塊,釋放obj鎖後,A1,A2,A3中的一個纔有機會獲得鎖繼續執行。
  

談一下synchronized和wait()、notify()等的關係:

1.有synchronized的地方不一定有wait,notify

2.有wait,notify的地方必有synchronized.這是因爲wait和notify不是屬於線程類,而是每一個對象都具有的方法,而且,這兩個方法都和對象鎖有關,有鎖的地方,必有synchronized。

另外,注意一點:如果要把notify和wait方法放在一起用的話,必須先調用notify後調用wait,因爲如果調用完wait,該線程就已經不是currentthread了。


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