对象及变量的并发访问一
生活随笔
收集整理的這篇文章主要介紹了
对象及变量的并发访问一
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
一、多個(gè)線程操作一個(gè)對(duì)象實(shí)例 ?當(dāng)兩個(gè)線程同時(shí)訪問一個(gè)沒有同步的方法,如果兩個(gè)線程同時(shí)操作業(yè)務(wù)對(duì)象中的實(shí)例變量,則有可能會(huì)出現(xiàn)“非線程安全問題”。 1 package concurrent;
2 /**
3 * 測(cè)試不同線程操作同一個(gè)實(shí)例變量線程安全問題
4 * @author foolishbird_lmy
5 *
6 */
7 class ThePrivateNumber{
8 private int num = 0;
9
10 public synchronized void addI(String username){
11 try {
12 if (username.equals("a" )){
13 num = 100;
14 System. out .println("a set over!");
15 Thread. sleep(2000);
16 } else {
17 num = 200;
18 System. out .println("b set over!");
19 }
20 System. out .println(Thread.currentThread().getName()+ " num is " +num );
21 } catch (InterruptedException e){
22 e.printStackTrace();
23 }
24 }
25 }
26
27 class ThePrivateNumA extends Thread{
28 private ThePrivateNumber tNumber;
29 public ThePrivateNumA(ThePrivateNumber tNumber){
30 this .tNumber = tNumber;
31 }
32 public void run(){
33 tNumber.addI( "a" );
34 }
35 }
36 class ThePrivateNumB extends Thread{
37 private ThePrivateNumber tNumber;
38 public ThePrivateNumB(ThePrivateNumber tNumber){
39 this .tNumber = tNumber;
40 }
41 public void run(){
42 tNumber.addI( "b" );
43 }
44 }
45 public class ThePrivateNum {
46 public static void main(String[] args) {
47 ThePrivateNumber tNumber = new ThePrivateNumber();
48 ThePrivateNumA tNumA = new ThePrivateNumA(tNumber);
49 tNumA.setName( "A" );
50 tNumA.start();
51 ThePrivateNumB tNumB = new ThePrivateNumB(tNumber);
52 tNumB.setName( "B" );
53 tNumB.start();
54 }
55 }
上面演示的就是兩個(gè)線程操作同一個(gè)對(duì)象實(shí)例,如果沒有加synchronized關(guān)鍵字執(zhí)行同步,則會(huì)出現(xiàn)線程安全問題,也就是產(chǎn)生臟讀數(shù)據(jù),即讀取到的數(shù)據(jù)是已經(jīng)被修改過的數(shù)據(jù); 分析:當(dāng)線程A進(jìn)入獲取到CPU執(zhí)行權(quán),匹配到“a”,則num=100,之后該線程被休眠2秒,此時(shí)B線程獲得CPU執(zhí)行權(quán),開始執(zhí)行到else代碼,執(zhí)行num=200,之后退出,A線程經(jīng)過短暫休眠后自動(dòng)蘇醒繼續(xù)執(zhí)行,但是此時(shí)num已經(jīng)被更改為了200,所以最后輸出num都是200,這就是線程非安全的;加了同步鎖之后,線程B必須等待線程A執(zhí)行完之后才能進(jìn)入,所以不會(huì)產(chǎn)生數(shù)據(jù)被修改問題。 二、一個(gè)對(duì)象實(shí)例中有同步方法與非同步方法 1 package concurrent; 2 3 class MySynchroized{ 4 public synchronized void methodA(){ 5 try { 6 System. out .println("the methodA is start:"+Thread. currentThread().getName()); 7 Thread. sleep(5000); 8 System. out .println("A end time "+System.currentTimeMillis ()); 9 } catch (InterruptedException e){ 10 e.printStackTrace(); 11 } 12 } 13 //synchronized,分別測(cè)試加同步鎖與不加執(zhí)行順序 14 public void methodB(){ 15 try { 16 System. out .println("the methodB is start:"+Thread. currentThread().getName()); 17 System. out .println("B begin time "+System.currentTimeMillis ()); 18 Thread. sleep(5000); 19 } catch (InterruptedException e){ 20 e.printStackTrace(); 21 } 22 } 23 } 24 class TestA extends Thread{ 25 private MySynchroized ms; 26 public TestA(MySynchroized ms){ 27 this .ms = ms; 28 } 29 public void run(){ 30 ms.methodA(); //調(diào)用同步方法 31 } 32 } 33 class TestB extends Thread{ 34 private MySynchroized ms; 35 public TestB(MySynchroized ms){ 36 this .ms = ms; 37 } 38 public void run(){ 39 ms.methodB(); //調(diào)用非同步方法 40 } 41 } 42 public class TestSynchroized { 43 public static void main(String[] args) { 44 MySynchroized ms = new MySynchroized(); 45 TestA a = new TestA(ms); 46 a.setName( "A" ); 47 TestB b = new TestB(ms); 48 b.setName( "B" ); 49 a.start(); 50 b.start(); 51 } 52 }
分析:在共享的對(duì)象實(shí)例類中有兩個(gè)方法,我們分別設(shè)置為同步與非同步, (1)左邊是B方法非同步測(cè)試效果,我們可以看到,當(dāng)執(zhí)行A線程時(shí),A拿到該對(duì)象的實(shí)例鎖,但是并沒有影響線程B執(zhí)行非同步的方法,說明A、B線程幾乎是同時(shí)進(jìn)行,B線程并沒有因?yàn)锳拿到鎖而發(fā)生等待現(xiàn)象,屬于異步執(zhí)行; (2)右邊是將B方法也執(zhí)行同步,可以看到A、B線程同步執(zhí)行即順序執(zhí)行,當(dāng)A先進(jìn)入拿到對(duì)象鎖,B此時(shí)就會(huì)是同步等待狀態(tài),只有當(dāng)A執(zhí)行休眠完成,釋放鎖之后B才有機(jī)會(huì)執(zhí)行,屬于同步執(zhí)行操作。 結(jié)論:對(duì)于執(zhí)行相同對(duì)象的不同線程,執(zhí)行對(duì)象類中的同步方法時(shí),不管有多少個(gè)同步的方法,都是同一個(gè)對(duì)象鎖,必須等一個(gè)線程執(zhí)行完畢釋放鎖之后另一個(gè)線程才能獲取執(zhí)行;但是非同步方法可以任意時(shí)刻調(diào)用,不收鎖限制。 三、關(guān)于臟讀問題 1 package concurrent; 2 class DirtyRead { 3 private String name = "a" ; 4 private String id = "aa" ; 5 public synchronized void set(String name, String id) { 6 try { 7 this .name = name; 8 Thread. sleep(2000); 9 this .id = id; 10 System. out .println("set method " + Thread.currentThread().getName() 11 + " name:" + name + " id:" + id); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 public void get() { 17 System. out .println("get method " + Thread.currentThread().getName() 18 + " name:" + name + " id:" + id ); 19 } 20 } 21 class DirtyReadRun extends Thread { 22 private DirtyRead dr; 23 public DirtyReadRun(DirtyRead dr) { 24 this .dr = dr; 25 } 26 public void run() { 27 dr.set( "b" , "bb" ); 28 } 29 } 30 public class TestDirtyRead { 31 public static void main(String[] args) { 32 try { 33 DirtyRead dr = new DirtyRead(); 34 DirtyReadRun drr = new DirtyReadRun(dr); 35 drr.start(); 36 // Thread.sleep(1000); 37 Thread. sleep(3000); 38 dr.get(); 39 } catch (InterruptedException e) { 40 e.printStackTrace(); 41 } 42 } 43 }
分析: (1)左邊結(jié)果是在主線程中休眠1秒,可以發(fā)現(xiàn)讀取數(shù)據(jù)發(fā)生了錯(cuò)誤,原因是我們將set方法設(shè)置為同步,所以線程在執(zhí)行時(shí)拿到鎖后會(huì)安全執(zhí)行,數(shù)據(jù)的設(shè)置沒有問題,但是當(dāng)我們調(diào)用非同步方法get獲取值時(shí),注意主線程只休眠了1秒,但是我們?cè)趕et線程中休眠了2秒,所以此時(shí)線程并沒有來得及給id賦值操作,就直接輸出了aa而不是bb; (2)右邊是主線程休眠3秒的情況,即調(diào)用get方法的線程是在線程賦值set休眠2秒之后才調(diào)用,此時(shí)已經(jīng)安全給數(shù)據(jù)賦值了,所以輸出結(jié)果正確; 當(dāng)然我們也可以直接給get方法執(zhí)行同步操作,這有另一個(gè)線程就必須等待第一個(gè)線程執(zhí)行完set里面的全部操作釋放鎖之后才能執(zhí)行,會(huì)發(fā)生等待。 四、鎖的可重入 關(guān)鍵字synchronized擁有可重入的功能,即當(dāng)一個(gè)線程得到一個(gè)對(duì)象鎖之后,再次請(qǐng)求此對(duì)象鎖時(shí)是可以再次得到該對(duì)象的鎖的,自己可以再次獲取自己的內(nèi)部鎖。 1 package concurrent; 2 /** 3 * 可重入鎖測(cè)試 4 * @author foolishbird_lmy 5 * 6 */ 7 class Synch{ 8 public synchronized void sA(){ 9 System. out .println("sA()" ); 10 sB(); 11 } 12 public synchronized void sB(){ 13 System. out .println("sB()" ); 14 sC(); 15 } 16 public synchronized void sC(){ 17 System. out .println("sC()" ); 18 } 19 } 20 class SynchARun extends Thread{ 21 private Synch sa; 22 public SynchARun(Synch sa){ 23 this .sa = sa; 24 } 25 public void run(){ 26 sa.sA(); 27 } 28 } 29 public class ReSynchronized { 30 public static void main(String[] args) { 31 SynchARun sa = new SynchARun( new Synch()); 32 sa.start(); 33 } 34 }
? 從輸出結(jié)果可以看出,線程只要獲取到了該對(duì)象鎖,其他的同步鎖也一樣能獲取。
?
五、靜態(tài)同步synchronized方法與synchronized(class)代碼塊
?用static修飾的同步方法中的同步鎖是給Class類上鎖,而非static方法是給對(duì)象上鎖;
?
1 package concurrent; 2 3 class StaticSyn { 4 public static synchronized void printA() { 5 try { 6 System. out .println(Thread.currentThread().getName() + " : " 7 + System.currentTimeMillis() + "進(jìn)入printA()"); 8 Thread. sleep(3000); 9 System. out .println(Thread.currentThread().getName() + " : " 10 + System.currentTimeMillis() + "退出printA()"); 11 } catch (InterruptedException e) { 12 e.printStackTrace(); 13 } 14 } 15 public static synchronized void printB() { 16 try { 17 System. out .println(Thread.currentThread().getName() + " : " 18 + System.currentTimeMillis() + "進(jìn)入printB()"); 19 Thread. sleep(3000); 20 System. out .println(Thread.currentThread().getName() + " : " 21 + System.currentTimeMillis() + "退出printB()"); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 } 26 public synchronized void printC() { 27 try { 28 System. out .println(Thread.currentThread().getName() + " : " 29 + System.currentTimeMillis() + "進(jìn)入printC()"); 30 Thread. sleep(3000); 31 System. out .println(Thread.currentThread().getName() + " : " 32 + System.currentTimeMillis() + "退出printC()"); 33 } catch (InterruptedException e) { 34 e.printStackTrace(); 35 } 36 } 37 } 38 class StaticSynRunA extends Thread{ 39 private StaticSyn ss; 40 public StaticSynRunA(StaticSyn ss){ 41 this .ss = ss; 42 } 43 @SuppressWarnings( "static-access" ) 44 public void run(){ 45 ss. printA(); 46 } 47 } 48 class StaticSynRunB extends Thread{ 49 private StaticSyn ss; 50 public StaticSynRunB(StaticSyn ss){ 51 this .ss = ss; 52 } 53 public void run(){ 54 ss .printB (); 55 } 56 } 57 class StaticSynRunC extends Thread{ 58 private StaticSyn ss; 59 public StaticSynRunC(StaticSyn ss){ 60 this .ss = ss; 61 } 62 public void run(){ 63 ss.printC(); 64 } 65 } 66 public class TestStaticSyn { 67 public static void main(String[] args) { 68 StaticSyn ss = new StaticSyn(); 69 StaticSynRunA ssa = new StaticSynRunA(ss); 70 ssa.setName( "A" ); 71 ssa.start(); 72 StaticSynRunB ssb = new StaticSynRunB(ss); 73 ssb.setName( "B" ); 74 ssb.start(); 75 StaticSynRunC ssc = new StaticSynRunC(ss); 76 ssc.setName( "C" ); 77 // ssc.start(); 78 } 79 }?
分析: (1)左邊結(jié)果是注釋掉C,即A、B線程都是調(diào)用靜態(tài)的同步方法,所以都是同步順序執(zhí)行,他們的鎖都是Class鎖,是同一種鎖,所以B線程必須等待A線程釋放鎖; (2)右邊的結(jié)果是A、B、C線程同時(shí)運(yùn)行,由于C線程調(diào)用的是非靜態(tài)的同步方法,非靜態(tài)的同步方法是對(duì)象鎖,與其他兩個(gè)線程的鎖不一樣,所以是異步的,但是A與B還是同步執(zhí)行。?
轉(zhuǎn)載于:https://www.cnblogs.com/lmy-foolishbird/p/5471853.html
總結(jié)
以上是生活随笔為你收集整理的对象及变量的并发访问一的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Steam 导入已下载好的游戏
- 下一篇: 将功补过 树形动态规划