1. 程式人生 > >Java程序-同步與非同步

Java程序-同步與非同步

1、synchronized修飾程式碼塊

1、兩個併發的執行緒訪問同一個物件中的synchronized(this)同步程式碼塊時,同一時間內只有一個執行緒執行,另外一個執行緒需要等到當前執行緒結束之後才能執行。

package MapTest;

public class Thread1 implements Runnable{
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    /**
     * 一旦一個物件實現了runable介面,那麼就意味著建立了一個執行緒,啟動執行緒的時候該物件的run()方法
     * 也就被啟動
     */
    @Override
    public void run() {
       synchronized (this){
           for(int i = 0; i < 10; i++){
               System.out.println(Thread.currentThread().getName() + "===" + i);
           }
       }
    }

    public static void main(String[] args){
        //例項化
        Thread1 thread1 = new Thread1();
        //建立兩個執行緒
        Thread a = new Thread(thread1,"A");
        Thread b = new Thread(thread1,"B");
        //先啟動b執行緒,再啟動a執行緒
        b.start();
        a.start();
    }
    /**
     * A===0
     A===1
     A===2
     A===3
     A===4
     A===5
     A===6
     A===7
     A===8
     A===9
     B===0
     B===1
     B===2
     B===3
     B===4
     B===5
     B===6
     B===7
     B===8
     B===9
     */


}

2、當一個執行緒訪問物件中的同步程式碼塊(synchronized(this)),另外一個執行緒仍然可以訪問該物件中的非同步程式碼塊。

package MapTest;

public class Thread2 {

    //synchronized程式碼塊
    public void fun1() throws InterruptedException {
        synchronized (this) {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "==" + i);
                Thread.sleep(100);
            }
        }
    }

    //非synchronized程式碼塊
    public void fun2() throws InterruptedException {
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + "==" + i);
            Thread.sleep(100);
        }
    }
    
    public static void main(String[] args){
        //例項化一個物件
        Thread2 thread2 = new Thread2();

        //建立一個程序a
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.fun1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"a");

        //建立一個程序b
        //因為該物件沒有繼承runable類,所以例項化該物件的時候,建立執行緒並不會call裡面的方法
        //所以需要new一個runable(),通過物件來呼叫方法(因為是靜態方法呼叫非靜態方法)
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    thread2.fun2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"b");

        a.start();
        b.start();
    }

    /**
     * a==0
     b==0
     b==1
     a==1
     a==2
     b==2
     a==3
     b==3
     b==4
     a==4
     */
}

3、當一個執行緒訪問物件的同步程式碼塊(synchronized(this)),另外一個執行緒對該物件中的其他同步程式碼塊(synchronized(this))的訪問將被阻塞。

    //非synchronized程式碼塊===》改成synchronized程式碼塊
    public void fun2() throws InterruptedException {
        synchronized (this) {
            for(int i = 0; i < 5; i++){
                System.out.println(Thread.currentThread().getName() + "==" + i);
                Thread.sleep(100);
            }
        }
    }
b==0
b==1
b==2
b==3
b==4
a==0
a==1
a==2
a==3
a==4
需要注意的是哪個執行緒先被執行,不是看執行緒呼叫start方法的順序,也不是建立執行緒的順序。而是由CPU自動隨機呼叫哪個執行緒先執行,當然如果針對一個執行緒組得話,就有先後順序了,如下面得存錢和取前得操作

4、存錢和取錢的操作

account物件

package MapTest;

public class Account {
    private String name;
    private float amount;

    public Account(String name, float amount) {
        this.name = name;
        this.amount = amount;
    }

    //存錢
    public void deposit(float amt) throws InterruptedException {
        amount += amt;
        Thread.sleep(1000);
    }

    //取錢
    public void withdraw(float amt) throws InterruptedException {
        amount -= amt;
        Thread.sleep(1000);
    }

    public float show(){
        return amount;
    }

}

賬戶操作物件類

package MapTest;

