Java線程與進程理論知識

Java和Android開發(fā)過程中,我們經(jīng)常會提到進程、線程,什么是進程,什么是線程?

進程與線程

進程定義:進程是程序運行資源分配的最小單位(資源:CPU、內(nèi)存空間、IO),進程與進程之間獨立。
線程定義:CPU調(diào)度的最小單位,必須依賴于進程,一個進程內(nèi),允許有多個線程,線程之間可以共享資源。

并行與并發(fā)

舉個例子,如果有條高速公路 A 上面并排有 8 條車道,那么最大的并行車 輛就是 8 輛此條高速公路 A 同時并排行走的車輛小于等于 8 輛的時候,車輛就可 以并行運行。CPU 也是這個原理,一個 CPU 相當于一個高速公路 A,核心數(shù)或者線 程數(shù)就相當于并排可以通行的車道;而多個 CPU 就相當于并排有多條高速公路,而 每個高速公路并排有多個車道。
并發(fā):指應用能夠交替執(zhí)行不同的任務,比如單 CPU 核心下執(zhí)行多線程并非是 同時執(zhí)行多個任務,如果你開兩個線程執(zhí)行,就是在你幾乎不可能察覺到的速度不 斷去切換這兩個任務,已達到"同時執(zhí)行效果",其實并不是的,只是計算機的速度太 快,我們無法察覺到而已. 并行:指應用能夠同時執(zhí)行不同的任務,例:吃飯的時候可以邊吃飯邊打電話, 這兩件事情可以同時執(zhí)行
兩者區(qū)別:一個是交替執(zhí)行,一個是同時執(zhí)行.

Java多線程可以給程序帶來如下好處

1.充分利用CPU資源
2.加快響應用戶的時間
3.可以使你的代碼模塊化,異步化,簡單化

多線程程序需要注意事項

(1)線程之間的安全性 從前面的章節(jié)中我們都知道,在同一個進程里面的多線程是資源共享的,也就 是都可以訪問同一個內(nèi)存地址當中的一個變量。例如:若每個線程中對全局變量、 靜態(tài)變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的:若有多 個線程同時執(zhí)行寫操作,一般都需要考慮線程同步,否則就可能影響線程安全。
(2)線程之間的死鎖 為了解決線程之間的安全性引入了 Java 的鎖機制,而一不小心就會產(chǎn)生 Java 線程死鎖的多線程問題,因為不同的線程都在等待那些根本不可能被釋放的鎖,從
而導致所有的工作都無法完成。假設有兩個線程,分別代表兩個饑餓的人,他們必 須共享刀叉并輪流吃飯。他們都需要獲得兩個鎖:共享刀和共享叉的鎖。
假如線程 A 獲得了刀,而線程 B 獲得了叉。線程 A 就會進入阻塞狀態(tài)來等待 獲得叉,而線程 B 則阻塞來等待線程 A 所擁有的刀。
(3)線程太多了會將服務器資源耗盡形成死機當機 線程數(shù)太多有可能造成系統(tǒng)創(chuàng)建大量線程而導致消耗完系統(tǒng)內(nèi)存以及 CPU 的“過渡切換”,造成系統(tǒng)的死機,那么我們該如何解決這類問題呢? 某些系統(tǒng)資源是有限的,如文件描述符。多線程程序可能耗盡資源,因為每個 線程都可能希望有一個這樣的資源。如果線程數(shù)相當大,或者某個資源的侯選線 程數(shù)遠遠超過了可用的資源數(shù)則最好使用資源池。一個最好的示例是數(shù)據(jù)庫連接 池。只要線程需要使用一個數(shù)據(jù)庫連接,它就從池中取出一個,使用以后再將它返 回池中。資源池也稱為資源庫。

面試中可能會被問到,Java新啟線程的方式有幾種?

從Java源碼上,我們可以看到Java新啟線程的方式有兩種。

There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. 
This subclass should override the run method of class Thread.
An instance of the subclass can then be allocated and started. 
For example, a thread that computes primes larger than a stated value could be written as follows:

從Thread源碼注釋上,我們可以看到,Java啟動線程的方式有兩種:

  1. 繼承Thread類,重寫run()方法
