線程基礎

 

線程安全:

當多個線程訪問某一個類(對象或方法)時,這個對象始終都能表現出正確的行爲,那麼這個類(對象或方法)就是線程安全的

 

synchronized:

 

 可以在任意對象及方法上加鎖,而加鎖的這段代碼稱爲"互斥區"或"臨界區"

 

例子:

 

public class MyThread extends Thread{
	
	private int count = 5 ;
	
	//synchronized加鎖
	public void run(){
		count--;
		System.out.println(this.currentThread().getName() + " count = "+ count);
	}
	
	public static void main(String[] args) {
		
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread,"t1");
		Thread t2 = new Thread(myThread,"t2");
		Thread t3 = new Thread(myThread,"t3");
		Thread t4 = new Thread(myThread,"t4");
		Thread t5 = new Thread(myThread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}
輸出結果:t2 count = 3

                   t1 count = 3

                   t4 count = 2

                   t3 count = 1

                   t5 count = 0

 

    count值並沒有按照順序去減

    下面 將run方法加上synchronized

public class MyThread extends Thread{
	
	private int count = 5 ;
	
	//synchronized加鎖
	public synchronized void run(){
		count--;
		System.out.println(this.currentThread().getName() + " count = "+ count);
	}
	
	public static void main(String[] args) {
		 
		MyThread myThread = new MyThread();
		Thread t1 = new Thread(myThread,"t1");
		Thread t2 = new Thread(myThread,"t2");
		Thread t3 = new Thread(myThread,"t3");
		Thread t4 = new Thread(myThread,"t4");
		Thread t5 = new Thread(myThread,"t5");
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
	}
}
輸出結果:t1 count = 4
              t3 count = 3
              t4 count = 2
              t2 count = 1
              t5 count = 0

 

結論分析:

              當多個線程訪問myThread的run方法時,以排隊的方式進行處理(這裏排對是按照CPU分配的先後順序而定的),一個線程想要執行synchronized修飾的方法裏的代碼:

1 嘗試獲得鎖

2 如果拿到鎖,執行synchronized代碼體內容;拿不到鎖,這個線程就會不斷的嘗試獲得這把鎖,直到拿到爲止,

   而且是多個線程同時去競爭這把鎖。(也就是會有鎖競爭的問題)

 

 對象鎖和類級別鎖

 

 關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock)

 在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。

 

列子

 

public class MultiThread {

	private int num = 0;
	
	/** static */
	public synchronized void printNum(String tag){
		try {
			
			if(tag.equals("a")){
				num = 100;
				System.out.println("tag a, set num over!");
				Thread.sleep(1000);
			} else {
				num = 200;
				System.out.println("tag b, set num over!");
			}
			
			System.out.println("tag " + tag + ", num = " + num);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//注意觀察run方法輸出順序
	public static void main(String[] args) {
		
		//倆個不同的對象
		final MultiThread m1 = new MultiThread();
		final MultiThread m2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("a");
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			@Override 
			public void run() {
				m2.printNum("b");
			}
		});		
		
		t1.start();
		t2.start();
		
	}
結果
	tag a, set num over!
        tag b, set num over!
        tag b, num = 200
        tag a, num = 100

 結果分析: 線程m1 m2都執行了run方法,如果m1線程調到run方法時,獲得了對象鎖,m2等待m1執行完才執行,結果應該是 :

 

         

tag a, set num over!
tag a, num = 100
tag b, set num over!
tag b, num = 200

 其實不然,m1 m2 是2個對象,有2把鎖,沒有衝突,所以都可以執行,可以使用類級別鎖,輸出結果會按照順序

 

public class MultiThread {

	private static int num = 0;
	
	/** static */
	public static synchronized void printNum(String tag){
		try {
			
			if(tag.equals("a")){
				num = 100;
				System.out.println("tag a, set num over!");
				Thread.sleep(1000);
			} else {
				num = 200;
				System.out.println("tag b, set num over!");
			}
			
			System.out.println("tag " + tag + ", num = " + num);
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	//注意觀察run方法輸出順序
	public static void main(String[] args) {
		
		//倆個不同的對象
		final MultiThread m1 = new MultiThread();
		final MultiThread m2 = new MultiThread();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				m1.printNum("a");
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
			@Override 
			public void run() {
				m2.printNum("b");
			}
		});		
		
		t1.start();
		t2.start();
		
	}
結果:
tag a, set num over!
tag a, num = 100
tag b, set num over!
tag b, num = 200

 

結果分析:在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類),無論創建幾個對象,調用printNum()方法,獲取的都是類級別鎖

 

結論:

   關鍵字synchronized取得的鎖都是對象鎖,而不是把一段代碼(方法)當做鎖,所以代碼中哪個線程先執行synchronized關鍵字的方法,哪個線程就持有該方法所屬對象的鎖(Lock),

   在靜態方法上加synchronized關鍵字,表示鎖定.class類,類一級別的鎖(獨佔.class類)。

 

對象鎖的同步和異步

同步: synchronized

  同步的概念就是共享,我們要牢牢記住共享這2個字,如果不是“共享”資源,就沒必要進行同步

異步:

  異步的概念就是獨立,相互之間不受任何制約,就好像學習http的時候,在頁面發起ajax請求,還可以繼續瀏覽和操作頁面的內容,二者之間沒有任何關係

public class MyObject {

       //同步方法
	public synchronized void method1(){
		try {
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/** 異步方法,沒有任何鎖的機制就是異步方法*/
	public void method2(){
			System.out.println(Thread.currentThread().getName());
	}
}

 區別:

     method1異步方法多個線程需要排隊的方法,而method2同步方法多個線程可以併發的訪問

例子

public class MyObject {

	public synchronized void method1(){
		try {
			System.out.println(Thread.currentThread().getName());
			Thread.sleep(4000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	/** synchronized */
	public void method2(){
			System.out.println(Thread.currentThread().getName());
	}
	
	public static void main(String[] args) {
		
		final MyObject mo = new MyObject();
			
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				mo.method1();
			}
		},"t1");
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				mo.method2();
			}
		},"t2");
		
		t1.start();
		t2.start();
		
	}
	
}
結果:
同時輸出 t1 t2,沒有等待4秒輸出t2

 

分析:

  t1線程先持有object對象的Lock鎖,t2線程可以以異步的方式調用對象中的非synchronized修飾的方法

  t1線程先持有object對象的Lock鎖,t2線程如果在這個時候調用對象中的同步(synchronized)方法則需等待,也就是同步

 

發佈了25 篇原創文章 · 獲贊 3 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章