Java Synchronized()

1、Synchronized使用的位置

同步的實現固然是採用鎖了,java中使用鎖的兩個基本工具是 synchronized 和 Lock。java

一直很喜歡synchronized,由於使用它很方便。好比,須要對一個方法進行同步,那麼只需在方法的簽名添加一個synchronized關鍵字。併發

// 未同步的方法ide

public void test() {}
1.1 放在方法的簽名上面

// 同步的方法工具

pubilc synchronized void test() {}
1.2放在代碼塊上面

synchronized 也能夠用在一個代碼塊上,看測試

public void test() {
     synchronized(obj) {
          System.out.println("===");
     }
}
1.3兩種方式的區別

synchronized 用在方法和代碼塊上有什麼區別呢?this

synchronized 用在方法簽名上(以test爲例),當某個線程調用此方法時,會獲取該實例的對象鎖,方法未結束以前,其餘線程只能去等待。當這個方法執行完時,纔會釋放對象鎖。其餘線程纔有機會去搶佔這把鎖,去執行方法test,可是發生這一切的基礎應當是全部線程使用的同一個對象實例,才能實現互斥的現象。不然synchronized關鍵字將失去意義。spa

(可是若是該方法爲類方法,即其修飾符爲static,那麼synchronized 意味着某個調用此方法的線程當前會擁有該類的鎖,只要該線程持續在當前方法內運行,其餘線程依然沒法得到方法的使用權!)線程

這樣看來使用類變量也能夠實現線程的互斥。code

