Ⅰ java中的synchronized()的具體作用
synchronized()
作用是:防止多個線程同一時間調用此代碼塊或者方法.
Ⅱ Java多線程初學者指南(10):使用Synchronized關鍵字同步類方法
要想解決 臟數據 的問題 最簡單的方法就是使用synchronized關鍵字來使run方法同步 代碼如下
publicsynchronizedvoidrun(){}
從上面的代碼可以看出 只要在void和public之間加上synchronized關鍵字 就可以使run方法同步 也就是說 對於同一個Java類的對象實例 run方法同時只能被一個線程調用 並當前的run執行完後 才能被其他的線程調用 即使當前線程執行到了run方法中的yield方法 也只是暫停了一下 由於其他線程無法執行run方法 因此 最終還是會由當前的線程來繼續執行 先看看下面的代碼
sychronized關鍵字只和一個對象實例綁定
classTest{publicsynchronizedvoidmethod(){}納察}{privateTesttest;publicvoidrun() { thod(); } publicSync(Testtest) { this test=test; } publicstaticvoidmain(String[]args)throwsException { Testtest =newTest(); Testtest =newTest(); Syncsync =newSync(test ); Syncsync =newSync(test ); newThread(sync ) start(); newThread(sync ) start(); } }
在Test類中的method方法是同步的 但上面的代碼建立了兩個Test類的實例 因此 test 和test 的method方法是分別執行的 要想讓method同步 必須在建立仿茄升Sync類的實例時向它的構造方法中傳入同一個Test類的實例 如下面的代碼所示
Syncsync =newSync(test );
不僅可以使用synchronized來同步非靜態方法 也可以使用synchronized來同步靜態方法 如可以按如下方式來定義method方法
classTest{ (){}}
建立Test類的對象實例如下
Testtest=newTest();
對於靜態方法來說 只要加上了synchronized關鍵字 這個方法就是同步的 無論是使用thod() 還是使用thod()來調用method方法 method都是同步的 並不存在非靜態方法的多個實例的問題
在 種設計模式中的單件(Singleton)模式如果按傳統的方法設計 也是線程不安全的 下面的代碼是一個線程不安全的單件模式
packagetest;//線程安全的Singleton模式classSingleton{privatestaticSingletonsample;privateSingleton(){備老}(){if(sample==null){Thread yield();//為了放大Singleton模式的線程不安全性sample=newSingleton();}returnsample;}}{publicvoidrun(){Singletonsingleton=Singleton getInstance();System out println(singleton hashCode());}publicstaticvoidmain(String[]args){Threadthreads[]=newThread[ ];for(inti= ;i<threads length;i++)threads[i]=newMyThread();for(inti= ;i<threads length;i++)threads[i] start();}}
在上面的代碼調用yield方法是為了使單件模式的線程不安全性表現出來 如果將這行去掉 上面的實現仍然是線程不安全的 只是出現的可能性小得多
程序的運行結果如下
上面的運行結果可能在不同的運行環境上有所有同 但一般這五行輸出不會完全相同 從這個輸出結果可以看出 通過getInstance方法得到的對象實例是五個 而不是我們期望的一個 這是因為當一個線程執行了Thread yield()後 就將CPU資源交給了另外一個線程 由於在線程之間切換時並未執行到創建Singleton對象實例的語句 因此 這幾個線程都通過了if判斷 所以 就會產生了建立五個對象實例的情況(可能創建的是四個或三個對象實例 這取決於有多少個線程在創建Singleton對象之前通過了if判斷 每次運行時可能結果會不一樣)
要想使上面的單件模式變成線程安全的 只要為getInstance加上synchronized關鍵字即可 代碼如下
(){}
當然 還有更簡單的方法 就是在定義Singleton變數時就建立Singleton對象 代碼如下
=newSingleton();
然後在getInstance方法中直接將sample返回即可 這種方式雖然簡單 但不知在getInstance方法中創建Singleton對象靈活 讀者可以根據具體的需求選擇使用不同的方法來實現單件模式
在使用synchronized關鍵字時有以下四點需要注意
synchronized關鍵字不能繼承
雖然可以使用synchronized來定義方法 但synchronized並不屬於方法定義的一部分 因此 synchronized關鍵字不能被繼承 如果在父類中的某個方法使用了synchronized關鍵字 而在子類中覆蓋了這個方法 在子類中的這個方法默認情況下並不是同步的 而必須顯式地在子類的這個方法中加上synchronized關鍵字才可以 當然 還可以在子類方法中調用父類中相應的方法 這樣雖然子類中的方法不是同步的 但子類調用了父類的同步方法 因此 子類的方法也就相當於同步了 這兩種方式的例子代碼如下
在子類方法中加上synchronized關鍵字
classParent{ publicsynchronizedvoidmethod(){}}classChildextendsParent{ publicsynchronizedvoidmethod(){}}
在子類方法中調用父類的同步方法
classParent{ publicsynchronizedvoidmethod(){}}classChildextendsParent{publicvoidmethod(){thod();}}
在定義介面方法時不能使用synchronized關鍵字
構造方法不能使用synchronized關鍵字 但可以使用下節要討論的synchronized塊來進行同步
synchronized可以自由放置
在前面的例子中使用都是將synchronized關鍵字放在方法的返回類型前面 但這並不是synchronized可放置唯一位置 在非靜態方法中 synchronized還可以放在方法定義的最前面 在靜態方法中 synchronized可以放在static的前面 代碼如下
publicsynchronizedvoidmethod();synchronizedpublicvoidmethod();();();();
但要注意 synchronized不能放在方法返回類型的後面 如下面的代碼是錯誤的
publicvoidsynchronizedmethod();();
synchronized關鍵字只能用來同步方法 不能用來同步類變數 如下面的代碼也是錯誤的
publicsynchronizedintn= ;publicstaticsynchronizedintn= ;
雖然使用synchronized關鍵字同步方法是最安全的同步方式 但大量使用synchronized關鍵字會造成不必要的資源消耗以及性能損失 雖然從表面上看synchronized鎖定的是一個方法 但實際上synchronized鎖定的是一個類 也就是說 如果在非靜態方法method 和method 定義時都使用了synchronized 在method 未執行完之前 method 是不能執行的 靜態方法和非靜態方法的情況類似 但靜態和非靜態方法不會互相影響 看看如下的代碼
packagetest;publicclassMyThread extendsThread{publicStringmethodName;publicstaticvoidmethod(Strings){System out println(s);while(true);}publicsynchronizedvoidmethod (){method( 非靜態的method 方法 );}publicsynchronizedvoidmethod (){method( 非靜態的method 方法 );} (){method( 靜態的method 方法 );} (){method( 靜態的method 方法 );}publicvoidrun(){try{getClass() getMethod(methodName) invoke(this);}catch(Exceptione){}}publicstaticvoidmain(String[]args)throwsException{MyThread myThread =newMyThread ();for(inti= ;i<= ;i++){thodName= method +String valueOf(i);newThread(myThread ) start();sleep( );}}}
運行結果如下
非靜態的method 方法靜態的method 方法
lishixin/Article/program/Java/gj/201311/27526
Ⅲ java方法synchronized在哪個類裡面
synchronized是java中表示同步代碼快的關鍵字。類似於final一樣的關鍵字,不屬於那個類。
synchronized可以放在方法修飾符前,比如private synchronized void test(){},也可以含頃放在方法內部,修飾某一段特定的代碼。可能你貼出完整的代碼會清晰一點乎拆,synchronized有一個地方需要注意,就是在給普通方法加鎖與給靜態方法加鎖機制是不一談頃陸樣的。
synchronized在靜態方法上表示調用前要獲得類的鎖,而在非靜態方法上表示調用此方法前要獲得對象的鎖。
Ⅳ Java中線程同步的synchronized()(同步方法塊)這個括弧里的參數是啥
synchronized()、synchronized(this)、synchronized(類名.class)
synchronized加在非靜態方法前和synchronized(this)都是鎖住了這個類的對象,如果多線程訪問,對象不同,就鎖不住,對象固定是一個,就可鎖住。
synchronized(類名.class)和加在靜態方法前,是鎖住了代碼塊,不管多線程訪問的時候對象是不是同一個,能縮小代碼段的范圍就盡量縮小,能在代碼段上加同步就不要再整個方法上加同步,縮小鎖的粒度。
Ⅳ synchronized 加在java方法前面是什麼作用
java的synchronized加在方法上或者對象上區別如下:
1.synchronized
在方法上,所有這個類的加了
synchronized
的方法,在執行時,會獲得一個該類的唯一的同步鎖,當這個鎖被佔用時,其他的加了
synchronized
的方法就必須等待
2.加在對象上的話,就是以這個對象為鎖,其他也以這個對象為鎖的代碼段,在這個鎖被佔用時,就必須等待
Ⅵ synchronized 加在java方法前面是什麼作用
Java語言的關鍵字,當它用來修飾一個方法或者一個手蔽螞代碼塊的時候,能夠保證在同一時刻最多隻有一個線程執行該段代碼。
一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當畢埋一個線程訪問object的一個並圓synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規則對其它對象鎖同樣適用.
舉例說明:
一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
package ths;
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
結果:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
package ths;
public class Thread2 {
public void m4t1() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
public void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread( new Runnable() { public void run() { myt2.m4t1(); } }, "t1" );
Thread t2 = new Thread( new Runnable() { public void run() { myt2.m4t2(); } }, "t2" );
t1.start();
t2.start();
}
}
結果:
t1 : 4
t2 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
//修改Thread2.m4t2()方法:
public void m4t2() {
synchronized(this) {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
}
結果:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
//修改Thread2.m4t2()方法如下:
public synchronized void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
}
}
}
結果:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
五、以上規則對其它對象鎖同樣適用:
package ths;
public class Thread3 {
class Inner {
private void m4t1() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t1()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
private void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
}
private void m4t1(Inner inner) {
synchronized(inner) { //使用對象鎖
inner.m4t1();
}
private void m4t2(Inner inner) {
inner.m4t2();
}
public static void main(String[] args) {
final Thread3 myt3 = new Thread3();
final Inner inner = myt3.new Inner();
Thread t1 = new Thread( new Runnable() {public void run() { myt3.m4t1(inner);} }, "t1");
Thread t2 = new Thread( new Runnable() {public void run() { myt3.m4t2(inner);} }, "t2");
t1.start();
t2.start();
}
}
結果:
盡管線程t1獲得了對Inner的對象鎖,但由於線程t2訪問的是同一個Inner中的非同步部分。所以兩個線程互不幹擾。
t1 : Inner.m4t1()=4
t2 : Inner.m4t2()=4
t1 : Inner.m4t1()=3
t2 : Inner.m4t2()=3
t1 : Inner.m4t1()=2
t2 : Inner.m4t2()=2
t1 : Inner.m4t1()=1
t2 : Inner.m4t2()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=0
現在在Inner.m4t2()前面加上synchronized:
private synchronized void m4t2() {
int i = 5;
while(i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : Inner.m4t2()=" + i);
try {
Thread.sleep(500);
} catch(InterruptedException ie) {
}
}
}
結果:
盡管線程t1與t2訪問了同一個Inner對象中兩個毫不相關的部分,但因為t1先獲得了對Inner的對象鎖,所以t2對Inner.m4t2()的訪問也被阻塞,因為m4t2()是Inner中的一個同步方法。
t1 : Inner.m4t1()=4
t1 : Inner.m4t1()=3
t1 : Inner.m4t1()=2
t1 : Inner.m4t1()=1
t1 : Inner.m4t1()=0
t2 : Inner.m4t2()=4
t2 : Inner.m4t2()=3
t2 : Inner.m4t2()=2
t2 : Inner.m4t2()=1
t2 : Inner.m4t2()=0
第二篇:
synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。
1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:
public synchronized void accessVal(int newVal);
synchronized 方法控制對類成員變數的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能
執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的線程方能獲得該鎖,重新進入可執行
狀態。這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多隻有一個處於可執行狀態(因為至多隻有
一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變數的訪問沖突(只要所有可能訪問類成員變數的方法均被聲明為 synchronized)
。
在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成
員變數的訪問。
synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run() 聲明為
synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可
以通過將訪問類成員變數的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供
了更好的解決辦法,那就是 synchronized 塊。
2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:
synchronized(syncObject) {
//允許訪問控制的代碼
}
synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機
制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。
對synchronized(this)的一些理解
一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另一個線
程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。
二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized
(this)同步代碼塊。
三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)
同步代碼塊的訪問將被阻塞。
四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個
object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。
五、以上規則對其它對象鎖同樣適用
http://hi..com/sunshibing/blog/item/5235b9b731d48ff430add14a.html
java中synchronized用法
打個比方:一個object就像一個大房子,大門永遠打開。房子里有 很多房間(也就是方法)。
這些房間有上鎖的(synchronized方法), 和不上鎖之分(普通方法)。房門口放著一把鑰匙(key),這把鑰匙可以打開所有上鎖的房間。
另外我把所有想調用該對象方法的線程比喻成想進入這房子某個 房間的人。所有的東西就這么多了,下面我們看看這些東西之間如何作用的。
在此我們先來明確一下我們的前提條件。該對象至少有一個synchronized方法,否則這個key還有啥意義。當然也就不會有我們的這個主題了。
一個人想進入某間上了鎖的房間,他來到房子門口,看見鑰匙在那兒(說明暫時還沒有其他人要使用上鎖的 房間)。於是他走上去拿到了鑰匙
,並且按照自己 的計劃使用那些房間。注意一點,他每次使用完一次上鎖的房間後會馬上把鑰匙還回去。即使他要連續使用兩間上鎖的房間,
中間他也要把鑰匙還回去,再取回來。
因此,普通情況下鑰匙的使用原則是:「隨用隨借,用完即還。」
這時其他人可以不受限制的使用那些不上鎖的房間,一個人用一間可以,兩個人用一間也可以,沒限制。但是如果當某個人想要進入上鎖的房
間,他就要跑到大門口去看看了。有鑰匙當然拿了就走,沒有的話,就只能等了。
要是很多人在等這把鑰匙,等鑰匙還回來以後,誰會優先得到鑰匙?Not guaranteed。象前面例子里那個想連續使用兩個上鎖房間的傢伙,他
中間還鑰匙的時候如果還有其他人在等鑰匙,那麼沒有任何保證這傢伙能再次拿到。 (JAVA規范在很多地方都明確說明不保證,象
Thread.sleep()休息後多久會返回運行,相同優先權的線程那個首先被執行,當要訪問對象的鎖被 釋放後處於等待池的多個線程哪個會優先得
到,等等。我想最終的決定權是在JVM,之所以不保證,就是因為JVM在做出上述決定的時候,絕不是簡簡單單根據 一個條件來做出判斷,而是
根據很多條。而由於判斷條件太多,如果說出來可能會影響JAVA的推廣,也可能是因為知識產權保護的原因吧。SUN給了個不保證 就混過去了
。無可厚非。但我相信這些不確定,並非完全不確定。因為計算機這東西本身就是按指令運行的。即使看起來很隨機的現象,其實都是有規律
可尋。學過 計算機的都知道,計算機里隨機數的學名是偽隨機數,是人運用一定的方法寫出來的,看上去隨機罷了。另外,或許是因為要想弄
的確定太費事,也沒多大意義,所 以不確定就不確定了吧。)
再來看看同步代碼塊。和同步方法有小小的不同。
1.從尺寸上講,同步代碼塊比同步方法小。你可以把同步代碼塊看成是沒上鎖房間里的一塊用帶鎖的屏風隔開的空間。
2.同步代碼塊還可以人為的指定獲得某個其它對象的key。就像是指定用哪一把鑰匙才能開這個屏風的鎖,你可以用本房的鑰匙;你也可以指定
用另一個房子的鑰匙才能開,這樣的話,你要跑到另一棟房子那兒把那個鑰匙拿來,並用那個房子的鑰匙來打開這個房子的帶鎖的屏風。
記住你獲得的那另一棟房子的鑰匙,並不影響其他人進入那棟房子沒有鎖的房間。
為什麼要使用同步代碼塊呢?我想應該是這樣的:首先對程序來講同步的部分很影響運行效率,而一個方法通常是先創建一些局部變
量,再對這些變數做一些 操作,如運算,顯示等等;而同步所覆蓋的代碼越多,對效率的影響就越嚴重。因此我們通常盡量縮小其影響范圍。
如何做?同步代碼塊。我們只把一個方法中該同 步的地方同步,比如運算。
另外,同步代碼塊可以指定鑰匙這一特點有個額外的好處,是可以在一定時期內霸佔某個對象的key。還記得前面說過普通情況下鑰
匙的使用原則嗎。現在不是普通情況了。你所取得的那把鑰匙不是永遠不還,而是在退出同步代碼塊時才還。
還用前面那個想連續用兩個上鎖房間的傢伙打比方。怎樣才能在用完一間以後,繼續使用另一間呢。用同步代碼塊吧。先創建另外
一個線程,做一個同步代碼 塊,把那個代碼塊的鎖指向這個房子的鑰匙。然後啟動那個線程。只要你能在進入那個代碼塊時抓到這房子的鑰匙
,你就可以一直保留到退出那個代碼塊。也就是說 你甚至可以對本房內所有上鎖的房間遍歷,甚至再sleep(10*60*1000),而房門口卻還有
1000個線程在等這把鑰匙呢。很過癮吧。
在此對sleep()方法和鑰匙的關聯性講一下。一個線程在拿到key後,且沒有完成同步的內容時,如果被強制sleep()了,那key還一
直在 它那兒。直到它再次運行,做完所有同步內容,才會歸還key。記住,那傢伙只是幹活干累了,去休息一下,他並沒幹完他要乾的事。為
了避免別人進入那個房間 把裡面搞的一團糟,即使在睡覺的時候他也要把那唯一的鑰匙戴在身上。
最後,也許有人會問,為什麼要一把鑰匙通開,而不是一個鑰匙一個門呢?我想這純粹是因為復雜性問題。一個鑰匙一個門當然更
安全,但是會牽扯好多問題。鑰匙 的產生,保管,獲得,歸還等等。其復雜性有可能隨同步方法的增加呈幾何級數增加,嚴重影響效率。這也
算是一個權衡的問題吧。為了增加一點點安全性,導致效 率大大降低,是多麼不可取啊。
synchronized的一個簡單例子
public class TextThread {
public static void main(String[] args) {
TxtThread tt = new TxtThread();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
new Thread(tt).start();
}
}
class TxtThread implements Runnable {
int num = 100;
String str = new String();
public void run() {
synchronized (str) {
while (num > 0) {
try {
Thread.sleep(1);
} catch (Exception e) {
e.getMessage();
}
System.out.println(Thread.currentThread().getName()
+ "this is " + num--);
}
}
}
}
上面的例子中為了製造一個時間差,也就是出錯的機會,使用了Thread.sleep(10)
Java對多線程的支持與同步機制深受大家的喜愛,似乎看起來使用了synchronized關鍵字就可以輕松地解決多線程共享數據同步問題。到底如
何?――還得對synchronized關鍵字的作用進行深入了解才可定論。
總的說來,synchronized關鍵字可以作為函數的修飾符,也可作為函數內的語句,也就是平時說的同步方法和同步語句塊。如果再細的分類,
synchronized可作用於instance變數、object reference(對象引用)、static函數和class literals(類名稱字面常量)身上。
在進一步闡述之前,我們需要明確幾點:
A.無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其
他線程的對象訪問。
B.每個對象只有一個鎖(lock)與之相關聯。
C.實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。
接著來討論synchronized用到不同地方對代碼產生的影響:
假設P1、P2是同一個類的不同對象,這個類中定義了以下幾種情況的同步塊或同步方法,P1、P2就都可以調用它們。
1. 把synchronized當作函數修飾符時,示例代碼如下:
Public synchronized void methodAAA()
{
//….
}
這也就是同步方法,那這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不同的線程中
執行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的Class所產生的另一對象P2卻可以任意調用這個被加了
synchronized關鍵字的方法。
上邊的示例代碼等同於如下代碼:
public void methodAAA()
{
synchronized (this) // (1)
{
//…..
}
}
(1)處的this指的是什麼呢?它指的就是調用這個方法的對象,如P1。可見同步方法實質是將synchronized作用於object reference。――那個
拿到了P1對象鎖的線程,才可以調用P1的同步方法,而對P2而言,P1這個鎖與它毫不相干,程序也可能在這種情形下擺脫同步機制的控制,造
成數據混亂:(
2.同步塊,示例代碼如下:
public void method3(SomeObject so)
{
synchronized(so)
{
//…..
}
}
這時,鎖就是so這個對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明
確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的instance變數(它得是一個對象)來充當鎖:
class Foo implements Runnable
{
private byte[] lock = new byte[0]; // 特殊的instance變數
Public void methodA()
{
synchronized(lock) { //… }
}
//…..
}
註:零長度的byte數組對象創建起來將比任何對象都經濟――查看編譯後的位元組碼:生成零長度的byte[]對象只需3條操作碼,而Object lock
= new Object()則需要7行操作碼。
3.將synchronized作用於static 函數,示例代碼如下:
Class Foo
{
public synchronized static void methodAAA() // 同步的static 函數
{
//….
}
public void methodBBB()
{
synchronized(Foo.class) // class literal(類名稱字面常量)
}
}
代碼中的methodBBB()方法是把class literal作為鎖的情況,它和同步的static函數產生的效果是一樣的,取得的鎖很特別,是當前調用這
個方法的對象所屬的類(Class,而不再是由這個Class產生的某個具體對象了)。
記得在《Effective Java》一書中看到過將 Foo.class和 P1.getClass()用於作同步鎖還不一樣,不能用P1.getClass()來達到鎖這個Class的
目的。P1指的是由Foo類產生的對象。
可以推斷:如果一個類中定義了一個synchronized的static函數A,也定義了一個synchronized 的instance函數B,那麼這個類的同一對象Obj
在多線程中分別訪問A和B兩個方法時,不會構成同步,因為它們的鎖都不一樣。A方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class。
小結如下:
搞清楚synchronized鎖定的是哪個對象,就能幫助我們設計更安全的多線程程序。
還有一些技巧可以讓我們對共享資源的同步訪問更加安全:
1. 定義private 的instance變數+它的 get方法,而不要定義public/protected的instance變數。如果將變數定義為public,對象在外界可以
繞過同步方法的控制而直接取得它,並改動它。這也是JavaBean的標准實現方式之一。
2. 如果instance變數是一個對象,如數組或ArrayList什麼的,那上述方法仍然不安全,因為當外界對象通過get方法拿到這個instance對象
的引用後,又將其指向另一個對象,那麼這個private變數也就變了,豈不是很危險。 這個時候就需要將get方法也加上synchronized同步,並
且,只返回這個private對象的clone()――這樣,調用端得到的就是對象副本的引用了
Ⅶ Java怎麼使用synchronized聲明一個變數
首先要說明的是,java里不能直接使用synchronized聲明一個變數,而是使用synchronized去修飾一個代碼塊或一個方法。x0dx0ax0dx0a詳細說明如下:x0dx0ax0dx0a synchronized用來修飾一個方法或者一個代碼塊,它用來保證在同一時刻最多隻有一個線程執行該段代碼。x0dx0a 一、當兩個並發線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執行。另簡圓一個線程必須等待當前線程執行完這個代碼塊以後才能執行該代碼塊。x0dx0a 二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。x0dx0a 三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪明悉問將被阻塞。x0dx0a 四、第三激咐乎個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。x0dx0a 五、以上規則對其它對象鎖同樣適用。x0dx0ax0dx0a示例代碼:x0dx0apublic class Thread1 implements Runnable { x0dx0a public void run() { x0dx0a synchronized(this) { x0dx0a for (int i = 0; i < 5; i++) { x0dx0a System.out.println(Thread.currentThread().getName() + " synchronized loop " + i); x0dx0a } x0dx0a } x0dx0a } x0dx0a public static void main(String[] args) { x0dx0a Thread1 t1 = new Thread1(); x0dx0a Thread ta = new Thread(t1, "A"); x0dx0a Thread tb = new Thread(t1, "B"); x0dx0a ta.start(); x0dx0a tb.start(); x0dx0a }x0dx0a}x0dx0a結果: x0dx0a A synchronized loop 0 x0dx0a A synchronized loop 1 x0dx0a A synchronized loop 2 x0dx0a A synchronized loop 3 x0dx0a A synchronized loop 4 x0dx0a B synchronized loop 0 x0dx0a B synchronized loop 1 x0dx0a B synchronized loop 2 x0dx0a B synchronized loop 3 x0dx0a B synchronized loop 4
Ⅷ java多線程中synchronized關鍵字的用法
由於同一進程內的多個線程共享內存空間 在Java中 就是共享實例 當多個線程試圖同時修改某個實例的內容時 就會造成沖突 因此 線程必須實現共享互斥 使多線程同步
最簡單的同步是將一個方法標記為synchronized 對同一個實例來說 任一時刻只能有一個synchronized方法在執行 當一個方法正在執行某個synchronized方法時 其他線程如果想要執行這個實例的任意一個synchronized方法 都必須等待當前執行 synchronized方法的線程退出此方法後 才能依次執行
但是 非synchronized方法不受影響 不管當前有沒有執行synchronized方法 非synchronized方法都可以被多個線程同時執行
此外 必須注意 只有同一實例的synchronized方法同一時間只能被一個線程執行 不同實例的synchronized方法是可以並發的 例如 class A定義了synchronized方法sync() 則不同實例a sync()和a sync()可以同時由兩個線程來執行
多線程同步的實現最終依賴鎖機制 我們可以想像某一共享資源是一間屋子 每個人都是一個線程 當A希望進入房間時 他必須獲得門鎖 一旦A獲得門鎖 他進去後就立刻將門鎖上 於是B C D就不得不在門外等待 直到A釋放鎖出來後 B C D中的某一人搶到了該鎖(具體搶法依賴於 JVM的實現 可以先到先得 也可以隨機挑選) 然後進屋又將門鎖上 這樣 任一時刻最多有一人在桐羨屋內(使用共享資源)
Java語言規范內置了對多線程的支持 對於Java程序來說 每一個對象實例都有一把 鎖 一旦某個線程獲得了該鎖 別的線程如果希望獲得該鎖 只能等待這個線程釋放鎖之後 獲得鎖的方法只有一個 就是synchronized關鍵字 例如
public class SharedResource {
private int count = ;
public int getCount() { return count; }
public synchronized void setCount(int count) { unt = count; }
}
同步方法public synchronized void setCount(int count) { unt = count; } 事實上相當於
public void setCount(int count) {
synchronized(this) { // 在此獲得this鎖
unt = count;
} // 在此釋放this鎖
}
紅色部分表示需要同步的代碼段 該區域為 危險區域 如果兩個以上局滲拍的線程同時執行 會引發沖突 因此 要更改SharedResource的內部狀態 必須先獲得SharedResource實例的鎖
退出synchronized塊時 線程擁有的鎖自動釋放 於是 別的線程又可以獲取該鎖了
為了提高性能 不一定要鎖定this 例如 SharedResource有兩個獨立變化的變數
public class SharedResouce {
private int a = ;
private int b = ;
public synchronized void setA(int a) { this a = a; }
public synchronized void setB(int b) { this b = b; }
}
若同步整個方法 則setA()的時候無法setB() setB()時喊飢無法setA() 為了提高性能 可以使用不同對象的鎖
public class SharedResouce {
private int a = ;
private int b = ;
private Object sync_a = new Object()
private Object sync_b = new Object()
public void setA(int a) {
synchronized(sync_a) {
this a = a;
}
}
public synchronized void setB(int b) {
synchronized(sync_b) {
this b = b;
}
lishixin/Article/program/Java/gj/201311/27512
Ⅸ JAVA多線程中的synchronized(obj)到底怎麼用
你可以直接用synchronized來修飾指悶一個方法,如:public void synchronized printXX(){
........
}
這樣,只蠢逗蠢在一個線和進個方法,那麼此 「類」帶陪(方法所在的類) 就會被"上鎖",其它線程不能訪問。直到這個線程釋放資源為止。
還可以將要實現同步的代碼寫到一個synchronized塊裡面,如:synchronized(obj){
......
}
注意,此處的obj是一個對象,你可以Object obj = new Object(); 也可以用this來代替,表示當前對象!
Ⅹ java synchronized 有兩種用法一種是方法上加synchronized ,還有一種是什麼
synchronized
語句塊,可以方便的寫在方法內的某段。用法:synchronized
(對象地址){
邏輯
}
synchronized
的意思就是
先獲得對象地址的鎖,那麼就可以先執行那部分代碼。執行完就放開鎖其他線程就可以進來了。
synchronized
void
getName()這個代碼表示
以this類實例為對象地址相當於synchronized
(this){}.
那麼在多線程環境中。如果訪問同一個類同一個實例的這個方法,就會被同步