java基礎入門-多線程同步淺析-以銀行轉賬為樣例


在說之前先普及一下線程是什么?

線程:說白了就是一個任務片段

進程:是一個具有獨立功能的程序關於某個數據集合的一次執行活動。一個進程有一個或者多個線程


線程與進程的本質差別就是有么有數據共享空間。線程之間能夠共享數據。進程不能夠


以下進入主題:線程間的同步

因為如今業務流程添加。業務節點也添加。使用業務的人員也同一時候添加。這個時候就不可避免的出現並發問題,多個線程同一時候訪問操作某一個數據單元

我們以銀行轉賬為例說明,以下先上代碼:

建立一個銀行的類,里面主要包含三個方法,一個是轉賬,一個是得到現有銀行存款總數,一個是得到如今存戶數量

public class Bank {

	private final double[] accounts;

	public Bank(int n, double initialBalance) {
		accounts = new double[n];
		for (int i = 0; i < accounts.length; i++) {
			accounts[i] = initialBalance;
		}
	}

	public void transfer(int from, int to, double amount) {
		if (accounts[from] < amount) {
			return;
		}
		System.out.println(Thread.currentThread());
		accounts[from] -= amount;
		System.out.printf("%f from %d to %d ", amount, from, to);
		accounts[to] += amount;
		System.out.println("total:" + getTotalBalance());
	}

	public double getTotalBalance() {
		double sum = 0d;
		for (int i = 0; i < accounts.length; i++) {
			sum += accounts[i];
		}
		return sum;
	}

	public int getAccountSize() {
		return accounts.length;
	}
}


以下是轉賬類,由於須要並發操作。所以實現Runnable接口


public class TransferRunnable implements Runnable {


	private Bank bank;
	private int fromAccount = 0;
	private double maxAmount = 0;


	public TransferRunnable(Bank b, int fromAccount, double maxAmount) {
		this.bank = b;
		this.fromAccount = fromAccount;
		this.maxAmount = maxAmount;
	}


	@Override
	public void run() {
		double amount = maxAmount * Math.random();
		int toAccount = (int) ((int) bank.getAccountSize() * Math.random());
		bank.transfer(fromAccount, toAccount, amount);
		try {
			Thread.sleep((long) (100L * Math.random()));
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}


}

以下是測試類:

public class Test {
	public static void main(String[] args) {
		Bank bank = new Bank(100, 1000);
		for (int i = 0; i < 3; i++) {
			TransferRunnable transferRunnable = new TransferRunnable(bank, i,
					1000);
			Thread thread = new Thread(transferRunnable);
			thread.start();
		}
	}
}

輸出結果:

Thread[Thread-0,5,main]
Thread[Thread-2,5,main]
Thread[Thread-1,5,main]


430.796266 from 0 to 75

714.274395 from 1 to 88

849.880218 from 2 to 33 


total:98435.8453871716
total:99150.11978192833
total:100000.0

我們看上面的結果,特別是最后三行的total總數,發現,第一第二次轉賬后,總數不正確了。細致觀察打印結果,因為並行運行任務,並且中間因為是由cup分配運行順序。所以我們看到的結果並沒有全然依照我們的方法所實現的那樣輸出出來

因為出現這種問題,我們引入“鎖”的概念。因為這里面是淺析,就不針對鎖具體說明,以下我們在bank類里面的轉賬方法上面加上最簡單最經常使用的鎖synchronized,看看結果是如何:

public class Bank {

	private final double[] accounts;

	public Bank(int n, double initialBalance) {
		accounts = new double[n];
		for (int i = 0; i < accounts.length; i++) {
			accounts[i] = initialBalance;
		}
	}

	//加了鎖
	public synchronized void transfer(int from, int to, double amount) {
		if (accounts[from] < amount) {
			return;
		}
		System.out.println(Thread.currentThread());
		accounts[from] -= amount;
		System.out.printf("%f from %d to %d ", amount, from, to);
		accounts[to] += amount;
		System.out.println("total:" + getTotalBalance());
	}

	public double getTotalBalance() {
		double sum = 0d;
		for (int i = 0; i < accounts.length; i++) {
			sum += accounts[i];
		}
		return sum;
	}

	public int getAccountSize() {
		return accounts.length;
	}
}


輸出結果:

Thread[Thread-0,5,main]
187.754955 from 0 to 50 total:100000.0


Thread[Thread-1,5,main]
282.138799 from 1 to 90 total:100000.0


Thread[Thread-2,5,main]
217.089515 from 2 to 86 total:100000.00000000001


上面的輸出結果基本一致,最后一個結果出現小數問題,主要是因為我數據精度的問題,興許能夠通過其它設置來避免。


程序里面因為出現了鎖,所以在性能上面不可避免的出現下降,特別是在一些大型程序里面。所以這里面須要依據實際業務所需,把鎖的范圍鎖到比較小的范圍,使得性能不會大幅度的下降。


最后我們把上面的東西總結出一個圖






免責聲明!

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



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