Java多線程知識點整理(synchronized/volatile)

1、線程API知識點java

1.sleep()方法緩存

    做用是在指定的毫秒數內讓當前「正在執行的線程」休眠(暫停執行),這個正在執行的線程是指this.currentThread()返回的線程。安全

代碼:多線程

Thread.sleep(2000)

2.getId()方法併發

    做用是取得線程的惟一標識。ide

3.中止線程函數

    a.使用Thread.stop()方法,可是這個方法是廢棄的。性能

    b.使用Thread.interrupt()方法,但這個方法不會終止一個正在運行的線程,還須要加入一個判斷才能夠完成線程的終止。測試

    c.異常也能夠中止線程。this

    三種方法:

    1)、使用退出標誌,使線程正常退出,也就是當run方法完成後線程終止。

    2)、使用stop方法強行終止線程,可是不推薦使用這個方法,由於stop和suspend及resume同樣,都是做廢過時的方法,使用它們可能產生不可預料的結果。

    3)、使用interrupt方法中斷線程。

--------------------------------------------------------------------------------------------------

使用interrupt方法的效果並不像for+break語句那樣,立刻就中止,調用interrupt方法只是在當前線程打了一箇中止的標記,並非真的中止線程。代碼:

Thread thread = new Thread();
thread.start();
thread.interrupt();//打下標記

判斷線程是不是中止的狀態

a.this.interruptted():測試當前線程是否已經中斷,執行以後具備將狀態標誌清除爲false的功能。

b.this.isInterrupted():測試線程是否已經中斷,但不清除狀態。

4.yield方法

    做用是放棄當前的cpu資源,將它讓給其餘的任務去佔用CPU執行時間。但放棄的時間不肯定,有可能剛剛放棄,立刻又得到CPU時間片。

Thread.yield()

5.線程的優先級

    設置線程的優先級使用setPriority()方法。線程的優先級分爲1~10個等級,若是小於1或大於10,則JDK拋出異常throw new IllegalArgumentException().

Thread.currentThread().setPriority(6);

注意:線程的優先級具備隨機性。

6.守護線程

典型的守護線程就是垃圾回收線程,當進程中沒有守護線程了,則垃圾回收線程也就沒有存在的必要了,自動銷燬。守護即線程的做用是爲其餘線程的運行提供便利服務,最典型的應用就是GC。

Thread thread = new Thread();
thread.setDaemon(true);//設置爲守護線程