public class StartThreadFirstMethod {
    static class FirstMethodThread extends Thread{
        @Override
        public void run() {
            System.out.println("啟動線程第一種方法,繼承Thread");
        }
    }
  public static void main(String[] args) {
        FirstMethodThread firstMethodThread=new FirstMethodThread();
        firstMethodThread.start();

    }
}
  1. 實現(xiàn)Runnable接口,實現(xiàn)run方法

public class StartThreadSecondMethod {
    static class PrimeRun implements Runnable{

        @Override
        public void run() {
            System.out.println("啟動線程第二種方法,implements Runnable");
        }
    }
    public static void main(String[] args) {
        PrimeRun primeRun=new PrimeRun();
        new Thread(primeRun).start();

    }
}

Thread類是Java對線程概念的抽象,new Thread(),只是new出一個Thread的實例,還沒有和操作系統(tǒng)中真正的線程掛鉤,只有執(zhí)行了start()方法,才真正意義上啟動了線程。start()方法讓線程進入就緒狀態(tài),等待分配CPU,分到CPU以后才會執(zhí)行run()方法,start()方法不能重復調(diào)用,重復調(diào)用會拋異常??!而 run 方法是業(yè)務邏輯實現(xiàn)的地方,本質(zhì)上和任意一個類的任意一個成員方法并沒有任何區(qū)別,可以重復執(zhí)行,也可以被單獨調(diào)用。

有開始就有結(jié)束,怎么樣才能讓Java里的線程安全停止工作呢?

1.線程自然中止

1).run執(zhí)行完成;
2).拋出一個未處理的異常,導致線程提前結(jié)束

2.stop()

暫停、恢復和停止操作對應在線程 Thread 的 API 就是 suspend()、resume() 和 stop()。但是這些 API 是過期的,也就是不建議使用的。不建議使用的原因主 要有:以 suspend()方法為例,在調(diào)用后,線程不會釋放已經(jīng)占有的資源(比如 鎖),而是占有著資源進入睡眠狀態(tài),這樣容易引發(fā)死鎖問題。同樣,stop()方 法在終結(jié)一個線程時不會保證線程的資源正常釋放,通常是沒有給予線程完成資 源釋放工作的機會,因此會導致程序可能工作在不確定狀態(tài)下。正因為 suspend()、 resume()和 stop()方法帶來的副作用,這些方法才被標注為不建議使用的過期方法。

3.interrupt()

安全的中止則是其他線程通過調(diào)用某個線程 A 的 interrupt()方法對其進行中 斷操作, 中斷好比其他線程對該線程打了個招呼,“A,你要中斷了”,不代表 線程 A 會立即停止自己的工作,同樣的 A 線程完全可以不理會這種中斷請求。 因為 java 里的線程是協(xié)作式的,不是搶占式的。線程通過檢查自身的中斷標志 位是否被置為 true 來進行響應。
線程通過方法 isInterrupted()來進行判斷是否被中斷,也可以調(diào)用靜態(tài)方法 Thread.interrupted()來進行判斷當前線程是否被中斷,不過 Thread.interrupted() 會同時將中斷標識位改寫為 false。

public class StartThreadFirstMethod {
    static class FirstMethodThread extends Thread {
        public FirstMethodThread(String firstMethodThread) {
            super(firstMethodThread);
        }

        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + " interrupte flag = " + isInterrupted());
            while (!isInterrupted()) {
                System.out.println(name + " is runing");
                System.out.println(name + " inner interrupte flag = " + isInterrupted());
            }
            System.out.println(name + " end interrupte flag = " + isInterrupted());
        }
    }


    public static void main(String[] args) throws InterruptedException {
        FirstMethodThread firstMethodThread = new FirstMethodThread("FirstMethodThread");
        firstMethodThread.start();
        Thread.sleep(30);
        firstMethodThread.interrupt();
    }
}

FirstMethodThread is runing
FirstMethodThread inner interrupte flag = false
FirstMethodThread is runing
FirstMethodThread inner interrupte flag = true
FirstMethodThread end interrupte flag = true
從輸出結(jié)果可以看到,調(diào)用isInterrupted()方法,未改變中斷標識位。

調(diào)用Thread.interrupted()方法,我們可以看到中斷標識位改寫為 false。

public class StartThreadFirstMethod {
    static class FirstMethodThread extends Thread {
        public FirstMethodThread(String firstMethodThread) {
            super(firstMethodThread);
        }