public class AccountOperator implements Runnable {

    private Account account;

    public AccountOperator(Account account) {
        this.account = account;
    }

    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see Thread#run()
     */
    @Override
    public void run() {
        try {
            synchronized (this) {
                account.deposit(1000);
                account.withdraw(100);
                System.out.println(Thread.currentThread().getName() + "==" + account.show());
            }
            /**
             * thread1==1400.0
             thread4==2300.0
             thread3==3200.0
             thread2==4100.0
             thread0==5000.0
             */
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }
}

建立執行緒組操作

package MapTest;

public class AccountTest {

    public static void main(String[] args){
        //例項化一個賬戶
        Account zs = new Account("zs",500);
        //建立一個賬戶操作物件
        AccountOperator zsOperator = new AccountOperator(zs);

        //建立五個執行緒來操作該賬戶
        final int THREAD_NUM = 5;
        //建立一個執行緒組
        Thread[] threads = new Thread[THREAD_NUM];
        for(int i = 0; i < THREAD_NUM; i++){
            //利用賬戶操作物件建立執行緒
            threads[i] = new Thread(zsOperator,"thread" + i);
            threads[i].start();
        }
    }
}

最後列印的結果如下

thread0==1400.0
thread3==2300.0
thread4==3200.0
thread2==4100.0
thread1==5000.0
可見每個執行緒都是在前一個執行緒所有操作結束的基礎之上才進行操作,也就是說我每次存1000取500的操作都是原子性的。都必須處理完才能進行第二次操作。因為這裡的存和取得操作都屬於同步程式碼塊synchronized,用官方一點得說法就是說一個執行緒訪問該物件得同步程式碼塊時,其他程序對該程式碼塊得訪問將被阻塞。

2、synchronized修飾方法

1、修飾非靜態方法

package MapTest;

public class SynFun {
    //建立一個synchronized修飾得方法
    public synchronized void fun() throws InterruptedException {
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + "==" + i);
            Thread.sleep(1000);
        }
    }
}
package MapTest;

public class TestFun {
    public static void main(String[] args){

        SynFun synFun = new SynFun();
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A");

        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B");

        b.start();
        a.start();

    }
    /**
     * A==0
     A==1
     A==2
     A==3
     A==4
     B==0
     B==1
     B==2
     B==3
     B==4
    這裡和同步程式碼塊得意思差不多
     */
}

對於非靜態方法,實際上是對該方法得物件加鎖,也就是“物件鎖”。也就是說同一個物件中所有得非靜態方法共用一把物件鎖,不同得程序不能同時訪問該物件中得所有非靜態方法。這時候訪問該物件靜態方法得所有程序是互斥得。其實和程式碼塊也差不多。只有在兩個程序訪問不同物件中的非靜態方法時,才不是互斥的,因為這時有兩把物件鎖。

2、修飾靜態方法

package MapTest;

public class SynFun {
    //建立一個synchronized修飾得方法
    public synchronized static void  fun1() throws InterruptedException {
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + "=靜態=" + i);
            Thread.sleep(1000);
        }
    }

    //建立一個synchronized修飾得方法
    public synchronized  void  fun2() throws InterruptedException {
        for(int i = 0; i < 5; i++){
            System.out.println(Thread.currentThread().getName() + "=非靜態=" + i);
            Thread.sleep(1000);
        }
    }
}
package MapTest;

public class TestFun {
    public static void main(String[] args){

        SynFun synFun = new SynFun();
        //訪問靜態方法得程序A1
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A1");

        //訪問靜態方法得程序A2
        Thread b = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "A2");

        //訪問非靜態方法得程序B1
        Thread c = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B1");