2、對象及變量的併發訪問(synchronized

1.synchronized同步方法

a.方法加synchronized

private int num;
	synchronized public void add(String name){
		try{
			if(name.equals("b")){
				 num =100;
				System.out.println(num);
			}else{
				num = 200;
				System.out.println(num);
			}
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}

關鍵字synchronized取得的所都是對象鎖,而不是一段代碼或方法(函數)看成鎖,因此那個線程先執行帶synchronized關鍵字的方法,那個線程就持有該方法所屬對象的鎖Lock,其餘線程只能呈等待狀態,可是前提是多個線程訪問的是同一個對象。

注意:set和get方法,若是set方法上有synchronized修飾,則get方法上也應該加上,否則就出現數據不一致。

2.synchronized鎖重入

    概念是:本身能夠再次獲取本身的內部鎖。好比有1條線程得到了某個對象的鎖,此時這個對象鎖尚未釋放,當其次想要獲取這個對象的鎖的時候仍是能夠獲取的,若是不可鎖重入的話,就會形成死鎖。

synchronized public void service1(){
		System.out.println("service1");
		service2();
	}
	synchronized public void service2(){
		System.out.println("service2");
	}

同時,當存在父子類繼承關係時。子類徹底能夠經過「可重入鎖」調用父類的同步方法,可是鎖不具備繼承性。假如線程出現了異常,鎖自動釋放。

3.synchronized同步代碼塊

public void service3(){
		synchronized (this) {
			System.out.println("service3");
		}
	}

    在使用同步synchronized(this)代碼塊時須要注意的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其餘線程對同一個object中全部其餘synchronized(this)同步代碼塊的訪問將被阻塞,這說明synchronized使用的是「對象監視器」。同時代碼塊也是鎖定當前對象的。

      多個線程調用同一個對象中的不一樣名稱的synchronized同步方法或synchronized(this)同步代碼塊時,調用的效果就是按順序執行,也就是同步的,阻塞的。

(1)、synchronized同步方法

a.對其餘synchronized同步方法或synchronized(this)同步代碼塊調用呈阻塞狀態。

b.同一時間只有一個線程能夠執行synchronized同步方法中的代碼。

同理synchronized代碼塊也是同樣的。

(2)、同步代碼塊

支持任意對象做爲對象監視器來實現同步的功能。這個「任意對象」大多數是實例變量及方法的參數,使用格式爲synchronized(非this對象)。

a.在多線程持有「對象監視器」爲同一個對象的前提下,同一時間只有一個線程能夠執行synchronized(非對象x)同步代碼塊中的代碼

b.當持有「對象監視器」爲同一個對象的前提下,同一時間只有一個線程能夠執行synchronized(非this對象X)同步代碼中的代碼

//同步代碼對象
	class Service{
	   private String anyString = new String();//若是這個放進setString方法裏面就會出現數據的不一致。
	   public void setString(String userName){
		   synchronized (anyString) {
			System.out.println("業務處理");
			try {
				Thread.sleep(20000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	   }

	}
	class ThreadA extends Thread{
		private Service service ;
		
		public ThreadA(Service service) {
			super();
			this.service = service;
		}

		public void run(){
			System.out.println("業務處理A");
			service.setString("a");
		}
	}
	
	class ThreadB extends Thread{
		private Service service ;
		
		public ThreadB(Service service) {
			super();
			this.service = service;
		}

		public void run(){
			System.out.println("業務處理B");
			service.setString("B");
		}
	}
	
	public static void main(String[] args) {
		Service service = new Service();
		ThreadA threadA = new ThreadA(service);
		threadA.setName("a");
		threadA.start();
		ThreadB threadB = new ThreadB(service);
		threadB.setName("B");
		threadB.start();
	}

總結:synchronized(非this對象X)格式的寫法是將X對象自己做爲對象監視器,如下是結論。

a.當多個線程同時執行synchronized(X){}同步代碼塊時呈同步效果。

b.當其餘線程執行X對象中synchronized同步時呈同步效果。

c.當其餘線程執行X對象方法時裏面的synchronized(this)代碼塊時也呈現同步效果。

靜態同步synchronized和同步synchronized 的區別:synchronized關鍵字加到static靜態方法上是給Class類上鎖,而synchronized關鍵字加到非static靜態方法上是給對象加鎖。

注意:

將synchronized(String) 同步塊與String聯合使用時,須要注意的是由於JVM會爲String進行緩存,因此會出現線程不能執行的問題。因此同步synchronized代碼塊時都不使用String做爲鎖對象,而改用其餘,好比new Object()實例一個Object對象,它不會放入緩存

總結:

關鍵字synchronized能夠保證在同一時刻,只有一個線程能夠執行某一個方法或一個代碼塊。它包含兩個特徵:互斥性和可見性。同步synchronized不只能夠解決一個線程看到對象處於不一致的狀態,還能夠保證進入同步方法或者同步代碼塊的每一個線程,都看到由同一個鎖保護以前的修改爲果。

4.死鎖

4.1 同步synchronized方法無線等待與解決

死鎖舉例代碼一:

synchronized public void service1(){
		System.out.println("service1");
		
		boolean isCount = true;
		while(isCount){
			
		}
		System.out.println("service1 end");
		
	}

死鎖舉例代碼二:

public class test2 implements Runnable{

	public Object lock1 = new Object();
	
	public Object lock2 = new Object();
	public String uname;
	
	public void setUname(String uname) {
		this.uname = uname;
	}


	@Override
	public void run() {
		if(uname.equals("a")){
			synchronized (lock1) {
				
				try {
					System.out.println(uname);
					Thread.sleep(20000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				synchronized (lock2) {
					System.out.println("lock2");
				}
				
			}
		}
		
		
		if(uname.equals("b")){
			synchronized (lock2) {
				
				try {
					System.out.println(uname);
					Thread.sleep(20000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
				synchronized (lock1) {
					System.out.println("lock1");
				}
				
			}
		}
		
		
	}

}

總結:1.while循環加上推薦語句,容易出現。

           2.雙方互相有對方的鎖的狀況。

3、對象及變量的併發訪問(volatile

1.volatile相關的概念和做用

關鍵字volatile的主要做用是使變量在多個線程間可見。

解決上面死鎖的問題。

class test extends Thread{

    volatile private boolean isRuning = true;
	
	public boolean isRuning(){
		return isRuning;
	}
	

   public void run(){
    
    while(isRuning == true){
    }

   }
}

關鍵字volatile的做用是強制從公共堆棧中取得變量的值,而不是從線程私有數據棧中取得變量的值。

使用volatile關鍵字增長了實例變量在多個線程之間的可見性。但volatile關鍵字最致命的缺點是不支持原子性。

注意:改變實例變量中的數據,好比i++,並非原子操做,也就不是線程安全的,詳細步驟分解爲:

a.從內存中取出i的值;b.計算i的值;c.將i的值寫到內存中。步驟b容易出息問題。

總結:對於volatile修飾的變量,JVM虛擬機只是保證從內存加載到線程工做內存的值是最新的,也就是說,volatile關鍵字解決的是變量讀時的可見性問題,但沒法保證原子性,對於多個線程訪問同一個實例變量仍是須要同步加鎖。

對於上面的i++,JDK 提供了AtomicInteger原子類進行實現。相關的API在util包下面。

2.volatile和synchronized的對比

a.關鍵字volatile是線程同步的輕量級實現,因此volatile性能比synchronized要好,而且volatile只能修飾變量,而synchronized能夠修飾方法,以及代碼塊。

b.多線程訪問volatile不會發生阻塞,而synchronized會出現阻塞。

c.volatile能保證數據的可見性,但不能保證原子性;而synchronized能夠保證原子性,也能夠間接保證可見性,由於它會將私有內存和公共內存中的數據作同步。

相關文章
相關標籤/搜索