1. 程式人生 > >Java——執行緒的同步和通訊

Java——執行緒的同步和通訊

執行緒安全是指多個執行緒訪問同一程式碼,不會產生不確定的結果;

執行緒同步:

為了避免多執行緒在共享資源時發生衝突,所以 要線上程使用該資源時,就為執行緒上一把“鎖”,第一個訪問資源的執行緒為資源上鎖,其他執行緒若想訪問該資源,則必須等待解鎖為止,解鎖的同時,另一個執行緒訪問資源併為資源上鎖。

在使用多執行緒程式設計時,一般解決同步問題的方法有三種種:

  1. 用synchronize關鍵字:
一種是同步程式碼塊 一種是同步方法(同步方法使用this關鍵字作為標記,即物件本身作為同步監視器)
  1. wait()方法和notify()或notifyAll()方法:當使用synchronize來修飾某個共享資源時,如果執行緒A在執行synchronize程式碼塊,另外一個執行緒B也要執行同一物件的同一synchronize程式碼時,執行緒B將要等到執行緒A釋放物件鎖,才能繼續執行,這時可以使用wait()方法和notify()方法;
  2. 還有一種非常靈活簡便的同步鎖可以選擇,可以通過java.util.concurrent.locks.ReentrntLock類 的物件呼叫lock()方法來實現加鎖操作,此時可以進行同步操作,同步操作結束後,呼叫unlock()釋放同步鎖。(在呼叫同一物件的wait()和notify()方法語句必須放在同步程式碼塊中,並且同步程式碼塊使用該物件的同步鎖,否則在執行時會丟擲ILLegalMonitorStateException)異常。
方法 作用
lock() 以阻塞的方式獲取鎖,也就是說,如果獲取了鎖,立即返回,如果別的執行緒持有鎖,當前執行緒等待,直到取鎖後返回
trylock() 以非阻塞的方式獲取鎖,只是嘗試性的取獲取一下鎖,如果獲取了鎖 ,立即返回true,否則,立即返回false
trylock(long timeout,TieUnit unit) 如果獲取了鎖 ,立即返回true,否則會等待給定引數的時間,在等待的過程中,如果獲取了鎖,返回true,等待超時,返回false
lockInterruptibly() 如果獲取了鎖 ,立即返回true,如果沒有獲得鎖,當前執行緒處於休眠狀態,直到獲取鎖,或者當前執行緒被別的執行緒中斷(會收到InterruptedException異常)

synchronized加在例項方法上,鎖的是物件例項;加在靜態方法上鎖的是類。

同步程式碼塊:
synchronize(Object obj) {
     //需要同步的程式碼塊,這個地方會訪問obj的程式碼
}
同步方法:
【訪問控制符】 【static|final】synchronize 返回型別 方法名(引數列表){
         //需要同步的程式碼
         【return 返回值】
}

其中,synchronize實現同步程式碼塊的關鍵字,obj表示任意物件,可以是實際存在的也可以是假設的,在Java中任意一個物件都有一個同步鎖,以下是注意事項:

  • 物件O的同步鎖在任何時刻最多隻能被一個執行緒擁有;
  • 如果物件O的同步鎖被執行緒T擁有,那麼當其他執行緒來訪問O時,這些執行緒將被放到O的鎖池中,並將它們轉為同步阻塞狀態;
  • 擁有O的鎖的執行緒T執行完後,會自動釋放O的鎖。若在執行過程中,執行緒T發生異常退出,也將自動釋放O的鎖;
  • 如果執行緒T在執行同步程式碼塊時,呼叫了O的wait()方法,則執行緒T會釋放O的鎖,執行緒T也將轉為等待阻塞狀態;
  • 如果執行緒T在執行同步程式碼塊時,呼叫了Thread類的sleep()方法,執行緒T將放棄執行權,即放棄CPU,但執行緒T不會放棄物件O的鎖;
  • 如果執行緒T釋放了物件O的鎖,並放棄執行權,則CPU將會隨機分配給物件O鎖池中的執行緒,該執行緒也將會擁有物件O鎖;