        //訪問非靜態方法得程序B2
        Thread d = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    synFun.fun2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "B2");

        b.start();
        a.start();
        c.start();
        d.start();

    }
    /**
     *A2=靜態=0
     B2=非靜態=0
     B2=非靜態=1
     A2=靜態=1
     A2=靜態=2
     B2=非靜態=2
     A2=靜態=3
     B2=非靜態=3
     A2=靜態=4
     B2=非靜態=4
     A1=靜態=0
     B1=非靜態=0
     A1=靜態=1
     B1=非靜態=1
     B1=非靜態=2
     A1=靜態=2
     A1=靜態=3
     B1=非靜態=3
     B1=非靜態=4
     A1=靜態=4
     */
}
可見,不同的程序在訪問同一個物件中得靜態與非靜態方法之間不是互斥的,因為一個使用的時物件鎖,一個使用的是類物件鎖。不同的程序在訪問同一個物件的非靜態方法是互斥的,因為synchronized修飾靜態方法實際上是對類物件枷鎖。也就是永遠只有一把鎖,因為一個物件只對應一個類物件(.class)。

相關推薦

Java程序-同步非同步

1、synchronized修飾程式碼塊1、兩個併發的執行緒訪問同一個物件中的synchronized(this)同步程式碼塊時,同一時間內只有一個執行緒執行,另外一個執行緒需要等到當前執行緒結束之後才能執行。package MapTest; public class Th

java基礎 - 同步非同步的區別

同步:傳送一個請求,等待返回,然後再發送下一個請求 非同步:傳送一個請求,不等待返回,隨時可以再發送下一個請求 同步可以避免出現死鎖,讀髒資料的發生,一般共享某一資源的時候用,如果每個人都有修改許可權,同時修改一個檔案,有可能使一個人讀取另一個人已經刪除的內容,就會出錯,同步就會按順

JAVA NIO 同步非同步區別

什麼是非阻塞?(為什麼我沒有說什麼是IO,既然你都學到NIO了,,,要是不知道什麼是IO的話我也沒辦法咯..) 這篇文章也是簡單介紹NIO,想要看各類原始碼的同學可以繞道了- - 1 ) 非同步非阻塞例子:(網上看到的比較短小精悍的好例子,直接拿過來了) 老張愛喝茶,廢話

程序同步非同步

首先我們要清楚程序的同步與非同步說的是程序間的執行關係,而不是程序的執行的執行狀態(阻塞與非阻塞)。 我們所謂的程序同步是說在某些地方,多個併發程序需要相互等待或交換資訊而產生的制約關係,併發程序之間不是相互排斥臨界資源,而是相互依賴的關係。確切說,同步關係就是前一個程序的輸出作為後一個程序的輸

CIL鎖,GIL執行緒池的區別,程序池和執行緒池,同步非同步

一.GIL鎖 什麼是GIL? 全域性直譯器鎖,是加在直譯器上的互斥鎖 GC是python自帶的記憶體管理機制,GC的工作原理:python中的記憶體管理使用的是應用計數,每個數會被加上一個整型的計數器,表示這個資料被引用的次數,當這個整數變為0時則表示該資料已經沒有人使用,成為了垃圾資料,當記憶體佔用達到

java同步非同步

Java同步與非同步 一、關鍵字:  thread(執行緒)、thread-safe(執行緒安全)、intercurrent(併發的)  synchronized(同步的)、asynchronized(非同步的)、  volatile(易變的)、atomic(原子的)、sh

java中的同步非同步(轉)

經常看到介紹 ArrayList 和HashMap是非同步,Vector和HashTable是同步,這裡同步是執行緒安全的,非同步不是執行緒安全的,舉例說明: 當建立一個Vector物件時候, Vector ve=new Vector(); ve.a

js單執行緒java多執行緒、同步非同步

       寫這篇部落格源於想對比一下單執行緒js和多執行緒java兩種語言的區別。       定義區:            單執行緒:只能執行一個任務,只有在完成執行後,才能繼續執行其他的任務。            多執行緒:有多個執行緒,可以同時執行多個任務。

java中的同步非同步