        @Override
        public void run() {
            String name = Thread.currentThread().getName();
            System.out.println(name + " interrupte flag = " + isInterrupted());
            while (!Thread.interrupted()) {
                System.out.println(name + " is runing");
                System.out.println(name + " inner interrupte flag = " + isInterrupted());
            }
            System.out.println(name + " end interrupte flag = " + isInterrupted());
        }
    }


    public static void main(String[] args) throws InterruptedException {
        FirstMethodThread firstMethodThread = new FirstMethodThread("FirstMethodThread");
        firstMethodThread.start();
        Thread.sleep(5);
        firstMethodThread.interrupt();
    }
}

FirstMethodThread is runing
FirstMethodThread inner interrupte flag = false
FirstMethodThread is runing
FirstMethodThread inner interrupte flag = false
FirstMethodThread is runing
FirstMethodThread inner interrupte flag = true
FirstMethodThread end interrupte flag = false

如果一個線程處于了阻塞狀態(tài)(如線程調(diào)用了 thread.sleep、thread.join、 thread.wait 等),則在線程在檢查中斷標示時如果發(fā)現(xiàn)中斷標示為 true,則會在 這些阻塞方法調(diào)用處拋出 InterruptedException 異常,并且在拋出異常后會立即 將線程的中斷標示位清除,即重新設置為 false。

public class StartThreadFirstMethod {
    static class FirstMethodThread extends Thread {
        public FirstMethodThread(String firstMethodThread) {
            super(firstMethodThread);
        }

        @Override
        public void run() {
            String threadName = currentThread().getName();
            System.out.println("thread name is "+threadName);
            while (!isInterrupted()){
                try {
                    sleep(100);
                    System.out.println(threadName +" is running");
                } catch (InterruptedException e) {
                    e.printStackTrace();
//                    interrupt();
                    System.out.println(threadName +" isInterrupted is "+isInterrupted());
                }

            }
            System.out.println(threadName +" end isInterrupted is "+isInterrupted());
        }
    }


    public static void main(String[] args) throws InterruptedException {
        FirstMethodThread firstMethodThread = new FirstMethodThread("FirstMethodThread");
        firstMethodThread.start();
        Thread.sleep(1000);
        firstMethodThread.interrupt();
    }
}

FirstMethodThread is running
FirstMethodThread is running
FirstMethodThread is running
FirstMethodThread is running
FirstMethodThread is running
FirstMethodThread is running
FirstMethodThread is running
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.java.base.thread.StartThreadFirstMethod$FirstMethodThread.run(StartThreadFirstMethod.java:15)
FirstMethodThread isInterrupted is false
FirstMethodThread is running
從輸出結(jié)果,可以看到,雖然調(diào)用了線程的interrupt方法,但是線程中斷標識位打印出來仍然是false,這是因為在拋出中斷異常的異常時,中斷標識也同時被修改成了false,所以線程不會中止,仍然會一直輸出內(nèi)容,只有在捕獲異常的位置再次調(diào)用interrupt方法,線程才會被真正的中斷。

public class StartThreadFirstMethod {
    static class FirstMethodThread extends Thread {
        public FirstMethodThread(String firstMethodThread) {
            super(firstMethodThread);
        }

        @Override
        public void run() {
            String threadName = currentThread().getName();
            System.out.println("thread name is "+threadName);
            while (!isInterrupted()){
                try {
                    sleep(100);
                    System.out.println(threadName +" is running");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    interrupt();
                    System.out.println(threadName +" isInterrupted is "+isInterrupted());
                }

            }
            System.out.println(threadName +" end isInterrupted is "+isInterrupted());
        }
    }


    public static void main(String[] args) throws InterruptedException {
        FirstMethodThread firstMethodThread = new FirstMethodThread("FirstMethodThread");
        firstMethodThread.start();
        Thread.sleep(1000);
        firstMethodThread.interrupt();
    }
}

FirstMethodThread is running
FirstMethodThread is running
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.java.base.thread.StartThreadFirstMethod$FirstMethodThread.run(StartThreadFirstMethod.java:15)
FirstMethodThread isInterrupted is true
FirstMethodThread end isInterrupted is true
捕獲異常的位置再次調(diào)用interrupt方法,線程才會被真正的中斷,中斷標識被設置為true.

