<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 154  文章 - 60  trackbacks - 0
    <2007年10月>
    30123456
    78910111213
    14151617181920
    21222324252627
    28293031123
    45678910

    聲明:

    該blog是為了收集資料,認識朋友,學習、提高技術,所以本blog的內容除非聲明,否則一律為轉載!!

    感謝那些公開自己技術成果的高人們!!!

    支持開源,尊重他人的勞動!!

    常用鏈接

    留言簿(3)

    隨筆分類(148)

    隨筆檔案(143)

    收藏夾(2)

    其他

    學習(技術)

    觀察思考(非技術)

    搜索

    •  

    最新評論

    閱讀排行榜

    評論排行榜

    工作需要,簡單查看,很多東西日后鉆研!先放在這


    ==================================================
    synchronized 的語法:
     synchronized 可以作為一個method的修飾符,也可以一段代碼里出現,先說在代碼里出:
      其語法是
       synchronized(xx) {
        //your code
       }
       這個xx需要是一個Object,只要是一個Object就行,如:
            String s="haha";
            synchronized(s) {
               //your code
            }
       不是Object不行,如:
            int n=3;
            synchronized(n) {
                    //your code
            }
       是不可以的,有autoboxing也不行。
       如果你理解this的含義,那么
        synchronized(this) {
            //your code
        }
       也很好理解,它需要一個Object,而this是一個特殊的Object,當然可以這樣用。
    再說synchronized 修飾 method的情況,如果synchronized修飾一個非static的method,
    如:
        public synchronized void aMethod() {
            //some code
        }
    相當于:
        public void aMethod() {
            synchronized(this) {
               //some code
            }
        }
    修飾一個 static的method,
    如:
        public static synchronized void aMethod() {
            //some code
        }
    相當于:
        public static synchronized void aMethod() {
            synchronized(XX.class) {
               //some code
            }
        }
    XX是這個方法所在的類,XX.class 也是一個Object,類型是Class而已,在一個
    ClassLoader里,它是唯一的,就是獨一無二的object
        總之synchronized的語法可以統(tǒng)一為: synchronized(a var) { do something }


    synchronized 的語義:
     這是我自己的理解,
       synchronized(xx) {
          //your code
       }
     的語義是,在xx這個Object的“授權”、“名義”、 “面子”下,執(zhí)行 your code。要注
    意的是,xx只能授權給一個人(線程),當xx授權給某個人執(zhí)行后,就不能再授權給別人了
    。 當那個人執(zhí)行完那段代碼后,xx才能繼續(xù)授權給其它人執(zhí)行,可以理解為,別人在xx的
    授權下,執(zhí)行完這段代碼后,把這個權利又還給xx了。 當xx不能授權給一個人時,這個人
    必須等在這里,知道xx可以授權給它。 (上面說的人都是線程)


    synchronized 的作用:
     synchronized是用在多線程環(huán)境中的,作用簡單的說,就是不允許 “某些” 線程 同時執(zhí)
    行到一段代碼里。 這個 “某些”線程 怎么界定? 是由那個xx object決定的,就是當兩
    個線程執(zhí)行到 synchronized的時候,需要同一個Object授權時,這兩個線程不能同時執(zhí)行
    到需要授權的代碼。
      極端情況是 系統(tǒng)你所有的線程都不能執(zhí)行到這段代碼里,那么你就選一個極端唯一的
    object作為xx,一般選Class object,如:
     synchronized(String.class) {
     }

    ==================對synchronized(this)的一些理解======================
    一、當兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執(zhí)行。另一個線程必須等待當前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。



    二、然而,當一個線程訪問object的一個synchronized(this)同步代碼塊時,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。



    三、尤其關鍵的是,當一個線程訪問object的一個synchronized(this)同步代碼塊時,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。



    四、第三個例子同樣適用其它同步代碼塊。也就是說,當一個線程訪問object的一個synchronized(this)同步代碼塊時,它就獲得了這個object的對象鎖。結果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞。



    五、以上規(guī)則對其它對象鎖同樣適用.



    舉例說明:



    一、當兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內只能有一個線程得到執(zhí)行。另一個線程必須等待當前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊。



    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



    五、以上規(guī)則對其它對象鎖同樣適用:



    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
    ==============關于java中的synchronized關鍵字的理解==============

    搞清楚synchronized鎖定的是哪個對象,就能幫助我們設計更安全的多線程程序

    ●首先是synchronized關鍵字的作用域可以有兩種:

    ?某個對象實例內

    synchronized aMethod(){}可以防止多個線程同時訪問這個對象的synchronized方法(如果一個對象有多個synchronized方法,只要一個線 程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)

    ?某個類的范圍

    synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法

    ●synchronized關鍵字是不能繼承的

    ●要注意的事項

    ?無論synchronized關鍵字加在方法上還是對象上,它取得的鎖都是對象,而不是把一段代碼或函數當作鎖――而且同步方法很可能還會被其他線程的對象訪問

    ?每個對象只有一個鎖(lock)與之相關聯(lián)

    ?實現同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制

    對共享資源的同步訪問更加安全的技巧

    ?定義private instance變量+它的 get方法,而不要定義public/protectedinstance變量。如果將變量定義為public,對象在外界可以繞過同步方法的控制而直接取得它,并改動它。

    ?如果instance變量是一個對象,如數組或ArrayList什么的,那上述方法仍然不安全,因為當外界對象通過get方法拿到這個instance對象的引用后,又將其指向另一個對象,那么這個private變量也就變了,豈不是很危險。 這個時候就需要將get方法也加上synchronized同步,并且,只返回這個private對象的clone()――這樣,調用端得到的就是對象副本的引用了。


    =====================Java線程及同步(synchronized)樣例代碼=======================

    import java.io.*;
    import java.util.*;
    import java.text.SimpleDateFormat;

    public class TestThread extends Thread
    {
        private static Integer threadCounterLock; //用于同步,防止數據被寫亂
        private static int threadCount; //本類的線程計數器

        static
        {
            threadCount = 0;
            threadCounterLock = new Integer(0);
        }

        public TestThread()
        {
            super();
        }

        public synchronized static void incThreadCount()
        {
            threadCount++;
            System.out.println("thread count after enter: " + threadCount);
        }

        public synchronized static void decThreadCount()
        {
            threadCount--;
            System.out.println("thread count after leave: " + threadCount);
        }

        public void run()
        {
            synchronized(threadCounterLock) //同步
            {
                threadCount++;
                System.out.println("thread count after enter: " + threadCount);
            }

            //incThreadCount(); //和上面的語句塊是等價的

            final long nSleepMilliSecs = 1000;  //循環(huán)中的休眠時間

            long nCurrentTime = System.currentTimeMillis();
            long nEndTime = nCurrentTime + 30000;  //運行截止時間
            SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

            try
            {
                while (nCurrentTime < nEndTime)
                {
                    nCurrentTime = System.currentTimeMillis();
                    System.out.println("Thread " + this.hashCode() + ", current time: " + simpleDateFormat.format(new Date(nCurrentTime)));

                    try
                    {
                        sleep(nSleepMilliSecs);
                    }
                    catch(InterruptedException ex)
                    {
                        ex.printStackTrace();
                    }
                }
            }
            finally
            {
                synchronized(threadCounterLock) //同步
                {
                    threadCount--;
                    System.out.println("thread count after leave: " + threadCount);
                }

                //decThreadCount();   //和上面的語句塊是等價的
            }
        }

        public static void main(String[] args)
        {
            TestThread[] testThread = new TestThread[2];
            for (int i=0; i<testThread.length; i++)
            {
                testThread[i] = new TestThread();
                testThread[i].start();
            }
        }
    }

    同步就是簡單的說我用的時候你不能用,大家用的要是一樣的就這樣!
    比如說有只蘋果很好吃,我拉起來咬一口,放下,你再拉起咬一口,這就同步了,要是大家一起咬,呵呵,那就結婚吧,婚禮上常能看到這個,也不怕咬著嘴,嘻嘻嘻!

    舉個例子,現在有個類,類中有一個私有成員一個蘋果,兩個方法一個看,一個吃。
    現在不同步,我一“看”,哈哈一個蘋果,我“吃”四分之一了
    你一“看”,哈哈一個蘋果,也“吃”四分之一了。
    可能的情況就是都是看到一個蘋果但我的吃方法用在你的之前,所以可能你只能吃到3/4的1/4也就是3/16個而不是1/4個蘋果了。
    現在加入同步鎖,我在吃的時候你看被鎖,等吃完了,你再看,啊3/4個蘋果,吃1/3好了了,就這樣!

    ===========================synchronized的一個簡單例子================================

     

    public class TextThread
    {

     /**
      * @param args
      */
     public static void main(String[] args)
     {
      // TODO 自動生成方法存根
            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()
     {
      while (true)
      {
       synchronized(str)
       {
       if (num>0)
       {
        try
        {
         Thread.sleep(10);
        }
        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)與之相關聯(lián)。

    C.實現同步是要很大的系統(tǒng)開銷作為代價的,甚至可能造成死鎖,所以盡量避免無謂的同步控制。

    接著來討論synchronized用到不同地方對代碼產生的影響:

    假設P1P2是同一個類的不同對象,這個類中定義了以下幾種情況的同步塊或同步方法,P1P2就都可以調用它們。

    1. synchronized當作函數修飾符時,示例代碼如下:

    Public synchronized void methodAAA()

    {

    //….

    }

    這也就是同步方法,那這時synchronized鎖定的是哪個對象呢?它鎖定的是調用這個同步方法對象。也就是說,當一個對象P1在不同的線程中執(zhí)行這個同步方法時,它們之間會形成互斥,達到同步的效果。但是這個對象所屬的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這個對象,誰拿到這個鎖誰就可以運行它所控制的那段代碼。當有一個明確的對象作為鎖時,就可以這樣寫程序,但當沒有明確的對象作為鎖,只是想讓一段代碼同步時,可以創(chuàng)建一個特殊的instance變量(它得是一個對象)來充當鎖:

    class Foo implements Runnable

    {

           private byte[] lock = new byte[0]; // 特殊的instance變量

        Public void methodA()

    {

           synchronized(lock) { //… }

    }

    //…..

    }

    注:零長度的byte數組對象創(chuàng)建起來將比任何對象都經濟――查看編譯后的字節(jié)碼:生成零長度的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類產生的對象。

    可以推斷:如果一個類中定義了一個synchronizedstatic函數A,也定義了一個synchronized instance函數B,那么這個類的同一對象Obj在多線程中分別訪問AB兩個方法時,不會構成同步,因為它們的鎖都不一樣。A方法的鎖是Obj這個對象,而B的鎖是Obj所屬的那個Class

    小結如下:

    搞清楚synchronized鎖定的是哪個對象,就能幫助我們設計更安全的多線程程序。

    還有一些技巧可以讓我們對共享資源的同步訪問更加安全:

    1. 定義private instance變量+它的 get方法,而不要定義public/protectedinstance變量。如果將變量定義為public,對象在外界可以繞過同步方法的控制而直接取得它,并改動它。這也是JavaBean的標準實現方式之一。

    2. 如果instance變量是一個對象,如數組或ArrayList什么的,那上述方法仍然不安全,因為當外界對象通過get方法拿到這個instance對象的引用后,又將其指向另一個對象,那么這個private變量也就變了,豈不是很危險。這個時候就需要將get方法也加上synchronized同步,并且,只返回這個private對象的clone()――這樣,調用端得到的就是對象副本的引用了。



    ==================搞懂java中的synchronized關鍵字====================

    2006年12月27日 上午 10:00 | 作者:littlebat

    實際上,我關于java的基礎知識的90%以上都來自Thinking in Java。對于其中的synchronized關鍵字,當時就是瀏覽一下,大概知道意思,也沒有細看。后來一直沒有用過這個關鍵字。昨天看Thinking in Patterns with Java中的Observer模式,看了其中的Observable類的源碼,發(fā)現里面幾乎所有的方法都用了synchronized關鍵字(不是全部),其中個別用了synchronized(this){}的區(qū)塊。于是,我發(fā)覺有必要好好理解一下這個關鍵字了。

    我再次看了侯捷譯的Thinking in Java第二版中文版,得到有關synchronized的如下信息:

    1、synchronized關鍵字的作用域有二種:

    1)是某個對象實例內,synchronized aMethod(){}可以防止多個線程同時訪問這個對象的synchronized方法(如果一個對象有多個synchronized方法,只要一個線程訪問了其中的一個synchronized方法,其它線程不能同時訪問這個對象中任何一個synchronized方法)。這時,不同的對象實例的synchronized方法是不相干擾的。也就是說,其它線程照樣可以同時訪問相同類的另一個對象實例中的synchronized方法;

    2)是某個類的范圍,synchronized static aStaticMethod{}防止多個線程同時訪問這個類中的synchronized static 方法。它可以對類的所有對象實例起作用。

    2、除了方法前用synchronized關鍵字,synchronized關鍵字還可以用于方法中的某個區(qū)塊中,表示只對這個區(qū)塊的資源實行互斥訪問。用法是: synchronized(this){/*區(qū)塊*/},它的作用域是當前對象;

    3、synchronized關鍵字是不能繼承的,也就是說,基類的方法synchronized f(){} 在繼承類中并不自動是synchronized f(){},而是變成了f(){}。繼承類需要你顯式的指定它的某個方法為synchronized方法;

    4、疑問:

    我知道了有static區(qū)塊(其中的加載時機好像也不清楚,需要學習一下,原來看Thinking in Java好像是說: static區(qū)塊加載的時機是類的一個對象實例創(chuàng)建或類的一個static方法被訪問,但是我今天早上看了javaeye.com上面的一個關于“<static塊到底什么時候執(zhí)行?-eway -JavaEye技術社區(qū)>”的文章后又有點迷糊了:),也許,需要認真學習一下JVM的內在機制才是理解這類問題最根本的途徑),也有synchronized區(qū)塊,那么,有synchronized static 區(qū)塊嗎?意思是問:有沒有一個類作用域內的synchronized區(qū)塊?



    ==============ThreadLocal與synchronized ====================
    Java良好的支持多線程。使用java,我們可以很輕松的編程一個多線程程序。但是使用多線程可能會引起并發(fā)訪問的問題。synchronized和ThreadLocal都是用來解決多線程并發(fā)訪問的問題。大家可能對synchronized較為熟悉,而對ThreadLocal就要陌生得多了。
    并發(fā)問題。當一個對象被兩個線程同時訪問時,可能有一個線程會得到不可預期的結果。

    一個簡單的java類Studnet

    代碼
    1. public class Student {   
    2.   private int age=0;   
    3.      
    4.   public int getAge() {   
    5.       return this.age;   
    6.          
    7.   }   
    8.      
    9.   public void setAge(int age) {   
    10.       this.age = age;   
    11.   }   
    12. }  

    一個多線程類ThreadDemo.
    這個類有一個Student的私有變量,在run方法中,它隨機產生一個整數。然后設置到student變量中,從student中讀取設置后的值。然后睡眠5秒鐘,最后再次讀student的age值。

     

    代碼
    1. public class ThreadDemo implements Runnable{   
    2.   Student student = new Student();   
    3.   public static void main(String[] agrs) {   
    4.      ThreadDemo td = new ThreadDemo();   
    5.      Thread t1 = new Thread(td,"a");   
    6.      Thread t2 = new Thread(td,"b");   
    7.     t1.start();   
    8.     t2.start();   
    9.   
    10.   }   
    11. /* (non-Javadoc)  
    12.  * @see java.lang.Runnable#run()  
    13.  */  
    14.  public void run() {   
    15.      accessStudent();   
    16.  }   
    17.     
    18.  public void accessStudent() {   
    19.         String currentThreadName = Thread.currentThread().getName();   
    20.         System.out.println(currentThreadName+" is running!");   
    21.        // System.out.println("first  read age is:"+this.student.getAge());   
    22.         Random random = new Random();   
    23.         int age = random.nextInt(100);   
    24.         System.out.println("thread "+currentThreadName +" set age to:"+age);   
    25.           
    26.         this.student.setAge(age);   
    27.         System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());   
    28.         try {   
    29.         Thread.sleep(5000);   
    30.         }   
    31.         catch(InterruptedException ex) {   
    32.             ex.printStackTrace();   
    33.         }   
    34.         System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());   
    35.             
    36.  }   
    37.      
    38. }  
    運行這個程序,屏幕輸出如下:
    a is running!
    b is running!
    thread b set age to:33
    thread b first read age is:33
    thread a set age to:81
    thread a first read age is:81
    thread b second read age is:81
    thread a second read age is:81

     

    需要注意的是,線程a在同一個方法中,第一次讀取student的age值與第二次讀取值不一致。這就是出現了并發(fā)問題。

    synchronized
    上面的例子,我們模似了一個并發(fā)問題。Java提供了同步機制來解決并發(fā)問題。synchonzied關鍵字可以用來同步變量,方法,甚至同步一個代碼塊。
    使用了同步后,一個線程正在訪問同步對象時,另外一個線程必須等待。
    Synchronized同步方法
    現在我們可以對accessStudent方法實施同步。
    public synchronized void accessStudent()
    再次運行程序,屏幕輸出如下:
    a is running!
    thread a set age to:49
    thread a first read age is:49
    thread a second read age is:49
    b is running!
    thread b set age to:17
    thread b first read age is:17
    thread b second read age is:17

    加上了同步后,線程b必須等待線程a執(zhí)行完畢后,線程b才開始執(zhí)行。

    對方法進行同步的代價是非常昂貴的。特別是當被同步的方法執(zhí)行一個冗長的操作。這個方法執(zhí)行會花費很長的時間,對這樣的方法進行同步可能會使系統(tǒng)性能成數量級的下降。

    Synchronized同步塊
    在accessStudent方法中,我們真實需要保護的是student變量,所以我們可以進行一個更細粒度的加鎖。我們僅僅對student相關的代碼塊進行同步。

    代碼
    1. synchronized(this) {   
    2. Random random = new Random();   
    3. int age = random.nextInt(100);   
    4. System.out.println("thread "+currentThreadName +" set age to:"+age);   
    5.   
    6. this.student.setAge(age);   
    7.   
    8. System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());   
    9. try {   
    10. Thread.sleep(5000);   
    11. }   
    12. catch(InterruptedException ex) {   
    13.     ex.printStackTrace();   
    14. }   
    15. }  
    運行方法后,屏幕輸出:
    a is running!
    thread a set age to:18
    thread a first read age is:18
    b is running!
    thread a second read age is:18
    thread b set age to:62
    thread b first read age is:62
    thread b second read age is:62

     

    需要特別注意這個輸出結果。
    這個執(zhí)行過程比上面的方法同步要快得多了。
    只有對student進行訪問的代碼是同步的,而其它與部份代碼卻是異步的了。而student的值并沒有被錯誤的修改。如果是在一個真實的系統(tǒng)中,accessStudent方法的操作又比較耗時的情況下。使用同步的速度幾乎與沒有同步一樣快。

    使用同步鎖
    稍微把上面的例子改一下,在ThreadDemo中有一個私有變量count,。
    private int count=0;
    在accessStudent()中, 線程每訪問一次,count都自加一次, 用來記數線程訪問的次數。

    代碼
    1. try {   
    2. this.count++;   
    3. Thread.sleep(5000);   
    4. }catch(InterruptedException ex) {   
    5.     ex.printStackTrace();   
    6. }  
    為了模擬線程,所以讓它每次自加后都睡眠5秒。
    accessStuden()方法的完整代碼如下:
    代碼
    1.    String currentThreadName = Thread.currentThread().getName();   
    2. System.out.println(currentThreadName+" is running!");   
    3.   try {   
    4. this.count++;   
    5. Thread.sleep(5000);   
    6. }catch(InterruptedException ex) {   
    7.     ex.printStackTrace();   
    8. }   
    9.  System.out.println("thread "+currentThreadName+" read count:"+this.count);   
    10.   
    11.   
    12. synchronized(this) {   
    13. Random random = new Random();   
    14. int age = random.nextInt(100);   
    15. System.out.println("thread "+currentThreadName +" set age to:"+age);   
    16.   
    17. this.student.setAge(age);   
    18.   
    19. System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());   
    20. try {   
    21. Thread.sleep(5000);   
    22. }   
    23. catch(InterruptedException ex) {   
    24.     ex.printStackTrace();   
    25. }   
    26. }   
    27. System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());  
    運行程序后,屏幕輸出:
    a is running!
    b is running!
    thread a read count:2
    thread a set age to:49
    thread a first read age is:49
    thread b read count:2
    thread a second read age is:49
    thread b set age to:7
    thread b first read age is:7
    thread b second read age is:7

     

    我們仍然對student對象以synchronized(this)操作進行同步。
    我們需要在兩個線程中共享count失敗。

    所以仍然需要對count的訪問進行同步操作。

    代碼
    1. synchronized(this) {   
    2.   try {   
    3.   this.count++;   
    4.   Thread.sleep(5000);   
    5.   }catch(InterruptedException ex) {   
    6.     ex.printStackTrace();   
    7.   }   
    8.   }   
    9.   System.out.println("thread "+currentThreadName+" read count:"+this.count);   
    10.      
    11.     
    12.   synchronized(this) {   
    13.   Random random = new Random();   
    14.   int age = random.nextInt(100);   
    15.   System.out.println("thread "+currentThreadName +" set age to:"+age);   
    16.     
    17.   this.student.setAge(age);   
    18.     
    19.   System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());   
    20.   try {   
    21.   Thread.sleep(5000);   
    22.   }   
    23.   catch(InterruptedException ex) {   
    24.     ex.printStackTrace();   
    25.   }   
    26.   }   
    27.   System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());   
    28.   long endTime = System.currentTimeMillis();   
    29.   long spendTime = endTime - startTime;   
    30.   System.out.println("花費時間:"+spendTime +"毫秒");  

    程序運行后,屏幕輸出
    a is running!
    b is running!
    thread a read count:1
    thread a set age to:97
    thread a first read age is:97
    thread a second read age is:97
    花費時間:10015毫秒
    thread b read count:2
    thread b set age to:47
    thread b first read age is:47
    thread b second read age is:47
    花費時間:20124毫秒

     

    我們在同一個方法中,多次使用synchronized(this)進行加鎖。有可能會導致太多額外的等待。
    應該使用不同的對象鎖進行同步。

    設置兩個鎖對象,分別用于student和count的訪問加鎖。

    代碼
    1.  private Object studentLock = new Object();   
    2. private Object countLock = new Object();   
    3.   
    4. accessStudent()方法如下:   
    5.      long startTime = System.currentTimeMillis();   
    6.         String currentThreadName = Thread.currentThread().getName();   
    7.         System.out.println(currentThreadName+" is running!");   
    8.        // System.out.println("first  read age is:"+this.student.getAge());   
    9.   
    10.          synchronized(countLock) {   
    11.         try {   
    12.         this.count++;   
    13.         Thread.sleep(5000);   
    14.         }catch(InterruptedException ex) {   
    15.             ex.printStackTrace();   
    16.         }   
    17.         }   
    18.         System.out.println("thread "+currentThreadName+" read count:"+this.count);   
    19.            
    20.           
    21.         synchronized(studentLock) {   
    22.         Random random = new Random();   
    23.         int age = random.nextInt(100);   
    24.         System.out.println("thread "+currentThreadName +" set age to:"+age);   
    25.           
    26.         this.student.setAge(age);   
    27.           
    28.         System.out.println("thread "+currentThreadName+" first  read age is:"+this.student.getAge());   
    29.         try {   
    30.         Thread.sleep(5000);   
    31.         }   
    32.         catch(InterruptedException ex) {   
    33.             ex.printStackTrace();   
    34.         }   
    35.         }   
    36.         System.out.println("thread "+currentThreadName +" second read age is:"+this.student.getAge());   
    37.         long endTime = System.currentTimeMillis();   
    38.         long spendTime = endTime - startTime;   
    39.         System.out.println("花費時間:"+spendTime +"毫秒");  

    這樣對count和student加上了兩把不同的鎖。

     

    運行程序后,屏幕輸出:
    a is running!
    b is running!
    thread a read count:1
    thread a set age to:48
    thread a first read age is:48
    thread a second read age is:48
    花費時間:10016毫秒
    thread b read count:2
    thread b set age to:68
    thread b first read age is:68
    thread b second read age is:68
    花費時間:20046毫秒
    與兩次使用synchronized(this)相比,使用不同的對象鎖,在性能上可以得到更大的提升。

    由此可見synchronized是實現java的同步機制。同步機制是為了實現同步多線程對相同資源的并發(fā)訪問控制。保證多線程之間的通信。
    可見,同步的主要目的是保證多線程間的數據共享。同步會帶來巨大的性能開銷,所以同步操作應該是細粒度的。如果同步使用得當,帶來的性能開銷是微不足道的。使用同步真正的風險是復雜性和可能破壞資源安全,而不是性能。

    ThreadLocal
    由上面可以知道,使用同步是非常復雜的。并且同步會帶來性能的降低。Java提供了另外的一種方式,通過ThreadLocal可以很容易的編寫多線程程序。從字面上理解,很容易會把ThreadLocal誤解為一個線程的本地變量。其它ThreadLocal并不是代表當前線程,ThreadLocal其實是采用哈希表的方式來為每個線程都提供一個變量的副本。從而保證各個線程間數據安全。每個線程的數據不會被另外線程訪問和破壞。

    我們把第一個例子用ThreadLocal來實現,但是我們需要些許改變。
    Student并不是一個私有變量了,而是需要封裝在一個ThreadLocal對象中去。調用ThreadLocal的set方法,ThreadLocal會為每一個線程都保持一份Student變量的副本。所以對student的讀取操作都是通過ThreadLocal來進行的。

    代碼
    1. protected Student getStudent() {   
    2.     Student student = (Student)studentLocal.get();   
    3.     if(student == null) {   
    4.         student = new Student();   
    5.         studentLocal.set(student);   
    6.     }   
    7.     return student;   
    8. }   
    9.   
    10. protected void setStudent(Student student) {   
    11.     studentLocal.set(student);   
    12. }  

    accessStudent()方法需要做一些改變。通過調用getStudent()方法來獲得當前線程的Student變量,如果當前線程不存在一個Student變量,getStudent方法會創(chuàng)建一個新的Student變量,并設置在當前線程中。
    Student student = getStudent();
    student.setAge(age);
    accessStudent()方法中無需要任何同步代碼。

     

    完整的代碼清單如下:
    TreadLocalDemo.java

    代碼
    1. public class TreadLocalDemo implements Runnable {   
    2.    private final static  ThreadLocal studentLocal = new ThreadLocal();   
    3.       
    4.    public static void main(String[] agrs) {   
    5.        TreadLocalDemo td = new TreadLocalDemo();   
    6.          Thread t1 = new Thread(td,"a");   
    7.          Thread t2 = new Thread(td,"b");   
    8.            
    9.         t1.start();   
    10.         t2.start();   
    11.           
    12.           
    13.   
    14.       }   
    15.       
    16.     /* (non-Javadoc)  
    17.      * @see java.lang.Runnable#run()  
    18.      */  
    19.     public void run() {   
    20.          accessStudent();   
    21.     }   
    22.   
    23.     public  void  accessStudent() {   
    24.            
    25.         String currentThreadName = Thread.currentThread().getName();   
    26.         System.out.println(currentThreadName+" is running!");   
    27.         Random random = new Random();   
    28.         int age = random.nextInt(100);   
    29.         System.out.println("thread "+currentThreadName +" set age to:"+age);   
    30.         Student student = getStudent();   
    31.         student.setAge(age);   
    32.         System.out.println("thread "+currentThreadName+" first  read age is:"+student.getAge());   
    33.         try {   
    34.         Thread.sleep(5000);   
    35.         }   
    36.         catch(InterruptedException ex) {   
    37.             ex.printStackTrace();   
    38.         }   
    39.         System.out.println("thread "+currentThreadName +" second read age is:"+student.getAge());   
    40.            
    41.     }   
    42.        
    43.     protected Student getStudent() {   
    44.         Student student = (Student)studentLocal.get();   
    45.         if(student == null) {   
    46.             student = new Student();   
    47.             studentLocal.set(student);   
    48.         }   
    49.         return student;   
    50.     }   
    51.        
    52.     protected void setStudent(Student student) {   
    53.         studentLocal.set(student);   
    54.     }   
    55. }  
    運行程序后,屏幕輸出:
    b is running!
    thread b set age to:0
    thread b first read age is:0
    a is running!
    thread a set age to:17
    thread a first read age is:17
    thread b second read age is:0
    thread a second read age is:17

     

    可見,使用ThreadLocal后,我們不需要任何同步代碼,卻能夠保證我們線程間數據的安全。
    而且,ThreadLocal的使用也非常的簡單。
    我們僅僅需要使用它提供的兩個方法
    void set(Object obj) 設置當前線程的變量的副本的值。
    Object get() 返回當前線程的變量副本

    另外ThreadLocal還有一個protected的initialValue()方法。返回變量副本在當前線程的初始值。默認為null

    ThreadLocal是怎么做到為每個線程都維護一個變量的副本的呢?
    我們可以猜測到ThreadLocal的一個簡單實現

    代碼
    1. public class ThreadLocal   
    2. {   
    3.  private Map values = Collections.synchronizedMap(new HashMap());   
    4.  public Object get()   
    5.  {   
    6.   Thread curThread = Thread.currentThread();    
    7.   Object o = values.get(curThread);    
    8.   if (o == null && !values.containsKey(curThread))   
    9.   {   
    10.    o = initialValue();   
    11.    values.put(curThread, o);    
    12.   }   
    13.   return o;    
    14.  }   
    15.   
    16.  public void set(Object newValue)   
    17.  {   
    18.   values.put(Thread.currentThread(), newValue);   
    19.  }   
    20.   
    21.  public Object initialValue()   
    22.  {   
    23.   return null;    
    24.  }   
    25. }  

    由此可見,ThreadLocal通過一個Map來為每個線程都持有一個變量副本。這個map以當前線程為key。與synchronized相比,ThreadLocal是以空間換時間的策略來實現多線程程序。

     

    Synchronized還是ThreadLocal?
    ThreadLocal以空間換取時間,提供了一種非常簡便的多線程實現方式。因為多個線程并發(fā)訪問無需進行等待,所以使用ThreadLocal會獲得更大的性能。雖然使用ThreadLocal會帶來更多的內存開銷,但這點開銷是微不足道的。因為保存在ThreadLocal中的對象,通常都是比較小的對象。另外使用ThreadLocal不能使用原子類型,只能使用Object類型。ThreadLocal的使用比synchronized要簡單得多。
    ThreadLocal和Synchonized都用于解決多線程并發(fā)訪問。但是ThreadLocal與synchronized有本質的區(qū)別。synchronized是利用鎖的機制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并不是同一個對象,這樣就隔離了多個線程對數據的數據共享。而Synchronized卻正好相反,它用于在多個線程間通信時能夠獲得數據共享。
    Synchronized用于線程間的數據共享,而ThreadLocal則用于線程間的數據隔離。
    當然ThreadLocal并不能替代synchronized,它們處理不同的問題域。Synchronized用于實現同步機制,比ThreadLocal更加復雜。

    =================Java 多線程程序設計要點(synchronized..) =====================
     

    多線程程序設計要點:


      1.多線程中有主內存和工作內存之分, 在JVM中,有一個主內存,專門負責所有線程共享數據;而每個線程都有他自己私有的工作內存, 主內存和工作內存分貝在JVM的stack區(qū)和heap區(qū)。

      2.線程的狀態(tài)有'Ready', 'Running', 'Sleeping', 'Blocked', 和 'Waiting'幾個狀態(tài),
    'Ready' 表示線程正在等待CPU分配允許運行的時間。

      3.線程運行次序并不是按照我們創(chuàng)建他們時的順序來運行的,CPU處理線程的順序是不確定的,如果需要確定,那么必須手工介入,使用setPriority()方法設置優(yōu)先級。

      4.我們無從知道一個線程什么時候運行,兩個或多個線程在訪問同一個資源時,需要synchronized

      5. 每個線程會注冊自己,實際某處存在著對它的引用,因此,垃圾回收機制對它就“束手無策”了。

      6. Daemon線程區(qū)別一般線程之處是:主程序一旦結束,Daemon線程就會結束。

      7. 一個對象中的所有synchronized方法都共享一把鎖,這把鎖能夠防止多個方法對通用內存同時進行的寫操作。synchronized static方法可在一個類范圍內被相互間鎖定起來。

      8. 對于訪問某個關鍵共享資源的所有方法,都必須把它們設為synchronized,否則就不能正常工作。

      9. 假設已知一個方法不會造成沖突,最明智的方法是不要使用synchronized,能提高些性能。

      10. 如果一個\"同步"方法修改了一個變量,而我們的方法要用到這個變量(可能是只讀),最好將自己的這個方法也設為 synchronized。

      11. synchronized不能繼承,  父類的方法是synchronized,那么其子類重載方法中就不會繼承“同步”。

      12. 線程堵塞Blocked有幾個原因造成:

      (1)線程在等候一些IO操作
      (2)線程試圖調用另外一個對象的“同步”方法,但那個對象處于鎖定狀態(tài),暫時無法使用。

      13.原子型操作(atomic), 對原始型變量(primitive)的操作是原子型的atomic. 意味著這些操作是線程安全的, 但是大部分情況下,我們并不能正確使用,來看看 i = i + 1 , i是int型,屬于原始型變量:

      (1)從主內存中讀取i值到本地內存.
      (2)將值從本地內存裝載到線程工作拷貝中.
      (3)裝載變量1.
      (4)將i 加 1.
      (5)將結果給變量i.
      (6)將i保存到線程本地工作拷貝中.
      (7)寫回主內存.

      注意原子型操作只限于第1步到第2步的讀取以及第6到第7步的寫, i的值還是可能被同時執(zhí)行i=i+1的多線程中斷打擾(在第4步)。

      double 和long 變量是非原子型的(non-atomic)。數組是object 非原子型。

      14. 由于13條的原因,我們解決辦法是:

      class xxx extends Thread{
       //i會被經常修改
      private int i;

      public synchronized int read(){ return i;}

      public synchronized void update(){ i = i + 1;}

      ..........

      }
     

      15. Volatile變量, volatile變量表示保證它必須是與主內存保持一致,它實際是"變量的同步", 也就是說對于volatile變量的操作是原子型的,如用在long 或 double變量前。

      16. 使用yield()會自動放棄CPU,有時比sleep更能提升性能。

      17. sleep()和wait()的區(qū)別是:wait()方法被調用時會解除鎖定,但是我們能使用它的地方只是在一個同步的方法或代碼塊內。

      18. 通過制造縮小同步范圍,盡可能的實現代碼塊同步,wait(毫秒數)可在指定的毫秒數可退出wait;對于wait()需要被notisfy()或notifyAll()踢醒。

      19. 構造兩個線程之間實時通信的方法分幾步:
      (1). 創(chuàng)建一個PipedWriter和一個PipedReader和它們之間的管道;
      PipedReader in = new PipedReader(new PipedWriter())
      (2). 在需要發(fā)送信息的線程開始之前,將外部的PipedWriter導向給其內部的Writer實例out
      (3). 在需要接受信息的線程開始之前,將外部的PipedReader導向給其內部的Reader實例in
      (4). 這樣放入out的所有東西度可從in中提取出來。

      20. synchronized帶來的問題除性能有所下降外,最大的缺點是會帶來死鎖DeadLock,只有通過謹慎設計來防止死鎖,其他毫無辦法,這也是線程難以馴服的一個原因。不要再使用stop() suspend() resume()和destory()方法

      21. 在大量線程被堵塞時,最高優(yōu)先級的線程先運行。但是不表示低級別線程不會運行,運行概率小而已。

      22. 線程組的主要優(yōu)點是:使用單個命令可完成對整個線程組的操作。很少需要用到線程組。

      23. 從以下幾個方面提升多線程的性能:

      檢查所有可能Block的地方,盡可能的多的使用sleep或yield()以及wait();

      盡可能延長sleep(毫秒數)的時間;

      運行的線程不用超過100個,不能太多;

      不同平臺linux或windows以及不同JVM運行性能差別很大。



    ========================================================================
     更多文章: http://scholar.ilib.cn/A-xdjsj-xby200301007.html
    posted on 2007-10-16 09:57 lk 閱讀(783) 評論(0)  編輯  收藏 所屬分類: j2se
    主站蜘蛛池模板: 一本大道一卡二大卡三卡免费| 不卡视频免费在线观看| 亚洲Av无码乱码在线播放| 中文字幕免费播放| 亚洲国产一区在线观看| 全黄性性激高免费视频| 182tv免费视频在线观看| 亚洲精品天堂在线观看| 国产亚洲一区区二区在线 | 曰批全过程免费视频网址 | 亚洲国产成人久久综合| 亚洲国产人成中文幕一级二级| 午夜精品射精入后重之免费观看| 中文字幕精品三区无码亚洲| 在线观看亚洲成人| 影音先锋在线免费观看| 三年片免费观看大全国语| 亚洲综合精品第一页| 亚洲精品无码国产| 嫩草影院免费观看| 一级毛片免费视频| 免费很黄无遮挡的视频毛片| 亚洲精品韩国美女在线| 亚洲午夜福利精品无码| 久久天天躁狠狠躁夜夜免费观看| 一级毛片a女人刺激视频免费 | 亚洲国产精华液网站w| 日韩中文无码有码免费视频| 99re免费在线视频| 精品亚洲国产成人av| 亚洲精品综合久久中文字幕| 亚洲乱色熟女一区二区三区丝袜| 成熟女人牲交片免费观看视频 | 一级片在线免费看| 亚洲欧美黑人猛交群| 狼友av永久网站免费观看| 午夜免费福利片观看| 国产精品小视频免费无限app | 亚洲爆乳少妇无码激情| 欧美最猛性xxxxx免费| AAAAA级少妇高潮大片免费看|