synchronized

synchronized 關鍵字,表明這個方法加鎖,至關於無論哪個線程(例如線程A),運行到這個方法時,都要檢查有沒有其它線程B(或者C、 D等)正在用這個方法,有的話要等正在使用synchronized方法的線程B(或者C 、D)運行完這個方法後再運行此線程A,沒有的話,直接運行。它包括兩種用法:synchronized 方法和 synchronized 塊。

synchronized 方法

聲明是爲了定義 變量的做用範圍和 做用域
經過在方法聲明中加入 synchronized 關鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類 成員變量的訪問:每一個類實例對應一把鎖,每一個 synchronized 方法都必須得到調用該方法的類實例的鎖方能執行,不然所屬 線程阻塞,方法一旦執行,就獨佔該鎖,直到從該方法返回時纔將鎖釋放,此後被阻塞的線程方能得到該鎖,從新進入可 執行狀態。這種機制確保了同一時刻對於每個類實例,其全部聲明爲 synchronized 的成員函數中至多隻有一個處於可 執行狀態(由於至多隻有一個可以得到該類實例對應的鎖),從而有效避免了類 成員變量的訪問衝突(只要全部可能訪問類成員變量的方法均被聲明爲 synchronized)。
在 Java 中,不光是類實例,每個類也對應一把鎖,這樣咱們也可將類的 靜態成員函數聲明爲 synchronized ,以控制其對類的 靜態成員變量的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明爲synchronized 將會大大影響效率,典型地,若將 線程類的方法 run() 聲明爲 synchronized ,因爲在線程的整個 生命期內它一直在運行,所以將致使它對本類任何 synchronized 方法的調用都永遠不會成功。固然咱們能夠經過將訪問類 成員變量的代碼放到專門的方法中,將其聲明爲 synchronized ,並在主方法中調用來解決這一問題,可是 Java 爲咱們提供了更好的解決辦法,那就是 synchronized 塊。

synchronized 塊

