欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java學(xué)習(xí)之線程同步與線程間通信詳解

 更新時(shí)間:2022年12月27日 16:25:43   作者:從未止步..  
這篇文章主要為大家詳細(xì)介紹了線程同步和線程之間的通信的相關(guān)知識,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Java有一定的幫助,感興趣的可以了解一下

線程同步的概念

由于同一個(gè)進(jìn)程的多個(gè)線程共享同一塊存儲空間,在帶來方便的同時(shí),也會帶來訪問沖突的問題:

舉例:

public class Runnable_test implements Runnable {//實(shí)現(xiàn)Runnable接口
private  int ticknumbers=10;

    @Override
    public void run() {
        while(true){
            if(ticknumbers<=0){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->拿到了第"+ticknumbers--+"票");//currentThread()監(jiān)測線程的狀態(tài)
        }
    }

    public static void main(String[] args) {
        Runnable_test runnable_test=new Runnable_test();
        new Thread(runnable_test,"小明").start();
        new Thread(runnable_test,"小黃").start();
        new Thread(runnable_test,"小紅").start();
    }
}

在輸出的數(shù)據(jù)中,顯然出現(xiàn)了,一張票同時(shí)被大于1人拿到的情況,這與我們的現(xiàn)實(shí)顯然不相符合。

為了解決此問題,Java 語言提供專門的機(jī)制來避免同一個(gè)對象被多個(gè)線程同時(shí)訪問,這個(gè)機(jī)制就是線程同步。

當(dāng)兩個(gè)或多個(gè)線程同時(shí)訪問同一個(gè)變量,并且有線程需要修改這個(gè)變量時(shí),就必須采用同步的機(jī)制對其進(jìn)行控制,否則就會出現(xiàn)邏輯錯(cuò)誤的運(yùn)行結(jié)果

造成上述這種錯(cuò)誤邏輯結(jié)果的原因是:可能有多個(gè)線程取得的是同一個(gè)值,各自修改并存入,從而造成修改慢的后執(zhí)行的線程把執(zhí)行快的線程的修改結(jié)果覆蓋掉了

因?yàn)榫€程在執(zhí)行過程中不同步,多個(gè)線程在訪問同一資源時(shí),需要進(jìn)行同步操作,被訪問的資源稱為共享資源。

同步的本質(zhì)是加鎖,Java 中的任何一個(gè)對象都有一把鎖以及和這個(gè)鎖對應(yīng)的等待隊(duì)列,當(dāng)線程要訪問共享資源時(shí),首先要對相關(guān)的對象進(jìn)行加鎖

如果加鎖成功,線程對象才能訪問共享資源并且在訪問結(jié)束后,要釋放鎖:如果加鎖不成功,那么線程進(jìn)入被加鎖對象對應(yīng)的是等待隊(duì)列。

Java用synchronized關(guān)鍵字給針對共享資源進(jìn)行操作的方法加鎖。每個(gè)鎖只有一把鑰匙,只有得到這把鑰匙之后才可以對被保護(hù)的資源進(jìn)行操作,而其他線程只能等待,直到拿到這把鑰匙。

實(shí)現(xiàn)同步的具體方式有同步代碼塊和同步方法兩種

同步代碼塊

使用 synchronized 關(guān)鍵字聲明的代碼塊稱為同步代碼塊。

在任意時(shí)刻,只能有一個(gè)線程訪問同步代碼塊中的代碼,所以同步代碼塊也稱為互斥代碼塊

同步代碼塊格式如下所示:

synchronized(同步對象){
//需要同步的代碼,對共享資源的訪問
}

synchronized關(guān)鍵字后面括號內(nèi)的對象就是被加載的對象,同步代碼塊要實(shí)現(xiàn)對共享資源的訪問

對上述實(shí)例進(jìn)行修改:

package Runnable;

public class Runnable_test implements Runnable {//實(shí)現(xiàn)Runnable接口
    private int ticknumbers = 20;
    private Object obj = new Object();//被加鎖的對象,同步對象

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (ticknumbers > 0) {
                    System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumbers-- + "票");//currentThread()監(jiān)測線程的狀態(tài)
                    try {
                        Thread.sleep(300);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else
                    break;
            }
        }
    }
}


class test{
    public static void main(String[] args) {
        Runnable_test runnable_test=new Runnable_test();
        new Thread(runnable_test,"小明").start();
        new Thread(runnable_test,"小黃").start();
        new Thread(runnable_test,"小紅").start();
    }
}

將票數(shù)產(chǎn)生變化的代碼塊修改為同步代碼塊:

修改過后輸出,我們發(fā)現(xiàn),并未出現(xiàn)同一張票,被第二個(gè)甚至第三個(gè)人拿到的情況:

小明-->拿到了第20票
小明-->拿到了第19票
小明-->拿到了第18票
小明-->拿到了第17票
小明-->拿到了第16票
小明-->拿到了第15票
小紅-->拿到了第14票
小紅-->拿到了第13票
小紅-->拿到了第12票
小黃-->拿到了第11票
小黃-->拿到了第10票
小黃-->拿到了第9票
小黃-->拿到了第8票
小紅-->拿到了第7票
小紅-->拿到了第6票
小紅-->拿到了第5票
小紅-->拿到了第4票
小紅-->拿到了第3票
小紅-->拿到了第2票
小紅-->拿到了第1票

在上面的修改中,僅僅是將需要互斥的代碼放人了同步塊中。此時(shí),在抽票的過程中通過給同一個(gè) obj對象加鎖來實(shí)現(xiàn)互斥,從而保證線程的同步執(zhí)行。

同步方法

synchronized關(guān)鍵字也可以出現(xiàn)在方法的聲明部分,該方法稱為同步方法

當(dāng)多個(gè)線程對象同時(shí)訪問共享資源時(shí),只有獲得鎖對象的線程才能進(jìn)入同步方法執(zhí)行,其他訪問共享資源的線程將會進(jìn)入鎖對象的等待隊(duì)列,執(zhí)行完同步方法的線程會釋放鎖。

[權(quán)限訪問限定]    synchronized 方法返回值 方法名稱(參數(shù)列表){
//.............需要同步的代碼,對共享資源的訪問
}

package Runnable;

public class Runnable_test implements Runnable {//實(shí)現(xiàn)Runnable接口
    private int ticknumbers = 20;
    @Override
    public  void run() {
        while (true) {
            if (ticknumbers > 0) {
                ticks();//調(diào)用同步方法
            }
        else
            break;
        }
    }
    