其他線程相關(guān)方法

1.yield()

使當前CPU讓出CPU占有權(quán),但讓出的時間是不可設定的,也不會釋放鎖資源。線程進入就緒狀態(tài),再次被操作系統(tǒng)選中時又重新開始執(zhí)行。


線程生命周期

2.join()

把指定的線程加入到當前線程,可以將兩個交替執(zhí)行的線程合并為順序執(zhí)行。 比如在線程 B 中調(diào)用了線程 A 的 Join()方法,直到線程 A 執(zhí)行完畢后,才會繼續(xù) 執(zhí)行線程 B。
面試考點:如何讓兩個線程順序執(zhí)行

/**
 *類說明:演示Join()方法的使用
 */
public class UseJoin {
    
    static class Goddess implements Runnable {
        private Thread thread;

        public Goddess(Thread thread) {
            this.thread = thread;
        }

        public Goddess() {
        }

        public void run() {
            System.out.println("Goddess開始排隊打飯.....");
            try {
                if(thread!=null) thread.join();
            } catch (InterruptedException e) {
            }
            SleepTools.second(2);//休眠2秒
            System.out.println(Thread.currentThread().getName()
                    + " Goddess打飯完成.");
        }
    }

    static class GoddessLikefriend implements Runnable {

        public void run() {
            SleepTools.second(2);//休眠2秒
            System.out.println("Goddess暗戀目標開始排隊打飯.....");
            System.out.println(Thread.currentThread().getName()
                    + " Goddess暗戀目標打飯完成.");
        }
    }

    public static void main(String[] args) throws Exception {

        Thread beiTaiThread = Thread.currentThread();
        GoddessLikefriend goddessLikefriend = new GoddessLikefriend();
        Thread gbf = new Thread(goddessLikefriend);
        Goddess goddess = new Goddess(gbf);
        //Goddess goddess = new Goddess();
        Thread g = new Thread(goddess);
        g.start();
        gbf.start();
        System.out.println("備胎開始排隊打飯.....");
        g.join();
        SleepTools.second(2);//讓主線程休眠2秒
        System.out.println(Thread.currentThread().getName() + " 備胎打飯完成.");
    }
}

SleepTools工具類代碼如下

public class SleepTools {
    
    /**
     * 按秒休眠
     * @param seconds 秒數(shù)
     */
    public static final void second(int seconds) {
        try {
            TimeUnit.SECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }
    
    /**
     * 按毫秒數(shù)休眠
     * @param seconds 毫秒數(shù)
     */
    public static final void ms(int seconds) {
        try {
            TimeUnit.MILLISECONDS.sleep(seconds);
        } catch (InterruptedException e) {
        }
    }
}

輸出結(jié)果:
Goddess開始排隊打飯.....
備胎開始排隊打飯.....
Goddess暗戀目標開始排隊打飯.....
Thread-0 Goddess暗戀目標打飯完成.
Thread-1 Goddess打飯完成.
main 備胎打飯完成.

3.setPriority(int)

在 Java 線程中,通過一個整型成員變量 priority 來控制優(yōu)先級,優(yōu)先級的范 圍從 1~10,在線程構(gòu)建的時候可以通過 setPriority(int)方法來修改優(yōu)先級,默認 優(yōu)先級是 5,優(yōu)先級高的線程分配時間片的數(shù)量要多于優(yōu)先級低的線程。 設置線程優(yōu)先級時,針對頻繁阻塞(休眠或者 I/O 操作)的線程需要設置較 高優(yōu)先級,而偏重計算(需要較多 CPU 時間或者偏運算)的線程則設置較低的 優(yōu)先級,確保處理器不會被獨占。在不同的 JVM 以及操作系統(tǒng)上,線程規(guī)劃會 存在差異,有些操作系統(tǒng)甚至會忽略對線程優(yōu)先級的設定。

4.setDaemon(true)

Daemon(守護)線程是一種支持型線程,因為它主要被用作程序中后臺調(diào) 度以及支持性工作。這意味著,當一個 Java 虛擬機中不存在非 Daemon 線程的 時候,Java 虛擬機將會退出??梢酝ㄟ^調(diào)用 Thread.setDaemon(true)將線程設置 為 Daemon 線程。我們一般用不上,比如垃圾回收線程就是 Daemon 線程。
Daemon 線程被用作完成支持性工作,但是在 Java 虛擬機退出時 Daemon 線 程中的 finally 塊并不一定會執(zhí)行。在構(gòu)建 Daemon 線程時,不能依靠 finally 塊中 的內(nèi)容來確保執(zhí)行關(guān)閉或清理資源的邏輯。

public class DaemonThread {
    private static class UseThread extends Thread{
        @Override
        public void run() {
            try {
                while (!isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() 
                            + " I am extends Thread.");
                }
                System.out.println(Thread.currentThread().getName() 
                        + " interrupt flag is " + isInterrupted());
            } finally {
                //守護線程中finally不一定起作用
                System.out.println(" .............finally");
            }
        }
    }
    
