java多線程操作例子-(以銀行存錢取錢爲例)

     以前一直沒有養成一個梳理知識的習慣,今天覆習到多線程的知識的,想到的經典的存錢取錢多線程同步機制,今天在這裏梳理下,錯的地方請各位大佬指正

1:多線程同步的方法

1:使用synchronized關鍵字修飾方法和代碼塊(這就是兩種了)

2:使用特殊變量volatitle關鍵字修飾

3:JAVA5.0引入了重入鎖機制


1: 先看不引入鎖我們存錢取錢會發生什麼

    1.1 :新建一個銀行類Bank


package testThread;

import org.omg.CORBA.PUBLIC_MEMBER;
import org.w3c.dom.css.Counter;
/**
 * 一本正經的假裝銀行
 *
 */
public class Bank {
	//賬戶餘額
	private int sum=0;
	
	/**
	 * 存錢
	 * @param money
	 */
	public synchronized void addMoney(int money) {
		sum+=money;
		System.out.println(System.currentTimeMillis()+"存進"+money);
	}
	
	/**
	 * 取錢
	 */
	public void subMoney(int money)
	{
		if(sum-money<0)
		{
			System.out.println("餘額不足");
			return;
		}else {
			sum-=money;
			System.out.println(System.currentTimeMillis()+"取出"+money);
		}
		
	}
	
	/**
	 * 查詢餘額
	 */
	public void lookMoney()
	{
	System.out.println("賬戶餘額"+sum);	
	}

}

2:建立顧客完成存錢取錢的操作

package testThread;
/**
 * 主方法完成.假裝兩個存錢和取錢的的(大佬有錢,一直存取)
 */
public class TestMain {
	 public static void main(String args[]){  
	        final Bank bank=new Bank();  
	        Thread tadd=new Thread(new Runnable() {  
	            @Override  
	            public void run() {  
	                // TODO Auto-generated method stub  
	                while(true){  
	                    try {  
	                        Thread.sleep(1000);  
	                    } catch (InterruptedException e) {  
	                        // TODO Auto-generated catch block  
	                        e.printStackTrace();  
	                    }  
	                    bank.addMoney(100);  
	                    bank.lookMoney();  
	                }  
	            }  
	        });  

	        Thread tsub = new Thread(new Runnable() {  

	            @Override  
	            public void run() {  
	                // TODO Auto-generated method stub  
	                while(true){  
	                    bank.subMoney(100);  
	                    bank.lookMoney();  
	                    try {  
	                        Thread.sleep(1000);  
	                    } catch (InterruptedException e) {  
	                        // TODO Auto-generated catch block  
	                        e.printStackTrace();  
	                    }     
	                }  
	            }  
	        });  
	        tsub.start();  
	        tadd.start();  
	    }  
}
1.3:截圖

結果分析:我電腦CPU處理的太快了,一直沒看到真正想要的結果真實的結果類似這個

餘額不足  
賬戶餘額:0  

餘額不足  
賬戶餘額:100  
(有一百你不讓我取,這個貨)
1441792010959存進:100  
賬戶餘額:100  

爲什麼出現這樣的結果呢,這個就是多線程同時操作同一個資源,沒有用同步機制,發生了小問題

2:引入synchronized關鍵字bank類

package testThread;

import org.omg.CORBA.PUBLIC_MEMBER;
import org.w3c.dom.css.Counter;
/**
 * 假裝銀行
 *
 */
public class Bank {
	//賬戶餘額
	private int sum=0;
	
	/**
	 * 存錢
	 * @param money
	 */
	public synchronized void addMoney(int money) {
		sum+=money;
		System.out.println(System.currentTimeMillis()+"存進"+money);
	}
	
	/**
	 * 取錢
	 */
	public synchronized void subMoney(int money)
	{
		if(sum-money<0)
		{
			System.out.println("餘額不足");
			return;
		}else {
			sum-=money;
			System.out.println(System.currentTimeMillis()+"取出"+money);
		}
		
	}
	