synchronized 用在代碼塊的使用方式:synchronized(obj){//todo code here}對象

當線程運行到該代碼塊內,就會擁有obj對象的對象鎖,若是多個線程共享同一個Object對象,那麼此時就會造成互斥!特別的,當obj == this時,表示當前調用該方法的實例對象。即

public void test() {
     ...
     synchronized(this) {
          // todo your code
     }
     ...
}

 

此時,其效果等同於

public synchronized void test() {
     // todo your code
}

使用synchronized代碼塊,能夠只對須要同步的代碼進行同步,這樣能夠大大的提升效率。

小結:

使用synchronized 代碼塊相比方法有兩點優點:

一、能夠只對須要同步的使用

二、與對象的wait()/notify()/nitifyAll()一塊兒使用時,比較方便

1.4 對象的wait() 與notify()/notifyAll()

這三個方法都是Object的方法,並非線程的方法!

wait():釋放佔有的對象鎖,線程進入等待池,釋放cpu,而其餘正在等待的線程便可搶佔此鎖,得到鎖的線程便可運行程序。而sleep()不一樣的是,線程調用此方法後,會休眠一段時間,休眠期間,會暫時釋放cpu,但並不釋放對象鎖也就是說,在休眠期間,其餘線程依然沒法進入此代碼內部。休眠結束,線程從新得到cpu,執行代碼。wait()和sleep()最大的不一樣在於wait()會釋放對象鎖,而sleep()不會!

notify(): 該方法會喚醒由於調用對象的wait()而等待的線程,其實就是對對象鎖的喚醒,從而使得wait()的線程能夠有機會獲取對象鎖。調用notify()後,並不會當即釋放鎖,而是繼續執行當前代碼,直到synchronized中的代碼所有執行完畢,纔會釋放對象鎖。JVM則會在等待的線程中調度一個線程去得到對象鎖,執行代碼。須要注意的是,wait()和notify()必須在synchronized代碼塊中調用。notifyAll()則是喚醒全部等待的線程。

1.5 wait 與notify 使用實例

package com.yuan.test;
//生產者
public class Produce implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
		int count = 10;
		while (count > 0) {
			synchronized (Test.obj) {//Test.obj是一個靜態變量

				// System.out.print("count = " + count);
				System.out.print("A");
				count--;
				try {
					Thread.sleep(5000);//讓生產該線程sleep
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				/*
				 * 另外,注意一點:若是要把notify和wait方法放在一塊兒用的話,
				 * 必須先調用notify後調用wait,
				 * 由於若是調用完wait,該線程就已經不是currentthread了。
                                 */
				try {
					Test.obj.notify();
					Test.obj.wait();
					
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}

		}

	}
}
package com.yuan.test;
//消費者
public class Consumer implements Runnable {

	@Override
	public void run() {
		// TODO Auto-generated method stub
       int count=10;
       while(count>0){
    	    synchronized (Test. obj) {//Test.obj是一個靜態變量
				System.out.println("B");
				count--;
				/*
				 * 另外,注意一點:若是要把notify和wait方法放在一塊兒用的話,
				 * 必須先調用notify後調用wait,
				 * 由於若是調用完wait,該線程就已經不是currentthread了。
                                 */
				 try {
					 Test. obj.notify(); // 喚醒一個線程
                                         Test. obj.wait();
                    
                     
               } catch (InterruptedException e) {
                      // TODO Auto-generated catch block
                     e.printStackTrace();
               }
			}   
    	   
       }
	}

}
package com.yuan.test;
//測試類
public class Test {
	public static final Object obj = new Object();
    
    public static void main(String[] args) {
          
           new Thread( new Produce()).start();//線程併發執行
           new Thread( new Consumer()).start();
          
    }
}

1.6 Lock 來實現notify()和wait()一樣的功能

ReentrantLock 與synchronized有相同的併發性和內存語義,還包含了中斷鎖等候和定時鎖等候,意味着線程A若是先得到了對象obj的鎖,那麼線程B能夠在等待指定時間內依然沒法獲取鎖,那麼就會自動放棄該鎖。

可是因爲synchronized是在JVM層面實現的,所以系統能夠監控鎖的釋放與否,而ReentrantLock使用代碼實現的,系統沒法自動釋放鎖,須要在代碼中finally子句中顯式釋放鎖lock.unlock();

一樣的例子,使用lock 如何實現呢?

package com.yuan.test;

import java.util.concurrent.locks.Lock;
//生產者
public class Produce implements Runnable {

	private Lock lock;
    public Produce(Lock lock) {
           this. lock = lock;
    }
    @Override
    public void run() {
           // TODO Auto-generated method stub
           int count = 10;
           while (count > 0) {
                try {
                    lock.lock();
                    count --;
                    System. out.print( "A");
               } finally {
                    lock.unlock();//釋放lock對象這個鎖
                     try {
                          Thread. sleep(90L);
                    } catch (InterruptedException e) {
                           // TODO Auto-generated catch block
                          e.printStackTrace();
                    }
               }
          }
    }
}
package com.yuan.test;
//消費者
import java.util.concurrent.locks.Lock;

public class Consumer implements Runnable {
/*
 * 可是因爲synchronized是在JVM層面實現的,
 * 所以系統能夠監控鎖的釋放與否,而ReentrantLock使用代碼實現的,
 * 系統沒法自動釋放鎖,
 * 須要在代碼中finally子句中顯式釋放鎖lock.unlock();
 * 
 */
	private Lock lock;
    public Consumer(Lock lock) {
           this. lock = lock;
    }
    @Override
    public void run() {
           // TODO Auto-generated method stub
           int count = 10;
           while( count > 0 ) {
                try {
                     lock.lock();
                     count --;
                    System. out.print( "B");
               } finally {
                     //lock.unlock(); 
                     try {
                          Thread. sleep(91L);
                    } catch (InterruptedException e) {
                           // TODO Auto-generated catch block
                          e.printStackTrace();
                    }
               }
          }

    }

}
package com.yuan.test;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
//測試
public class Test {
	private static final long serialVersionUID = 7373984872572414699L;
	
	public static void main(String[] args) {
		Lock lock = new ReentrantLock();//生成一個用於上鎖的對象,查看源碼發現有能夠查看等待獲取對象長度的方法

		Consumer consumer = new Consumer(lock);
		Produce producer = new Produce(lock);
		new Thread(consumer).start();
		new Thread(producer).start();
		
		
	}
}
相關文章
相關標籤/搜索