    public static void main(String[] args) 
            throws InterruptedException{
        UseThread useThread = new UseThread();
        useThread.setDaemon(true);
        useThread.start();
        Thread.sleep(5);
//      useThread.interrupt();
    }
}

當用戶線程結(jié)束時,守護線程也同時結(jié)束,finally不一定會執(zhí)行,用戶線程里的finally肯定會執(zhí)行,只有線程為守護線程的時候,finally可能不會執(zhí)行。

5.sleep()

面試中經(jīng)常會被問到sleep方法對鎖的影響,用代碼驗證一下

public class SleepLock {
    private Object lock = new Object();

    public static void main(String[] args) {
        SleepLock sleepTest = new SleepLock();
        Thread threadA = sleepTest.new ThreadSleep();
        threadA.setName("ThreadSleep");
        Thread threadB = sleepTest.new ThreadNotSleep();
        threadB.setName("ThreadNotSleep");
        threadA.start();
        try {
            Thread.sleep(1000);
            System.out.println(" Main slept!");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        threadB.start();
    }

    private class ThreadSleep extends Thread{

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+" will take the lock");
            try {

                synchronized(lock) {
                    System.out.println(threadName+" taking the lock");
                    Thread.sleep(5000);
                    System.out.println("Finish the work: "+threadName);
                }
            } catch (InterruptedException e) {
                //e.printStackTrace();
            }
        }
    }

    private class ThreadNotSleep extends Thread{

        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            System.out.println(threadName+" will take the lock time="+System.currentTimeMillis());
            synchronized(lock) {
                System.out.println(threadName+" taking the lock time="+System.currentTimeMillis());
                System.out.println("Finish the work: "+threadName);
            }
        }
    }
}

ThreadSleep will take the lock
ThreadSleep taking the lock
Main slept!
ThreadNotSleep will take the lock time=1606999948908
Finish the work: ThreadSleep
ThreadNotSleep taking the lock time=1606999952908
Finish the work: ThreadNotSleep
從輸出結(jié)果看,調(diào)用了sleep方法以后,鎖并沒有釋放

線程間的共享和協(xié)作

1.synchronized

Java 支持多個線程同時訪問一個對象或者對象的成員變量,關(guān)鍵字 synchronized 可以修飾方法或者以同步塊的形式來進行使用,它主要確保多個線 程在同一個時刻,只能有一個線程處于方法或者同步塊中,它保證了線程對變量訪問的可見性和排他性,又稱為內(nèi)置鎖機制
對象鎖和類鎖: 對象鎖是用于對象實例方法,或者一個對象實例上的,類鎖是用于類的靜態(tài) 方法或者一個類的 class 對象上的。我們知道,類的對象實例可以有很多個,但 是每個類只有一個 class 對象,所以不同對象實例的對象鎖是互不干擾的,但是 每個類只有一個類鎖。

不加鎖,可能造成的現(xiàn)象,例如

public class SynTest {

    private long count =0;
    private Object obj = new Object();//作為一個鎖

    public long getCount() {
        return count;
    }

    public void setCount(long count) {
        this.count = count;
    }

    /*用在同步塊上*/
    public void incCount(){
//      synchronized (obj){
            count++;
//      }
    }

    /*用在方法上*/
    public synchronized void incCount2(){
            count++;
    }

    /*用在同步塊上,但是鎖的是當前類的對象實例*/
    public void incCount3(){
        synchronized (this){
            count++;
        }
    }

