隨筆-200  評論-148  文章-15  trackbacks-0

          synchronized的作用?
          一、同步方法
          public synchronized void methodAAA(){

          //….

          }
          鎖定的是調用這個同步方法的對象

          測試:
          a、不使用這個關鍵字修飾方法,兩個線程調用同一個對象的這個方法。
          目標類:

          1 public ? class ?TestThread? {
          2 ???? public ?? void ?execute() {?? // synchronized,未修飾
          3 ???????? for ( int ?i = 0 ;i < 100 ;i ++ ) {
          4 ????????????System.out.println(i);
          5 ????????}
          ????
          6 ????}

          7 }


          線程類:

          1 public ? class ?ThreadA? implements ?Runnable {
          2 ????TestThread?test = null ;
          3 ???? public ?ThreadA(TestThread?pTest) {?? // 對象有外部引入,這樣保證是同一個對象
          4 ????????test = pTest;
          5 ????}

          6 ???? public ? void ?run()? {
          7 ????????test.execute();
          8 ????}

          9 }

          調用:
          1TestThread?test=new?TestThread();
          2Runnable?runabble=new?ThreadA(test);
          3Thread?a=new?Thread(runabble,"A");????????????????
          4a.start();
          5Thread?b=new?Thread(runabble,"B");
          6b.start();


          結果:
          輸出的數字交錯在一起。說明不是同步的,兩個方法在不同的線程中是異步調用的。

          b、修改目標類,增加synchronized修飾

          1public?class?TestThread?{
          2????public?synchronized??void?execute(){??//synchronized修飾
          3????????for(int?i=0;i<100;i++){
          4????????????System.out.println(i);
          5????????}
          ????
          6????}

          7}


          結果:
          輸出的數字是有序的,首先輸出A的數字,然后是B,說明是同步的,雖然是不同的線程,但兩個方法是同步調用的。
          注意:上面雖然是兩個不同的線程,但是是同一個實例對象。下面使用不同的實例對象進行測試。

          c、每個線程都有獨立的TestThread對象。
          目標類:

          1public?class?TestThread?{
          2????public?synchronized?void?execute(){??//synchronized修飾
          3????????for(int?i=0;i<100;i++){
          4????????????System.out.println(i);
          5????????}
          ????
          6????}

          7}

          線程類:
          1public?class?ThreadA?implements?Runnable{
          2????public?void?run()?{
          3????????TestThread?test=new?TestThread();
          4????????test.execute();
          5????}

          6}

          7

          調用:
          1Runnable?runabble=new?ThreadA();
          2Thread?a=new?Thread(runabble,"A");????????????????
          3a.start();
          4Thread?b=new?Thread(runabble,"B");
          5b.start();


          結果:
          輸出的數字交錯在一起。說明雖然增加了synchronized 關鍵字來修飾方法,但是不同的線程調用各自的對象實例,兩個方法仍然是異步的。

          引申:
          對于這種多個實例,要想實現同步即輸出的數字是有序并且按線程先后順序輸出,我們可以增加一個靜態變量,對它進行加鎖(后面將說明鎖定的對象)。

          修改目標類:

          ?1public?class?TestThread?{
          ?2????private?static?Object?lock=new?Object();?//必須是靜態的。
          ?3????public??void?execute(){
          ?4????????synchronized(lock){
          ?5????????????for(int?i=0;i<100;i++){
          ?6????????????????System.out.println(i);
          ?7????????????}
          ????
          ?8????????}

          ?9????}

          10}

          二、同步代碼塊

          1public?void?method(SomeObject?so){
          2????synchronized(so)
          3???????//…..
          4????}

          5}

          鎖定一個對象,其實鎖定的是該對象的引用(object reference)
          誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以按上面的代碼寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創建一個特殊的instance變量(它必須是一個對象)來充當鎖(上面的解決方法就是增加了一個狀態鎖)。

          a、鎖定一個對象,它不是靜態的
          private byte[] lock = new byte[0]; // 特殊的instance變量
          目標類:

          ?1public?class?TestThread?{
          ?2????private?Object?lock=new?Object();?
          ?3????public??void?execute(){
          ?4????????synchronized(lock){??//增加了個鎖,鎖定了對象lock,在同一個類實例中,是線程安全的,但不同的實例還是不安全的。
          ?5
          ?6因為不同的實例有不同對象鎖lock
          ?7????????????for(int?i=0;i<100;i++){
          ?8????????????????System.out.println(i);
          ?9????????????}
          ????
          10????????}

          11????}

          12}
          ??


          其實上面鎖定一個方法,等同于下面的:

          1public?void?execute(){??
          2????synchronized(this){???//同步的是當然對象
          3????????for(int?i=0;i<100;i++){
          4????????????System.out.println(i);
          5????????}
          ????
          6????}

          7}

          b、鎖定一個對象或方法,它是靜態的
          這樣鎖定,它鎖定的是對象所屬的類
          public synchronized? static void execute(){
          ??? //...
          }
          等同于

          1public?class?TestThread?{
          2????public?static?void?execute(){
          3????????synchronized(TestThread.class){
          4????????????//
          5????????}

          6????}

          7}

          測試:

          目標類:

          ?1public?class?TestThread?{
          ?2????private?static?Object?lock=new?Object();
          ?3????public?synchronized?static?void?execute(){??//同步靜態方法
          ?4????????for(int?i=0;i<100;i++){
          ?5????????????System.out.println(i);
          ?6????????}
          ????
          ?7????}

          ?8????public?static?void?execute1(){
          ?9????????for(int?i=0;i<100;i++){
          10????????????System.out.println(i);
          11????????}
          ????
          12????}

          13????public?void?test(){
          14????????execute();?????//輸出是有序的,說明是同步的
          15????????//execute1();??//輸出是無須的,說明是異步的
          16????}

          17}

          線程類:調用不同的方法,于是建立了兩個線程類

          ?1public?class?ThreadA?implements?Runnable{
          ?2????public?void?run()?{
          ?3????????TestThread.execute();//調用同步靜態方法
          ?4????}

          ?5}

          ?6public?class?ThreadB?implements?Runnable{
          ?7????public?void?run()?{
          ?8????????TestThread?test=new?TestThread();
          ?9????????test.test();//調用非同步非靜態方法
          10????}

          11}

          調用:

          1Runnable?runabbleA=new?ThreadA();
          2Thread?a=new?Thread(runabbleA,"A");????????????????
          3a.start();
          4Runnable?runabbleB=new?ThreadB();
          5Thread?b=new?Thread(runabbleB,"B");????????????????
          6b.start();

          注意:
          1、用synchronized 來鎖定一個對象的時候,如果這個對象在鎖定代碼段中被修改了,則這個鎖也就消失了。看下面的實例:

          目標類:

          ?1public?class?TestThread?{
          ?2?????private?static?final?class?TestThreadHolder?{
          ?3????????????private?static?TestThread?theSingleton?=?new?TestThread();
          ?4????????????public?static?TestThread?getSingleton()?{
          ?5????????????????return?theSingleton;
          ?6????????????}

          ?7????????????private?TestThreadHolder()?{
          ?8????????????}

          ?9????????}

          10?????
          11????private?Vector?ve?=null;
          12????private?Object?lock=new?Object();
          13????private?TestThread(){
          14????????ve=new?Vector();
          15????????initialize();
          16????}

          17????public?static?TestThread?getInstance(){
          18????????return?TestThreadHolder.getSingleton();
          19????}

          20????private?void?initialize(){
          21????????for(int?i=0;i<100;i++){
          22????????????ve.add(String.valueOf(i));
          23????????}

          24????}

          25????public?void?reload(){
          26????????synchronized(lock){
          27????????????ve=null;????????????
          28????????????ve=new?Vector();
          29????????????????????????//lock="abc";?
          30????????????for(int?i=0;i<100;i++){
          31????????????????ve.add(String.valueOf(i));
          32????????????}

          33????????}

          34????????System.out.println("reload?end");
          35????}

          36????
          37????public?boolean?checkValid(String?str){
          38????????synchronized(lock){
          39????????????System.out.println(ve.size());
          40????????????return?ve.contains(str);
          41????????}

          42????}

          43}

          說明:在reload和checkValid方法中都增加了synchronized關鍵字,對lock對象進行加鎖。在不同線程中對同一個對象實例分別調用reload和checkValid方法。
          在reload方法中,不修改lock對象即注釋lock="abc"; ,結果在控制臺輸出reload end后才輸出100。說明是同步調用的。
          如果在reload方法中修改lock對象即去掉注釋,結果首先輸出了一個數字(當前ve的大小),然后輸出reload end。說明是異步調用的。

          2、單例模式中對多線程的考慮

          ?1public?class?TestThread?{
          ?2?????private?static?final?class?TestThreadHolder?{
          ?3????????????private?static?TestThread?theSingleton?=?new?TestThread();
          ?4????????????public?static?TestThread?getSingleton()?{
          ?5????????????????return?theSingleton;
          ?6????????????}

          ?7????????????private?TestThreadHolder()?{
          ?8????????????}

          ?9????????}

          10????private?Vector?ve?=null;
          11????private?Object?lock=new?Object();
          12????private?TestThread(){
          13????????ve=new?Vector();
          14????????initialize();
          15????}

          16????public?static?TestThread?getInstance(){
          17????????return?TestThreadHolder.getSingleton();
          18????}

          19????????'''
          20}

          說明:增加了一個內部類,在內部類中申明一個靜態的對象,實例化該單例類,初始化的數據都在單例類的構造函數中進行。這樣保證了多個實例同時訪問的時候,初始化的數據都已經成功初始化了。

          總結:
          A. 無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖,所以首先應知道需要加鎖的對象
          B.每個對象只有一個鎖(lock)與之相關聯。
          C.實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制
          posted on 2008-08-06 14:44 無聲 閱讀(427) 評論(0)  編輯  收藏 所屬分類: 職場生活
          主站蜘蛛池模板: 申扎县| 海安县| 阿鲁科尔沁旗| 基隆市| 德钦县| 浦江县| 东乡族自治县| 长武县| 广宗县| 依安县| 德钦县| 西安市| 邯郸县| 土默特右旗| 石渠县| 桃园市| 汉寿县| 武平县| 滁州市| 铜陵市| 龙泉市| 呼伦贝尔市| 广丰县| 平邑县| 北票市| 霍城县| 凤山县| 扬州市| 永登县| 尤溪县| 浮山县| 岑巩县| 盱眙县| 湘潭市| 囊谦县| 滦平县| 上饶市| 永州市| 龙山县| 天峨县| 十堰市|