/**
 * 一個鍋裡有五碗飯,有兩個視窗,一次只能給一個人打飯。
 * @author lzq
 *
 */
class Ticket implements Runnable {
	private int ticket = 5;
	public Ticket() {
		
	}
	public void run() {
		while(ticket >= 1) {
			this.sale();
			}
		}
	/**
	 * 同步方法
	 */
	private void sale() {
		if(ticket > 0) {
			try {
				Thread.sleep(1000);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			if(ticket > 0) {
				System.out.println("------"+Thread.currentThread().getName()+"賣出第"
			+(ticket--)+"碗飯");
			}
		}
		
	}
}

/**
 * 兩個視窗賣票,一次賣一張,總共5張
 * 使用同步程式碼塊
 * @author lzq
 *
 */
public class TestDemo13 implements Runnable {
	private int ticket = 5;
	public TestDemo13() {
		
	}
	public void run() {
		while(ticket >= 1) {
			synchronized (this) {
				try {
					Thread.sleep(1000);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				if(ticket > 0) {
					System.out.println(Thread.currentThread().getName()+"賣出第"
				+(ticket--)+"張門票");
				}
			}
		}
	}

	
	public static void main(String[] args) {
		TestDemo13 x = new TestDemo13();
		
		Thread t1 = new Thread(x,"第一視窗");
		Thread t2 = new Thread(x,"第二視窗");
		t1.start();
		t2.start();
		
		Ticket y = new Ticket();
		Thread z1 = new Thread(y,"第1視窗");
		Thread z2 = new Thread(y,"第2視窗");
		z1.start();
		z2.start();

	}

}

執行結果:

------第2視窗賣出第5碗飯
------第1視窗賣出第4碗飯
第一視窗賣出第5張門票
第一視窗賣出第4張門票
------第2視窗賣出第2碗飯
------第1視窗賣出第3碗飯
------第2視窗賣出第1碗飯
第一視窗賣出第3張門票
第一視窗賣出第2張門票
第一視窗賣出第1張門票

/**
 * 使用同步鎖實現
 * 兩個視窗賣票,一次賣一張,總共10張
 * @author lzq
 *
 */
public class TestDemo14 implements Runnable {
	private final ReentrantLock lock = new ReentrantLock();//建立鎖
	private int ticket = 10;
	public TestDemo14() {
		
	}
	public void run() {
		while(ticket > 0) {
			lock.lock();   //加鎖
			try {
				Thread.sleep(1000);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			if(ticket > 0) {
				System.out.println(Thread.currentThread().getName()+"賣出第"
				+(ticket--)+"張門票");
			}
			lock.unlock();//釋放鎖
		}
	}


	
	public static void main(String[] args) {
        TestDemo14 x = new TestDemo14();
		
		Thread t1 = new Thread(x,"第一視窗");
		Thread t2 = new Thread(x,"第二視窗");
		t1.start();
		t2.start();

	}

}

執行結果:

第一視窗賣出第10張門票
第一視窗賣出第9張門票
第一視窗賣出第8張門票
第二視窗賣出第7張門票
第二視窗賣出第6張門票
第二視窗賣出第5張門票
第二視窗賣出第4張門票
第一視窗賣出第3張門票
第一視窗賣出第2張門票
第一視窗賣出第1張門票

範例:

  1. 有兩個執行緒分別為sf和gf,sf用來放置食物,而gf用來取走食物;
  2. sf一次只能放置一批次食物,sf放置好後通知gf取走;
  3. gf一次只能取走一批次的食物,等到gf取走食物後通知sf放置食物;
  4. sf不放置食物,則gf不能取走食物,若gf沒有取走食物,則sf不能放置食物;
  5. 開始時,先由sf放置,再由gf取走;
/**
 * 放置食物類
 * @author lzq
 *
 */
class SetFood implements Runnable {
	private Food food;
	public SetFood(Food food) {
		this.food = food;
	}
	public void run() {
		for(int number = 1;number <= 5;number++) {
			try {
				Thread.sleep(500);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			food.setfood(number);  //放置食物
		}
	}
}
/**
 * 取走食物類
 * @author lzq
 *
 */
class GetFood implements Runnable {
	private Food food;
	public GetFood(Food food) {
		this.food = food;
	}
	public void run() {
		for(int number = 1;number <= 5;number++) {
			try {
				Thread.sleep(500);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			food.getfood();  //取走食物
		}
	}
}
/**
 * 食物類
 * @author lzq
 *
 */
public class Food {
	private int number = 0; //食物批次
	private String food = null; //食物名稱
	public Food() {
		
	}
	public Food(String food,int number) {  
		this.food = food;
		this.number = number;
	}
	/**
	 * 放置食物
	 * @param n
	 */
	public synchronized void setfood(int n) {   
		if(this.number != 0) {
			try {
				wait();   //開始等待
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		number = n;
		System.out.println("放置"+this.food+"第"+this.number+"批次");
		notify();  //通知取走食物
	}
	/**
	 * 取走食物
	 * @param n
	 * @return
	 */
	public synchronized String getfood() {
		if(this.number == 0) {
			try {
				wait();  //等待放置食物
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("\t取走"+"第"+this.number+"批次"+this.food);
		this.number = 0;
		notify();   //通知放置食物
		return food+number;   
		
	}
	
	public static void main(String[] args) {
		Food f= new Food("白菜",0);
		SetFood sf = new SetFood(f);
		GetFood gf = new GetFood(f);
		new Thread(sf).start();
		new Thread(gf).start();

	}

}

執行結果:

放置白菜第1批次
	取走第1批次白菜
放置白菜第2批次
	取走第2批次白菜
放置白菜第3批次
	取走第3批次白菜
放置白菜第4批次
	取走第4批次白菜
放置白菜第5批次
	取走第5批次白菜

相關推薦

Java執行-同步非同步的區別

1.                                         &nb

java 執行同步非同步

ava執行緒 同步與非同步 執行緒池 1)多執行緒併發時,多個執行緒同時請求同一個資源,必然導致此資源的資料不安全,A執行緒修改了B線 程的處理的資料,而B執行緒又修改了A執行緒處理的數理。顯然這是由於全域性資源造成的,有時為了解 決此問題,優先考慮使用區域性變數,退

Java --- 執行同步非同步的區別

1. Java 執行緒 同步與非同步 多執行緒併發時,多個執行緒同時請求同一個資源,必然導致此資源的資料不安全,A執行緒修改了B執行緒的處理的資料,而B執行緒又修改了A執行緒處理的數理。顯然這是由於全域性資源造成的,有時為了解決此問題,優先考慮使用區域性變數,

關於Java執行執行同步執行通訊的一些小問題(順便分享幾篇高質量的博文)

一、對於執行緒同步和同步鎖的理解(注:分享了三篇高質量的部落格) 以下我精心的挑選了幾篇博文,分別是關於對執行緒同步的理解和如何選擇執行緒鎖以及瞭解執行緒鎖的作用範圍。 <一>執行緒同步鎖的選擇 2. 以上推薦的博文是以賣火車票為例,引出了非同步會導致的錯誤以及同步鎖(監視器)應該如果選擇,

Java執行同步非同步詳解

1. 多執行緒併發時,多個執行緒同時請求同一資源,必然導致此資源的資料不安全。 2. 執行緒池 在WEB服務中,對於web伺服器的響應速度必須儘可能的快,這就容不得在使用者提交請求按鈕後,再建立執行緒提供服務。為了減少使用者的等待時間,執行緒必須預先建立,放線上程池中,執行

Java執行同步併發問題示例

併發問題 多執行緒是一個非常強大的工具,它使我們能夠更好地利用系統的資源,但我們需要在讀取和寫入多個執行緒共享的資料時特別小心。

Java執行學習總結(執行的概念)

執行緒的概念: 多執行緒允許在程式中併發執行多個指令流,每個指令流都稱為一個執行緒,彼此間互相獨立。它和程序一樣擁有獨立的執行控制,由作業系統負責排程,區別在於執行緒沒有獨立的儲存空間,而是和所屬程序中的其它執行緒共享一個儲存空間,這使得執行緒間的通訊遠較程序簡單。 具體到java記憶體模型,

java執行同步方法

1.同步方法     synchronized修飾方法,物件內建鎖保護整個方法(物件鎖)     若方法static修飾,類鎖 2.同步程式碼塊     同步—高開銷—減少同步內容 3.wait和notify   &n

java執行資料庫連線池[從學習到工作(二)]

背景:前段時間工作需要開發一個socket服務端,在接受到客戶端發過來的報文資訊後解析報文呼叫資料庫程式完成資料檔案的生成再拼湊結果報文反饋給客戶端。由於客戶數比較多,所以用執行緒池和資料庫連線池。        一.執行緒池   

java執行waitnotify 筆記理解

首先明確 兩個的概念: wait()方法是暫停使用當前鎖執行的執行緒,並釋放當前執行緒的資源和當前的物件鎖。 notify()方法是隨機喚醒使用當前鎖暫停的執行緒,而notifyAll()是喚醒所有的使用當前鎖暫停的執行緒 直接兩段程式碼 看看效果: 一個生產者執行緒往list新增一

java執行間的通訊方式

1.三個運動員各自準備,等到三個人都準備好後,再一起跑 如何能夠讓每個執行緒都準備好執行,然後一起執行任務? 1.先建立一個公共 CyclicBarrier 物件,設定 同時等待 的執行緒數,CyclicBarrier cyclicBarrier = new CyclicBarrie

java 執行——synchronized volatile 關鍵字

synchronized 和 volatile 關鍵字 原子性 可見性 有序性 加在方法上 原子性、可見性、有序性 在講這兩個關鍵字之前,我們先來看一下幾個概念 原子性 原子性是指一個操作時不可中斷的,要麼全部執行成功,要麼全部執行失敗

Java 執行sleep() wait()的區別

sleep(休眠) 和 wait(等待) 方法是 Java 多執行緒中常用的兩個方法 區別1:使用限制 使用 sleep 方法可以讓當前執行緒休眠,時間一到當前執行緒繼續往下執行,在任何地方都能使用

OS實驗二 執行同步通訊

1 實驗目的與要求 1、掌握Linux下執行緒的概念; 2、瞭解Linux執行緒同步與通訊的主要機制; 3、通過訊號燈操作實現執行緒間的同步與互斥。 2 實驗內容 通過Linux多執行緒與訊號燈機制,設計並實現計算機執行緒與I/O執行緒共享緩衝區的同步與通訊。

Java 執行同步方式

Java中synchronized關鍵字鎖定的是物件。驗證程式碼如下: class Demo { public synchronized void sayHello(){ try{ System.out.println("hello start");

JAVA執行同步 waite,notify, notifyAll

public class MyObject { private static Object lock = new Object(); public void methidA(){ synchronized (lock){ for(in

JAVA 執行同步 synchronized

  synchronized public void methidA(){ for(int i = 0; i< 100;i++){ System.out.println("methidA"); try{ Thread.sleep(5); }

Java執行stopsuspend的廢棄

JDK1.0定義了stop和suspend方法,stop用來直接終止執行緒,suspend會阻塞執行緒直到另一個執行緒呼叫resume. stop和suspend都有一些共同的點:都試圖專橫的控制一個給定了的執行緒的行為. 從JDK1.2開始,這兩個方法都被棄用了.stop

Java執行同步的認識

synchronized關鍵字 Java以提供關鍵字synchronized的形式,以防止多執行緒時資源衝突提供了內建支援。當任務要執行被synchronized關鍵字保護的程式碼片段時,它將檢查鎖是否可用,然後獲取鎖,執行程式碼,釋放鎖。 所有物件都自動含

java執行---同步方法

關鍵字synchronized方法方法上修飾,那麼該方法就是同步方法。同步方法分為 :非靜態同步方法和靜態同步方法。程式碼如下:非靜態同步方法package sx.test.thread;public class Demo4 {public static void main(