選擇器(Selector)的作用是:將通道感興趣的事件放入佇列中,而不是馬上提交給應用程式,等已註冊的通道自己來請求處理這些事件。換句話說,就是選擇器將會隨時報告已經準備好了的通道,而且是按照先進先出的順序。那麼,選擇器是通過什麼來報告的呢?選擇鍵(SelectionKey)。選擇鍵的作用就是表明哪個通道已經

Java多執行緒基礎之物件鎖的同步非同步

同步:synchronized 同步的概念就是共享,如果不是共享的資源,就沒有必要進行同步。 非同步:asynchronized 非同步的概念就是獨立,相互之間不受到任何制約。 同步的目的就是為了執行緒安全,對於

Java的阻塞非同步模型

From Blog:Java——BIO、NIO、AIO 執行緒會阻塞在什麼地方?呼叫讀取檔案的API從硬碟上讀取檔案時,通過網路訪問API從網路上請求資源時,等待獲取到需要的資料以後再進行後續的處理。那麼我們應該怎樣線上程阻塞的時候讓這個執行緒去幹別的事呢,然後等資料來了以後再進行後續的

socket阻塞非阻塞 同步非同步 I/O模型

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

socket阻塞非阻塞,同步非同步、I/O模型(轉載只為查閱方便,若有侵權,立刪)

socket阻塞與非阻塞,同步與非同步 作者:huangguisu     1. 概念理解        在進行網路程式設計時,我們常常見到同步(Sync)/非同步(Async),阻塞(Block)/非阻塞(Unbl

20172306 2018-2019-2 《Java程序設計數據結構》第八周學習總結

class 平衡 each 插入 二叉 情況下 mov com 插入元素 20172306 2018-2019-2 《Java程序設計與數據結構》第八周學習總結 https://img2018.cnblogs.com/blog/1333004/201811/1333004-

經典程序同步互斥習題總結

基礎知識導引 臨界資源       在作業系統中,程序是佔有資源的最小單位(執行緒可以訪問其所在程序內的所有資源,但執行緒本身並不佔有資源或僅僅佔有一點必須資源)。但對於某些資源來說,其在同一時間只能被一個程序所佔用。這些一次只能被一個程序所佔用的資源就是所謂的臨界

程序同步互斥

1.概念 程序同步概念:亦稱直接制約關係,它是指為完成某種任務而建立的兩個或多個程序,這些程序因為需要在某些位置上協調它們的工作次序而產生的制約關係。程序間的直接制約關係就是源於它們之間的相互合作。 程序互斥概念:對臨界資源的訪問,需要互斥地進行。即同一個時間段內只能允許一個程序訪問該資源。 程序互斥的

有關程序同步互斥的經典問題

1生產者消費者問題 1.1使用二元訊號量解決無限緩衝區的生產者消費者問題 //使用二元訊號量解決無限緩衝區的生產者消費者問題 int count = 0; //count為緩衝區中的資料項個數 BinSem s = 1, delay = 0; //s為二元訊號量,控制生產者和消費

圖解阻塞非阻塞&同步非同步

一、阻塞I/O模型 二、非阻塞I/O模型 三、非同步I/O模型 四、同步I/O模型                    

Linux下阻塞非阻塞,同步非同步的關係及IO模型

一、阻塞與非阻塞,同步與非同步的關係 1、同步   同步,就是在發出一個功能呼叫時,在沒有得到結果之前,該呼叫就不返回。也就是說事情必須一件一件地做,等前一件做完了才能做下一件事。 2、非同步   非同步,就是在發出一個功能呼叫時,呼叫者不會立刻得到結果。實際處理這個呼叫的部

阻塞非阻塞,同步非同步

Java的IO有三種:IO、NIO、AIO 第一種是Java剛開始就出現的IO即BIO(同步阻塞IO),我們一般常說的IO就是這種IO,這種IO相對來說比較簡單,基於流模型,提供一種IO操作。互動方式是同步、阻塞,呼叫的順序是線性順序,這種IO有一個好處就是,程式碼簡單,但是缺點就是IO的效率較