    //線程
    private static class Count extends Thread{

        private SynTest simplOper;

        public Count(SynTest simplOper) {
            this.simplOper = simplOper;
        }

        @Override
        public void run() {
            for(int i=0;i<10000;i++){
                simplOper.incCount();//count = count+10000
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SynTest simplOper = new SynTest();
        //啟動兩個線程
        Count count1 = new Count(simplOper);
        Count count2 = new Count(simplOper);
        count1.start();
        count2.start();
        Thread.sleep(50);
        System.out.println(simplOper.count);//20000
    }
}

啟動兩個線程,分別對simplOper對象中count值進行10000次count++操作,理論上我們最后輸出的數(shù)值應該為20000,但是實際運行結(jié)果中,只會偶爾輸出20000的結(jié)果。

加鎖以后,我們就會得到我們想要的值了

public class SynTest {

   private long count =0;
   private Object obj = new Object();//作為一個鎖

   public long getCount() {
       return count;
   }

   public void setCount(long count) {
       this.count = count;
   }

   /*用在同步塊上*/
   public void incCount(){
       synchronized (obj){
           count++;
       }
   }

   /*用在方法上*/
   public synchronized void incCount2(){
           count++;
   }

   /*用在同步塊上,但是鎖的是當前類的對象實例*/
   public void incCount3(){
       synchronized (this){
           count++;
       }
   }

   //線程
   private static class Count extends Thread{

       private SynTest simplOper;

       public Count(SynTest simplOper) {
           this.simplOper = simplOper;
       }

       @Override
       public void run() {
           for(int i=0;i<10000;i++){
               simplOper.incCount();//count = count+10000
           }
       }
   }

   public static void main(String[] args) throws InterruptedException {
       SynTest simplOper = new SynTest();
       //啟動兩個線程
       Count count1 = new Count(simplOper);
       Count count2 = new Count(simplOper);
       count1.start();
       count2.start();
       Thread.sleep(50);
       System.out.println(simplOper.count);//20000
   }
}

當然我們也可以使用incCount2方法或者incCount3方法,incCount2為同步方法,incCount3為對象鎖,都會達到加鎖的效果

錯誤的加鎖方式,例如

public class TestIntegerSyn {

    public static void main(String[] args) throws InterruptedException {
        Worker worker=new Worker(1);
        //Thread.sleep(50);
        for(int i=0;i<5;i++) {
            new Thread(worker).start();
        }
    }

    private static class Worker implements Runnable{

        private Integer i;
        private Object o = new Object();

        public Worker(Integer i) {
            this.i=i;
        }

        @Override
        public void run() {
            synchronized (i) {
                Thread thread=Thread.currentThread();
                System.out.println(thread.getName()+"--@"
                        +System.identityHashCode(i));
                i++;
                System.out.println(thread.getName()+"-------"+i+"-@"
                        +System.identityHashCode(i));
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(thread.getName()+"-------"+i+"--@"
                        +System.identityHashCode(i));
            }
        }
    }
}

輸出結(jié)果
Thread-0--@1096443612
Thread-0-------2-@1335726215
Thread-4--@1335726215
Thread-4-------3-@357858746
Thread-0-------3--@357858746
Thread-4-------3--@357858746
Thread-3--@357858746
Thread-3-------4-@1892057330
Thread-3-------4--@1892057330
Thread-2--@1892057330
Thread-2-------5-@1488708714
Thread-2-------5--@1488708714
Thread-1--@1488708714
Thread-1-------6-@390233048
Thread-1-------6--@390233048
從輸出結(jié)果看,Thread-3--@357858746的位置輸出的不對,可見我們加的鎖并沒有起到作用,原因是在執(zhí)行i++的過程中,i的對象已經(jīng)改變,我們可以反編譯一下class文件可以看到,每次執(zhí)行i++都是調(diào)用的Integer的valueOf方法,而valueOf方法是new 一個新的對象,鎖的對象發(fā)生了改變,這是一個錯誤的加鎖方式。

2.volatile 最輕量的同步機制
volatile 保證了不同線程對這個變量進行操作時的可見性,即一個線程修改了某個變量的值,這新值對其他線程來說是立即可見的,但是volatile不能保證操作的原子性。
volatile 最適用的場景:一個線程寫,多個線程讀

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容