經過 synchronized 關鍵字來聲明synchronized 塊。語法以下:
synchronized(syncObject) {
//容許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須得到對象 syncObject (如前所述,能夠是類實例或類)的鎖方能執行,具體機制同前所述。因爲能夠針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。

編輯本段對synchronized(this)的一些理解

1、當兩個併發線程訪問同一個對象object中的這個synchronized(this) 同步代碼塊時,一個時間內只能有一個線程獲得執行。另外一個線程必須等待當前線程執行完這個代碼塊之後才能執行該代碼塊。
2、當一個線程訪問object的一個synchronized(this) 同步代碼塊時,其餘線程對object中全部其它synchronized(this)同步代碼塊的訪問將被阻塞。
3、然而,當一個 線程訪問object的一個synchronized(this) 同步代碼塊時,另外一個線程仍然能夠訪問該object中的除synchronized(this)同步代碼塊之外的部分。
4、第三個例子一樣適用其它 同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就得到了這個object的 對象鎖。結果,其它線程對該object對象全部同步代碼部分的訪問都被暫時阻塞。
5、以上規則對其它 對象鎖一樣適用

編輯本段synchronized的4種用法

1.方法聲明時使用,放在範圍操做符(public等)後,其返回類型聲明(void等)以前。即一次只能有一個線程進入該方法,其餘線程要想在此時調用該方法,只能排隊等候,當前線程(就是在synchronized方法內部的線程)執行完該方法後,別的線程才能進入。
例如:
public synchronized void synMethod() {
//方法體
}
2.對某一代碼塊使用,synchronized後跟括號,括號裏是變量,這樣,一次只有一個線程進入該代碼塊。例如:
public int synMethod(Object a1){
synchronized(Object) {
//一次只能有一個線程進入
}
}
3.synchronized後面括號裏是一對象,此時,線程得到的是對象鎖。例如:
public class MyThread implements Runnable {
public static void main(String args[]) {
MyThread mt = new MyThread();
Thread t1 = new Thread(mt, "t1");
Thread t2 = new Thread(mt, "t2");
Thread t3 = new Thread(mt, "t3");
Thread t4 = new Thread(mt, "t4");
Thread t5 = new Thread(mt, "t5");
Thread t6 = new Thread(mt, "t6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName());
}
}
}
對於3,若是線程進入,則獲得對象鎖,那麼別的線程在該類全部對象上的任何操做都不能進行。在對象級使用鎖一般是一種比較粗糙的方法。爲何要將整個對象都上鎖,而不容許其餘線程短暫地使用對象中其餘同步方法來訪問共享資源?若是一個對象擁有多個資源,就不須要只爲了讓一個線程使用其中一部分資源,就將全部線程都鎖在外面。因爲每一個對象都有鎖,能夠以下所示使用虛擬對象來上鎖:
class FineGrainLock {
MyMemberClass x, y;
Object xlock = new Object(), ylock = new Object();
public void foo() {
synchronized(xlock) {
//access x here
}
//do something here - but don‘t use shared resources
synchronized(ylock) {
//access y here
}
}
public void bar() {
synchronized(this) {
//access both x and y here
}
//do something here - but don‘t use shared resources
}
}
4.synchronized後面括號裏是類。例如:
class ArrayWithLockOrder{
private static long num_locks = 0;
private long lock_order;
private int[] arr;
public ArrayWithLockOrder(int[] a)
{
arr = a;
synchronized(ArrayWithLockOrder.class) {//-----------------------------------------這裏
num_locks++; // 鎖數加 1。
lock_order = num_locks; // 爲此對象實例設置惟一的 lock_order。
}
}
public long lockOrder()
{
return lock_order;
}
public int[] array()
{
return arr;
}
}
class SomeClass implements Runnable
{
public int sumArrays(ArrayWithLockOrder a1,
ArrayWithLockOrder a2)
{
int value = 0;
ArrayWithLockOrder first = a1; // 保留數組引用的一個
ArrayWithLockOrder last = a2; // 本地副本。
int size = a1.array().length;
if (size == a2.array().length)
{
if (a1.lockOrder() > a2.lockOrder()) // 肯定並設置對象的鎖定
{ // 順序。
first = a2;
last = a1;
}
synchronized(first) { // 按正確的順序鎖定對象。
synchronized(last) {
int[] arr1 = a1.array();
int[] arr2 = a2.array();
for (int i=0; i value += arr1[i] + arr2[i];
}
}
}
return value;
}
public void run() {
//...
}
}
對於4,若是線程進入,則線程在該類中全部操做不能進行,包括靜態變量和靜態方法,實際上,對於含有靜態方法和靜態變量的代碼塊的同步,咱們一般用4來加鎖。
以上4種之間的關係:
鎖是和對象相關聯的,每一個對象有一把鎖,爲了執行synchronized語句,線程必須可以得到synchronized語句中表達式指定的對象的鎖,一個對象只有一把鎖,被一個線程得到以後它就再也不擁有這把鎖,線程在執行完synchronized語句後,將得到鎖交還給對象。
在方法前面加上synchronized修飾符便可以將一個方法聲明爲同步化方法。同步化方法在執行以前得到一個鎖。若是這是一個類方法,那麼得到的鎖是和聲明方法的類相關的Class類對象的鎖。若是這是一個實例方法,那麼此鎖是this對象的鎖。synchronzied塊後面跟類的具體詳細例子:
public class DB2_JDBCFactory {
private static DB2_JDBCFactory instance = null;
public static final ThreadLocal threadLocal = new ThreadLocal();
private DB2_JDBCFactory() {
}
public static DB2_JDBCFactory getInstance() {
if(instance == null) {
synchronized(DB2_JDBCFactory.class) { //synchronized後面跟一個類
instance = new DB2_JDBCFactory();
}
}
return instance;
}
public Connection getConnection_JNDI_localhost(){
Connection c = (Connection) threadLocal.get();
try {
if (c == null || c.isClosed()) {
InitialContext ctx = new InitialContext();
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/localhost");
c = ds.getConnection();
threadLocal.set(c);
}
} catch (Exception ex) {
System.err.println("getConnection_JNDI Initial failed. " + ex);
return null;
}
return c;
}
} 外面的對象訪問這個類的 須要經過調用它的getInstance()
相關文章
相關標籤/搜索