    //同步方法
    public synchronized void ticks(){
        if (ticknumbers > 0) {
            System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumbers-- + "票");
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

//測試類
class test{
    public static void main(String[] args) {
        Runnable_test runnable_test=new Runnable_test();
        new Thread(runnable_test,"小明").start();
        new Thread(runnable_test,"小黃").start();
        new Thread(runnable_test,"小紅").start();
    }
}

輸出:

小明-->拿到了第20票
小明-->拿到了第19票
小明-->拿到了第18票
小明-->拿到了第17票
小明-->拿到了第16票
小黃-->拿到了第15票
小黃-->拿到了第14票
小黃-->拿到了第13票
小黃-->拿到了第12票
小黃-->拿到了第11票
小黃-->拿到了第10票
小黃-->拿到了第9票
小紅-->拿到了第8票
小紅-->拿到了第7票
小紅-->拿到了第6票
小紅-->拿到了第5票
小紅-->拿到了第4票
小紅-->拿到了第3票
小紅-->拿到了第2票
小紅-->拿到了第1票

同步方法的本質(zhì)也是給對象加鎖,但是是給同步方法所在類的 this 對象加鎖,所以在上述實(shí)例中,我們就刪除了obj對象的定義。

package Runnable;

public class Runnable_test implements Runnable {//實(shí)現(xiàn)Runnable接口
    private int ticknumbers = 20;
    boolean tag = false;//設(shè)置此變量的作用是為了讓一個(gè)線程進(jìn)入同步塊,另一個(gè)線程進(jìn)入同步方法

    @Override
    public void run() {
        if(tag){
            while(true)
                ticks();
        }
        else{
            while (true) {
                synchronized (this) {
                    if (ticknumbers > 0) {
                        System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumbers-- + "票");//currentThread()監(jiān)測線程的狀態(tài)
                        try {
                            Thread.sleep(300);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    } else
                        break;
                }
            }
        }
    }

    //同步方法
    public synchronized void ticks() {
        if (ticknumbers > 0) {
            System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticknumbers-- + "票");
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        else
            return;
    }
}


    //測試類
    class test {
        public static void main(String[] args) throws InterruptedException {
            Runnable_test runnable_test = new Runnable_test();
            Thread thread1=new Thread(runnable_test, "小明");
            thread1.start();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            runnable_test.tag=true;
            Thread thread2=new Thread(runnable_test, "小黃");
            thread2.start();
        }
    }

輸出:

小明-->拿到了第20票
小明-->拿到了第19票
小明-->拿到了第18票
小明-->拿到了第17票
小明-->拿到了第16票
小明-->拿到了第15票
小明-->拿到了第14票
小明-->拿到了第13票
小明-->拿到了第12票
小明-->拿到了第11票
小明-->拿到了第10票
小明-->拿到了第9票
小明-->拿到了第8票
小明-->拿到了第7票
小黃-->拿到了第6票
小黃-->拿到了第5票
小黃-->拿到了第4票
小黃-->拿到了第3票
小黃-->拿到了第2票
小黃-->拿到了第1票

通過程序運(yùn)行結(jié)果可以看出:線程thread1執(zhí)行同步代碼塊,線程thread2執(zhí)行同步方法,兩個(gè)線程之間形成了同步。

由于同步代碼塊是給 this對象加鎖,所以表明同步方法也是給 this對象加鎖,否則,兩者之間不能形成同步。

注意:多線程的同步程序中,不同的線程對象必須給同一個(gè)對象加鎖,否則這些線程對象之間無法實(shí)現(xiàn)同步

線程組

線程組可以看作是包含了許多線程的對象集,它擁有一個(gè)名字以及一些相關(guān)的屬性,可以當(dāng)作一個(gè)組來管理其中的線程。

每個(gè)線程都是線程組的一個(gè)成員,線程組把多個(gè)線程集成一個(gè)對象,通過線程組可以同時(shí)對其中的多個(gè)線程進(jìn)行操作。在生成線程時(shí)必須將線程放到指定的線程組,也可以放在缺省的線程組中,缺省的就是生成該線程的線程所在的線程組。一旦一個(gè)線程加入了某個(gè)線程組,就不能被移出這個(gè)組。

java,lang包的ThreadGroup類表示線程組,在創(chuàng)建線程之前,可以創(chuàng)建一個(gè)ThreadGroup對象。

下面代碼是創(chuàng)建線程組并在其中加人兩個(gè)線程

ThreadGroup myThreadGroup = new ThreadGroup("a");    //創(chuàng)建線程組

//將下述兩個(gè)線程加入其中
Thread myThread1 = new Thread(myThreadGroup,"worker1");
Thread myThread2 = new Thread(myThreadGroup,"worker2");
myThread1.start();
myThread2.start();

線程組的相關(guān)方法

String getName();    //返回線程組的名字
ThreadGoup getParent();    //返回父線程
int tactiveCount();    //返回線程組中當(dāng)前激活的線程的數(shù)目,包括子線程組中的活動(dòng)線程
int enumerate(Thread list[])    //將所有線程組中激活的線程復(fù)制到一個(gè)線程數(shù)組中
void setMaxPriority(int pri)    //設(shè)置線程的最高優(yōu)先級,pri是該線程組的新優(yōu)先級
void interrupt()    //向線程組及其子組中的線程發(fā)送一個(gè)中斷信息
boolean isDaemon()    //判斷是否為Daemon線程組
boolean parentOf(ThreadGoup g)    //判斷線程組是否是線程g或g的子線程
toString()    //返回一個(gè)表示本線程組的字符串 

線程組對象的基本應(yīng)用

舉例:

package Runnable;

public class MyThreadgroup {
    public void test(){
        ThreadGroup threadGroup=new ThreadGroup("test");    //創(chuàng)建名為test的線程組
        Thread A=new Thread(threadGroup,"線程A");
        Thread B=new Thread(threadGroup,"線程B");
        Thread C=new Thread(threadGroup,"線程C");

        //為線程設(shè)置優(yōu)先級
        A.setPriority(6);
        C.setPriority(4);
        A.start();
        B.start();
        C.start();
        System.out.println("threadGroup正在進(jìn)行活動(dòng)的個(gè)數(shù):"+threadGroup.activeCount());
        System.out.println("線程A的優(yōu)先級:"+A.getPriority());
        System.out.println("線程B的優(yōu)先級:"+B.getPriority());
        System.out.println("線程C的優(yōu)先級:"+C.getPriority());
    }
}
class MyThreadgroup_test{
    public static void main(String[] args) {
       MyThreadgroup myThreadgroup=new MyThreadgroup();
       myThreadgroup.test();
    }
}

輸出:

threadGroup正在進(jìn)行活動(dòng)的個(gè)數(shù):3
線程A的優(yōu)先級:6
線程B的優(yōu)先級:5
線程C的優(yōu)先級:4

線程間的通信

某些情況下,多個(gè)線程之間需要相互配合來完成一件事情,這些線程之間就需要進(jìn)行通信”,把一方線程的執(zhí)行情況告訴給另一方線程。

“通信”的方法在 java.lang.Object類中定義了,我們可以通過“生產(chǎn)者-消費(fèi)者”模型來理解線程間的通信。

有兩個(gè)線程對象,其中一個(gè)是生產(chǎn)者,另一個(gè)是消費(fèi)者。生產(chǎn)者線程負(fù)責(zé)生產(chǎn)產(chǎn)品并放入產(chǎn)品緩沖區(qū),消費(fèi)者線程負(fù)責(zé)從產(chǎn)品緩沖區(qū)取出產(chǎn)品并消費(fèi)。

當(dāng)生產(chǎn)者線程獲得 CPU 使用權(quán)后:

先判斷產(chǎn)品緩沖區(qū)是否有產(chǎn)品,如果有產(chǎn)品就調(diào)用 wait()方法進(jìn)入產(chǎn)品緩沖區(qū)對象的等待隊(duì)列并釋放產(chǎn)品緩沖區(qū)對象的鎖;如果發(fā)現(xiàn)產(chǎn)品緩沖區(qū)中沒有產(chǎn)品,就生產(chǎn)產(chǎn)品并放入緩沖區(qū)并調(diào)用notify()方法發(fā)送通知給消費(fèi)者線程。

當(dāng)消費(fèi)者線程獲得CPU使用權(quán)后:

先判斷產(chǎn)品緩沖區(qū)是否有產(chǎn)品,如果有產(chǎn)品就拿出來消費(fèi)并調(diào)用 notify()方法發(fā)送通知給生產(chǎn)者線程;如果發(fā)現(xiàn)產(chǎn)品緩沖區(qū)中沒有產(chǎn)品,調(diào)用 wait()方法進(jìn)入產(chǎn)品緩沖區(qū)對象的等待隊(duì)列并釋放產(chǎn)品緩沖區(qū)對象的鎖。

注意:線程間通信是建立在線程同步基礎(chǔ)上的,所以wait()notify()和notifyAll()方法的調(diào)用要出現(xiàn)在同步代碼塊或同步方法中

線程通信簡單應(yīng)用

package Runnable;


 class Box {//產(chǎn)品緩沖區(qū)
    public String name="蘋果";//表示產(chǎn)品的名稱
    public boolean isFull=true;//表示當(dāng)前緩沖區(qū)中是否有產(chǎn)品
}



//定義消費(fèi)者類
class Cossumer implements Runnable {
    Box box;

    Cossumer(Box box) {
        this.box = box;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (box) {//對產(chǎn)品緩沖區(qū)對象加鎖
                if (box.isFull == true) //緩沖區(qū)中有產(chǎn)品
                {
                    System.out.println("消費(fèi)者拿出----:" + box.name);
                    box.isFull = false;//設(shè)置緩沖區(qū)中產(chǎn)品為空
                    box.notify();//發(fā)送通知給生產(chǎn)者線程對象
                } else {
                    try {
                        //消費(fèi)者線程進(jìn)入產(chǎn)品緩沖區(qū)的等待隊(duì)列并釋放鎖
                        box.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}



    //生產(chǎn)者類
    class product implements Runnable{
        Box box;
        int Count=0;

        public product(Box box) {
            this.box=box;
        }

        @Override
        public void run() {
            while(true){
                synchronized (box)//對產(chǎn)品緩沖區(qū)對象加鎖
                {
                    if(box.isFull==true)//緩沖區(qū)中有產(chǎn)品
                    {
                        try {
                            box.wait();//生產(chǎn)者線程進(jìn)入等待隊(duì)列并釋放鎖
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else {
                        if (Count == 0) {
                            box.name = "香蕉";
                            System.out.println("生產(chǎn)者放入+++++:" + box.name);
                        } else {
                            box.name = "蘋果";
                            System.out.println("生產(chǎn)者放入+++++:" + box.name);
                        }
                        Count=(Count+1)%2;
                        box.isFull=true;//設(shè)置緩沖區(qū)中有產(chǎn)品
                        box.notify();//發(fā)送通知給消費(fèi)者線程對象
                    }
                }
            }
        }
    }


class box_test{
    public static void main(String[] args) {
        Box box=new Box();//創(chuàng)建產(chǎn)品緩沖區(qū)對象
        product product=new product(box);

        Cossumer cossumer=new Cossumer(box);//生產(chǎn)者和消費(fèi)者對象要共享同一個(gè)產(chǎn)品緩沖區(qū)
        Thread thread1=new Thread(product);//創(chuàng)建生產(chǎn)者線程對象
        Thread thread2=new Thread(cossumer);//創(chuàng)建消費(fèi)者線程對象
        thread1.start();//啟動(dòng)生產(chǎn)者線程對象
        thread2.start();//啟動(dòng)消費(fèi)者線程對象
    }
}

輸出:

消費(fèi)者拿出----:香蕉
生產(chǎn)者放入+++++:蘋果
消費(fèi)者拿出----:蘋果
生產(chǎn)者放入+++++:香蕉
消費(fèi)者拿出----:香蕉
生產(chǎn)者放入+++++:蘋果
消費(fèi)者拿出----:蘋果
生產(chǎn)者放入+++++:香蕉
消費(fèi)者拿出----:香蕉
生產(chǎn)者放入+++++:蘋果
消費(fèi)者拿出----:蘋果
生產(chǎn)者放入+++++:香蕉

從運(yùn)行結(jié)果可以看出:生產(chǎn)者線程向緩沖區(qū)放入什么產(chǎn)品,消費(fèi)者就從緩沖區(qū)中取出什么產(chǎn)品,生產(chǎn)者生產(chǎn)一個(gè)產(chǎn)品,消費(fèi)者就消費(fèi)一個(gè)產(chǎn)品,兩者之間實(shí)現(xiàn)了通信.

以上就是Java學(xué)習(xí)之線程同步與線程間通信詳解的詳細(xì)內(nèi)容,更多關(guān)于Java線程的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺析Java中的WeakHashMap

    淺析Java中的WeakHashMap

    這篇文章主要介紹了淺析Java中的WeakHashMap,WeakHashMap其實(shí)和HashMap大多數(shù)行為是一樣的,只是WeakHashMap不會阻止GC回收key對象,那么WeakHashMap是怎么做到的呢,這就是我們研究的主要問題,需要的朋友可以參考下
    2023-09-09
  • SpringBoot Druid配置過程圖解

    SpringBoot Druid配置過程圖解

    這篇文章主要介紹了SpringBoot Druid配置過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 如何解決java:錯(cuò)誤:無效的源發(fā)行版:17問題

    如何解決java:錯(cuò)誤:無效的源發(fā)行版:17問題

    這篇文章主要介紹了如何解決java:錯(cuò)誤:無效的源發(fā)行版:17問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • 一篇文章帶你了解Maven的繼承和聚合

    一篇文章帶你了解Maven的繼承和聚合

    這篇文章主要為大家介紹了Maven的繼承和聚合,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • java編寫的文件管理器代碼分享

    java編寫的文件管理器代碼分享

    本文給大家分享的是一則使用java編寫的文件管理器的代碼,新人練手的作品,邏輯上還是有點(diǎn)小問題,大家?guī)兔纯窗伞?/div> 2015-04-04
  • Java 自動(dòng)安裝校驗(yàn)TLS/SSL證書

    Java 自動(dòng)安裝校驗(yàn)TLS/SSL證書

    這篇文章主要介紹了Java 自動(dòng)安裝校驗(yàn)TLS/SSL證書的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-10-10
  • java:抽象類與模板方法模式詳解

    java:抽象類與模板方法模式詳解

    這篇文章主要介紹了Java抽象類的構(gòu)造模板模式用法,結(jié)合實(shí)例形式分析了java使用抽象類構(gòu)造模板模式相關(guān)操作技巧,需要的朋友可以參考下
    2021-09-09
  • java 數(shù)據(jù)庫連接與增刪改查操作實(shí)例詳解

    java 數(shù)據(jù)庫連接與增刪改查操作實(shí)例詳解

    這篇文章主要介紹了java 數(shù)據(jù)庫連接與增刪改查操作,結(jié)合實(shí)例形式詳細(xì)分析了java使用jdbc進(jìn)行數(shù)據(jù)庫連接及增刪改查等相關(guān)操作實(shí)現(xiàn)技巧與注意事項(xiàng),需要的朋友可以參考下
    2019-11-11
  • 關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式

    關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式

    這篇文章主要介紹了關(guān)于Java整合RabbitMQ實(shí)現(xiàn)生產(chǎn)消費(fèi)的7種通訊方式,消息中間件是基于隊(duì)列與消息傳遞技術(shù),在網(wǎng)絡(luò)環(huán)境中為應(yīng)用系統(tǒng)提供同步或異步、可靠的消息傳輸?shù)闹涡攒浖到y(tǒng),需要的朋友可以參考下
    2023-05-05
  • java并發(fā)編程_線程池的使用方法(詳解)

    java并發(fā)編程_線程池的使用方法(詳解)

    下面小編就為大家?guī)硪黄猨ava并發(fā)編程_線程池的使用方法(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-05-05

最新評論