	/**
	 * 查詢餘額
	 */
	public void lookMoney()
	{
	System.out.println("賬戶餘額"+sum);	
	}

}


在看看運行結果


咳咳,其實是上面的圖...........懂就好(synchronized 當然也可以修飾靜態的方法,不過調用它的時候會封鎖整個類);

3:synchronized修飾的代碼塊

package testThread;

import org.omg.CORBA.PUBLIC_MEMBER;
import org.w3c.dom.css.Counter;
/**
 * 假裝銀行
 *
 */
public class Bank {
	//賬戶餘額
	private int sum=0;
/**
	 * 存錢
	 * @param money
	 */
	public  void addMoney(int money) {
		synchronized(this)
		{
			sum+=money;
			System.out.println(System.currentTimeMillis()+"存進"+money);
		}
	}

/**
	 * 取錢
	 */
	public void subMoney(int money)
	{
		 synchronized(this)
		 {
		     if(sum-money<0)
			{
			System.out.println("餘額不足");
			return;
			}else {
				sum-=money;
				System.out.println(System.currentTimeMillis()+"取出"+money);
			} 
		 }
	
	}
	/**
	 * 查詢餘額
	 */
	public void lookMoney()
	{
	System.out.println("賬戶餘額"+sum);	
	}

}

結果和上面的一樣

4:使用volatitle關鍵字

package testThread;
/**
 * 假裝
 *
 */
public class Bank {
	//賬戶餘額
	private volatile int sum=0;
	
	/**
	 * 存錢
	 * @param money
	 */
	public void addMoney(int money) {
		sum+=money;
		System.out.println(System.currentTimeMillis()+"存進"+money);
	}
	
	/**
	 * 取錢
	 */
	public void subMoney(int money)
	{
		if(sum-money<0)
		{
			System.out.println("餘額不足");
			return;
		}else {
			sum-=money;
			System.out.println(System.currentTimeMillis()+"取出"+money);
		}
		
	}
	
	/**
	 * 查詢餘額
	 */
	public void lookMoney()
	{
	System.out.println("賬戶餘額"+sum);	
	}

}

結果分析

餘額不足  
賬戶餘額:0  

餘額不足  
賬戶餘額:100  

1441792010959存進:100  
賬戶餘額:100  

1441792011960取出:100  
賬戶餘額:0  

1441792011961存進:100  
賬戶餘額:100

結果又tmd不讓取了,,這個是爲啥呢

因爲volatitle不能保證原子操作導致,每次線程要訪問volatitle變量都是從內存中去讀的,而是不是從緩存中讀的,這就保證每個線程訪問的變量是一樣的,這就保證了同步

5:使用重入鎖實現線程同步

啥也不說,老規矩,上代碼

package testThread;

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

public class Bank{
	//賬戶餘額
	private  int sum=0;
	
	//1.1: 我們需要先申明這個鎖
	private Lock lock=new ReentrantLock();
	/**
	 * 存錢
	 * @param money
	 */
	public void addMoney(int money) {
		//1.2上鎖
		lock.lock();
		try {
			sum+=money;
			System.out.println(System.currentTimeMillis()+"存進"+money);
		} catch (Exception e) {
			// TODO: handle exception
		}finally{
			//1.3解鎖
			lock.unlock();
		}
		
	}
	
	/**
	 * 取錢
	 */
	public void subMoney(int money)
	{
		//1.1上鎖
		lock.lock();
		try {
			if(sum-money<0)
			{
				System.out.println("餘額不足");
				return;
			}else {
				sum-=money;
				System.out.println(System.currentTimeMillis()+"取出"+money);
			}
		} finally{
			//接鎖
			lock.unlock();
		}
		
		
	}
	
	/**
	 * 查詢餘額
	 */
	public void lookMoney()
	{
	System.out.println("賬戶餘額"+sum);	
	}

}

結果分析:

和上上圖結果一樣,不貼了,老鐵....

我們一般同步用synchronized,如果你想要用高級一點的,用lock,但別忘了釋放一下,不理會發生死鎖的哦,同學









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