java多線程二之線程同步的三種方法


      java多線程的難點是在:處理多個線程同步與並發運行時線程間的通信問題。java在處理線程同步時,常用方法有:

1、synchronized關鍵字。

2、Lock顯示加鎖。

3、信號量Semaphore。

 

線程同步問題引入:

      創建一個銀行賬戶Account類,在創建並啟動100個線程往同一個Account類實例里面添加一塊錢。在沒有使用上面三種方法的情況下:

代碼:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class AccountWithoutSync {
	private static Account account = new Account();   //實例化一個賬戶
	
	public static void main(String[] args)
	{
		long start = System.currentTimeMillis();
		//使用ExecutorService創建線程池
		ExecutorService executor  = Executors.newCachedThreadPool();
		
		for(int i=0;i<100;i++)
		{
			executor.execute(new AddPennyTask());
		}
		//關閉線程池 即使線程池中還有未完成的線程 返回未完成的清單
		executor.shutdown();
		
		//關閉之后還是要保證未完成的線程繼續完成  如果線程池中所有任務都完成了,isTerminated返回true
		while(!executor.isTerminated())
		{}
		long end = System.currentTimeMillis();

		//balance有余額的意思
		System.out.println("現在賬戶里面的余額是:" + account.getBalance());
		System.out.println("花費的時間以微秒為單位:"+(end-start)+"微秒");
	}
	
	//這個線程只調用了一個方法
	public static class AddPennyTask implements Runnable
	{
		@Override
		public void run() {
			account.deposit(1);
		}
	}
	
	//一個內部類  用於 賬戶的相關處理
	public static class Account
	{
		private int balance =0;
		public int getBalance()
		{
			return balance;
		}
		public  void deposit(int amount)
		{
			int newBalance = balance + amount;
			//為了讓錯誤體現的更明顯
			try {
				Thread.sleep(4);   //5毫秒
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			balance= newBalance;
			//其實就是balance +=amount;//不過換成這一段代碼結果就在100和99左右
		}
	}
}

 

運行截圖df97c06074cc45a383ff66c7b3f9cc66

 

       錯的不是一般的明顯,明明存入了100塊,顯示只有2塊,原因也很簡單,就是100個線程同時對acount進行修改,當第100該線程把錢改成100時,第2個線程最后修改,把它改成了2,這群不聽話的線程,那就好好管管他們吧,讓他們乖乖聽話,在這之前,還是要記住以下一些名詞:

競爭狀態:當多個線程訪問同一公共資源並引發沖突造成線程不同步,我們稱這種狀態為競爭狀態。

線程安全:是針對類來說的,如果一個類的對象在多線程程序中不會導致競爭狀態,我們就稱這類為線程安全的,上面的Account是線性不安全的,而又如StringBuffer是線程安全的,而StringBuilder則是線程不安全的。

 

臨界區:造成競爭狀態的原因是多個線程同時進入了程序中某一特定部分,如上面程序中的deposit方法,我們稱這部分為程序中的臨界區,我們要解決多線程不同步問題,就是要解決如何讓多個線程有序的訪問臨界區

 

使用synchronized關鍵字

1、同步方法:在deposit方法前加synchronized關鍵字,使得該方法變成同步方法:public synchronized void deposit(double amount){}

2、同步語句:對某一代碼塊加synchronized關鍵字,常用格式:

synchronized(exper) {statement}   其中exper是對象的引用,如上面的程序,在要在調用depsoit方法時,改成這樣:  synchronized(account){account.deposit(1);}

      使用synchronized,其實是隱式地給方法或者代碼塊加了鎖,任何同步的實例方法都可以轉換為同步語句:

public synchronized void  method(){}

轉換為同步語句: public  void  method{sysnchronized(this){}}

 

利用lock加鎖同步

       java也可以用Lock顯示的對臨界區代碼加鎖以及解鎖,這比用synchronized關鍵字更加直觀靈活

       一個鎖是一個Lock接口的實例,該接口定義了加鎖解鎖的方法,且一個鎖可以多次調用其newCondition()方法創建名為Condition對象的實例,以此進行線程間的通信(在后面用到)。

     有了Lock接口,我們還要實現它,java提供了RenentrantLock類,該類是為創建相互排斥鎖而實現了Lock接口,由此就好辦了,下面看一下書上的圖:

image

代碼如下:

public static class Account2
{
	private static Lock lock = new ReentrantLock();
	private int balance =0;
	public int getBalance()
	{
		return balance;
	}
	public  void deposit(int amount)
	{
		lock.lock();
		try{
			int newBalance = balance + amount;
			Thread.sleep(4);   
			balance= newBalance;
		}catch (InterruptedException e) {
			e.printStackTrace();
		}
		finally{
			lock.unlock();
		}
	}
}

 

給個運行截圖image

         100塊存入,且時間明顯比之前久了,100個線程都乖乖的排隊訪問臨界區。另外注意在對lock()的調用后,緊跟隨try catch finnaly語句,這是個好習慣

 

利用信號量同步

            信號量是個好東西,信號量機制在操作系統方面有着廣泛的應用,如linux進程同步信號量,而在java里,Semaphore包包含了一些訪問信號量的方法。

信號量可以用來限制訪問共享資源的線程數,在訪問臨界區資源前,線程必須獲取一個信號量,在訪問完之后返回一個信號量。下圖是關於類Semaphore,該類包含訪問信號量的方法:

`ES0J3D(F35DDQ$G~4BGX%I

利用信號量Semaphorre,可以將上面的Account類改成這樣:

public static class Account
{
	private static Semaphore semaphore = new Semaphore(1);  //創建一個信號量
	private int balance =0;
	public int getBalance()
	{
		return balance;
	}
	public  void deposit(int amount)
	{
		try {
			semaphore.acquire();
			int newBalance = balance + amount;
			Thread.sleep(4);   
			balance= newBalance;
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			semaphore.release();   //返回一個信號量
		}
	}
}

 

      網上有很多關於java多線程的文章,有不少寫成系列的,但個人覺得像多線程這么有有意思的部分,還是得好好靜下心來品位,上面的三種方法,可以解決多線程同步問題,后面還會繼續學習,嗯,加油。

     本文出自於博客園蘭幽,轉載請說明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM