繼續昨天的:java多線程

    今天接着昨天的線程再說一說線程的同步問題吧,那麼synchronized這個關鍵字就十分重要的啦,多線程可以完成數據的共享,那麼當然要同步啦,就是說當我要訪問一個數據,再我沒改時,別人是不允許訪問的,要是別人也訪問了這個數據,在別人訪問後我又把數據改了,那麼別人在寫會是就會發生錯誤,即讀髒了數據。下面說一個比較容易理解的例子吧!

經典的銀行取款問題,有一個銀行賬戶,還有餘額2000元,現在A通過銀行卡從中取出800,而同時另外一個人B通過存摺也從這個賬戶中取了800。在A取錢的時候還沒等把餘額寫回,則另一個人B此時又取走了800,然後兩人同時將餘額寫回,那麼取了兩次,結果餘額還是1200。當然了這個問題在現實中很不現實,根本是不可能的事,所以要做到這點就要弄明白同步這個問題,當A取錢的時候,拒絕B來取錢,只有在A取完錢把餘額寫回了纔可以允許B進行取錢。

下面看一下兩人同時取錢的情況:

 

package day16;
public class TestAccount {
	public static void main(String[] args) {
		Account account = new Account();
		R r1 = new R(account);
		R r2 = new R(account);
		Thread th1 = new Thread(r1,"me");
		Thread th2 = new Thread(r2,"her");
		th1.start();
		th2.start();
		try {
			Thread.sleep(5000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("餘額爲:"+account.balance);
	}
}
class Account{
	public /*synchronized*/int balance = 2000;
	 
	public void withDraw(){
		int temp = balance;
		temp = temp - 800;
		try {
			Thread.sleep(2000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		balance = temp;
		System.out.println(Thread.currentThread().getName()+"     "+this.balance);
	}
}
class R implements Runnable{
	private Account account;
	public R(Account account){
		this.account = account;
	}
	@Override
	public void run() {
		account.withDraw();
	}
	
}
運行結果:
her     1200
me     1200
餘額爲:1200
 

通過對象鎖,我們可以避免多個線程在訪問同一個數據時不同步問題,但這種方式也很容易引起“死鎖”,所謂死鎖,就是指兩個線程,都相互等待對方釋放lock,如經典哲學家進餐的問題,這種情形是不可預測或避開的,我們應採取措施避免死鎖的出現。出現死鎖的程序將一直等待,所以要避免這種情況的出現,要避免死鎖,應該確保在獲取多個鎖時,在所有的線程中都以相同的順序獲取鎖。

下面再看一個解決同步問題的,即壓棧與彈棧的操作,在同一個棧時壓棧的時候不允許彈棧,反之亦然,記得數據結構中介紹的,彈棧時,指針先減,然後將內容彈出;入棧則相反,先將內容壓入,然後指針再加,因爲指針指的是棧top的再上一個位置,說道棧,在這裏再說一下java中棧和堆與引用和對象之間的關係吧:

如有一個Person類有name和age屬性

一下代碼:

Person p1 = new Person(“張三”,20);

Person p2 = p1;

則可以表示爲以下所示:

p1表示引用,而new出來的則是對象,對象存在堆中,而引用則存在棧中,可以將引用看成地址,即p1p2兩個引用擁有相同的地址所以指向了相同的區域,另外引用的地址相同,那麼說明他們指向的內容肯定相同,即equals相同,hascode也相同,但hascode相同時,不能說明地址也相同,也不能說明equals相同,hascode一般在hashSethashTable可以用到,在別處基本上不用。

下面再看一個解決同步問題的例子吧,再熟悉熟悉~

先定義了一個接口
package my03;

public interface StackInterface {
	public void push(int n);
	public int[] pop();
}
Push和pop方法
package my03;

public class safeStack implements StackInterface{

	private int top = 0;
	private int[] values = new int[10];
	
	public  void push(int n){
		synchronized (this) {
			
		values[top]=n;
		System.out.println("壓入數字:"+n);
		top++;
		System.out.println("壓棧操作完成");
		}
		}
	public  int[] pop(){
		synchronized (this) {
			
		System.out.print("彈棧");
		top--;
		System.out.println(values[top]);
		int[] test = {values[top],top};
		return test;
		}
	}
}
Pop線程
package my03;

public class PopThread implements Runnable{
	
	private StackInterface s;
	public PopThread(StackInterface s){
		this.s = s;
	}
	public  void run(){
			System.out.println("---"+s.pop()[1]+"---");
		}
	}
Push線程
package my03;

public class PushThread implements Runnable {
	
	public PushThread(){
		
	}
	private StackInterface s;
	public PushThread(StackInterface s){
		this.s = s;
	}
	public  void run(){
		
			for(int i = 0; i < 10; i++){
				System.out.print(".");
			}
			int k = 23;
			s.push(k);
			}
	}
主方法:
package my03;

public class TestsafeStack {
	public static void main(String[] args) {
		safeStack s = new safeStack();
		s.push(1);
		s.push(2);
		PushThread r1 = new PushThread(s);
		PopThread  r2 = new PopThread(s);
		Thread th1 = new Thread(r1);
		Thread th2 = new Thread(r2);
		th1.start();
		th2.start()
}
}
輸出如下:
壓入數字:1
壓棧操作完成
壓入數字:2
壓棧操作完成
.........彈棧.2
壓入數字:23
壓棧操作完成
---1---
由上可見,壓棧和彈棧操作內部是不允許中斷的~

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