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

Java多線程知識(shí)點(diǎn)全面總結(jié)

 更新時(shí)間:2022年04月06日 17:22:41   作者:WG_tomorrow  
這篇文章主要介紹了Java多線程知識(shí)點(diǎn)全面總結(jié),具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

Java多線程知識(shí)點(diǎn)總結(jié)

(1)什么是進(jìn)程?什么是線程?

進(jìn)程:

是并發(fā)執(zhí)行程序在執(zhí)行過程中分配和管理資源的基本單位,當(dāng)程序進(jìn)入內(nèi)存圓形時(shí)即為線程

進(jìn)程三大特點(diǎn):

  • 獨(dú)立性: 進(jìn)程是系統(tǒng)中獨(dú)立存在的實(shí)體,它可以獨(dú)立擁有資源,每個(gè)進(jìn)程都有自己的獨(dú)立空間。
  • 動(dòng)態(tài)性: 進(jìn)程和程序的區(qū)別在于進(jìn)程動(dòng)態(tài)的,進(jìn)程具有自己的生命周期
  • 并發(fā)性:多個(gè)進(jìn)程可以在單個(gè)處理器上并發(fā)執(zhí)行互不影響

并行和并發(fā):

  • 并行是指同一時(shí)刻,多個(gè)命令同時(shí)執(zhí)行;
  • 并發(fā)是指在同一時(shí)刻,只有一條命令是在處理器上執(zhí)行的,但多個(gè)進(jìn)程命令被快速輪換執(zhí)行,使得在宏觀上具有多個(gè)進(jìn)程同時(shí)執(zhí)行的效果。

線程:

  • 線程(Thread)被稱為輕量級(jí)進(jìn)程,線程是進(jìn)程的執(zhí)行單元
  • 線程是進(jìn)程的組成部分,一個(gè)進(jìn)程可以擁有多個(gè)線程,而一個(gè)線程必須擁有一個(gè)父進(jìn)程。線程可以擁有堆棧,局部變量…但不能擁有系統(tǒng)資源,它與父進(jìn)程的其他線程共享該進(jìn)程的所有資源
  • 一個(gè)線程可與其父進(jìn)程的其他線程共同擁有父進(jìn)程共享變量和部分環(huán)境,共同非合作完成任務(wù)。
  • 可以簡便理解一個(gè)Class類執(zhí)行時(shí)就是一個(gè)JVM的進(jìn)程,其中的成員屬性是子線程共享資源,main方法是主線程,其他方法是子線程,子線程(方法)相互合作共同完成Class任務(wù)。
  • 線程是獨(dú)立運(yùn)行的,線程的執(zhí)行是搶占式的,也就是說執(zhí)行的線程可能被掛起,以便運(yùn)行另一個(gè)線程。
  • 一個(gè)進(jìn)程可以創(chuàng)建或者撤銷另一個(gè)線程,一個(gè)進(jìn)程中的線程是并發(fā)執(zhí)行的。

(2)多線程的運(yùn)行狀態(tài)

(1)線程一般具有5種基本狀態(tài) :

創(chuàng)建, 就緒, 運(yùn)行, 阻塞, 終止

在這里插入圖片描述

(2)

創(chuàng)建狀態(tài):在程序中

(3)線程的創(chuàng)建和使用

(1)線程的創(chuàng)建

在Java 中如果要想實(shí)現(xiàn)多線程,那么必須依靠線程的主題類,但這個(gè)線程主題類在定義的時(shí)候,也需要一類特殊的要求,這個(gè)類可以繼承 Thread ,實(shí)現(xiàn)Runnable 接口或者實(shí)現(xiàn) Callable 接口來完成定義。任何一個(gè)類只要繼承了 Thread 類就可以成為一個(gè)類的主類,同時(shí)線程中需要覆寫父類終點(diǎn) run( )方法。

(2)線程的使用

public class JavaDemoA {
    public static void main(String[] args) {
        new MyThread("線程A:").start();
        new MyThread("線程B:").start();
        new MyThread("線程C:").start();
    }
}
class MyThread extends Thread{ //線程的主體類
    private String title;
    public MyThread(String title){
        this.title=title;
    }
    @Override
    public void run() {
        for(int i=1;i<=4;i++){
            System.out.println(this.title+i);
        }
    }
}

(4)Runnable 接口實(shí)現(xiàn)多線程

Thread是 Runnable 的子類,繼承了 Runnable 的接口,Thread 類的確可以方便實(shí)現(xiàn)多線程,但這種方式最大的缺陷就是單繼承局限性

Runnable 接口從 JDK1.8 開始成為一個(gè)函數(shù)接口,可以直接利用lambda表達(dá)式來實(shí)現(xiàn)線程主體代碼,同時(shí)在該接口中提供有run()方法進(jìn)行線程功能定義

Thread構(gòu)造方法:public Thread(Runnable target).

在Thread類中會(huì)保存有target屬性,該屬性保存的是Runnable的核心業(yè)務(wù)主體對象

當(dāng)Thread.start() 方法啟動(dòng)多線程時(shí)也會(huì)調(diào)用Thread.run() 方法,而在 Thread.run() 會(huì)判斷是否提供有target實(shí)例,如果提供則調(diào)用真實(shí)主體 

public class JavaDemoB {
    public static void main(String[] args) {
        new Thread(new MyThreadB("線程A:")).start();
        new Thread(new MyThreadB("線程B:")).start();
        new Thread(new MyThreadB("線程C:")).start();
    }
}
class MyThreadB implements Runnable{
    private String title;
    public MyThreadB(String title){
        this.title=title;
    }
    @Override
    public void run() {
        for(int i=1;i<=5;i++){
            System.out.println(title+i);
  加粗樣式      }
    }
}

(5)Callable接口實(shí)現(xiàn)多線程

Runnable 接口實(shí)例化多線程可以避免單繼承問題,但 Runnable 中的run() 方法不能返回操作結(jié)果,為啦解決這樣的問題,從JDK1.5開始對于多線程的實(shí)現(xiàn)提供了一個(gè)新的接口 java.util.concurrent.Callable

Callable接口在定義時(shí)需要定義泛型。

通過FutureTask 實(shí)現(xiàn)Callable接口和Thread類的聯(lián)系,并且通過FutureTask類獲取Callable接口中call()方法的返回值。 

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class JavaDemoC {
    public static void main(String[] args) throws Exception{
        FutureTask<String> task=new FutureTask<>(new MyThreadC());
        new Thread(task).start();
        System.out.println(task.get());
    }
}
class MyThreadC implements Callable<String> {
    @Override
    public String call() throws Exception {
        for(int i=1;i<=5;i++){
            System.out.println("線程執(zhí)行:i="+i);
        }
        return "Callable實(shí)現(xiàn)多線程";
    }
}

多線程常用操作方法

(1)線程的命名和取得

public Thread (Runnable target,String name) 構(gòu)造實(shí)例化線程對象,接受 Runnable 接口子類對象,同時(shí)設(shè)置線程名稱;

public final void setName(String name) 普通 設(shè)置線程名稱

public final String getName() 取得線程名字

每當(dāng)實(shí)例化Thread類對象時(shí),都會(huì)調(diào)用init()方法,并且在沒有為線程命名時(shí),自動(dòng)命名。 

package ThreadTest;
public class JavaDemoD {
    public static void main(String[] args) {
        MyThreadD myThread=new MyThreadD();
        new Thread(myThread,"線程A").start();//線程手動(dòng)命名
        new Thread(myThread).start();//線程自動(dòng)命名
        new Thread(myThread,"線程B").start();
    }
}
class MyThreadD implements Runnable{
    @Override
    public void run() {
        //獲取當(dāng)前線程名稱
        System.out.println(Thread.currentThread().getName());
    }
}

(2)線程休眠

線程有時(shí)候需要減緩執(zhí)行速度,所以Thread 類提供啦sleep()方法

public static void sleep(long millis) throws InterruptedException設(shè)置線程毫秒數(shù),時(shí)間一到自動(dòng)喚醒

public static void sleep(long millis,int nanos) throws InterruptedException設(shè)置線程毫秒數(shù),納秒數(shù),時(shí)間一到自動(dòng)喚醒 

public class JavaDemoE {
    public static void main(String[] args){
        Runnable runnable=(()->{//Runnable接口實(shí)例
           for(int i=1;i<=5;i++){
               System.out.println(Thread.currentThread().getName()+i);
               try {
                   Thread.sleep(2000);//強(qiáng)制讓線程休眠2秒
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        new Thread(runnable,"線程A:").start();
        new Thread(runnable,"線程B:").start();
    }
    //本程序兩個(gè)線程對象,每一個(gè)線程對象執(zhí)行時(shí)都要休眠一秒,因?yàn)槎嗑€程的啟動(dòng)和執(zhí)行都是有操作系統(tǒng),
    //隨機(jī)分配,雖然看起來A,B線程同時(shí)休眠,但也有先后順序
}

(3)線程中斷

Thread 中提供的線程方法很多都會(huì)拋出InterruptedException中斷異常,所以線程在執(zhí)行過程中可以被另一個(gè)線程中斷。

public boolean isInterrupted( ) 普通方法 判斷線程是否被中斷

public void interrupt( ) 普通 中斷線程執(zhí)行 

public class JavaDemoF {
    public static void main(String[] args) throws InterruptedException {
        Thread thread=new Thread(()->{
            System.out.println("線程累了想休眠10S");
            try {
                Thread.sleep(10000);
                System.out.println("時(shí)間到,自然醒來");
            } catch (InterruptedException e) {
                System.out.println("被強(qiáng)制喚醒,繼續(xù)工作");
            }
        });
        thread.start();//線程執(zhí)行啟動(dòng)
        Thread.sleep(2000);//先讓子線程運(yùn)行2秒
        if(!thread.isInterrupted()){//如果線程沒有中斷
            System.out.println("讓線程終止休眠");
            thread.interrupt();//線程中斷
        }
    }
}

(4)線程強(qiáng)制執(zhí)行

在多線程并發(fā)執(zhí)行中每個(gè)線程對象都會(huì)交替執(zhí)行,如果某個(gè)線程對象需要優(yōu)先完成執(zhí)行,可以使用 join ()方法強(qiáng)制執(zhí)行,待其執(zhí)行完畢后其他線程才會(huì)繼續(xù)執(zhí)行

public final void join() throws InterruptedException

public class JavaDemoG {
    public static void main(String[] args) {
        Thread threadB=new Thread(new MyThreadH(),"線程B");
        Runnable runnable=(()->{
           for(int i=1;i<=9;i++){
               System.out.println(Thread.currentThread().getName()+i);
               if(i==5){
                   try {
                       threadB.join();//如果i=5,強(qiáng)制執(zhí)行線程B
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
        });
        new Thread(runnable,"線程A").start();
        threadB.start();
    }
}
class MyThreadH implements Runnable{
    @Override
    public void run() {
        for(int i=1;i<=5;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}

(5)線程禮讓

多線程在彼此交替執(zhí)行時(shí)往往需要進(jìn)行資源輪流占用,如果某些不是很重要的線程搶占到資源但又不著急執(zhí)行時(shí)就可以暫時(shí)禮讓出去,讓其他線程先執(zhí)行。

public static void yield( ) 線程禮讓

public class JavaDemoH {
    public static void main(String[] args) {
        Runnable runnable=(()->{//Lambda實(shí)例化線程類對象,方便演示
            for(int j=1;j<=15;j++){
                System.out.println(Thread.currentThread().getName()+j);
            }
        });
        new Thread(new MyThreadI(),"禮讓線程A").start();
        new Thread(runnable,"非禮讓線程B:").start();
        new Thread(runnable,"非禮讓線程C:").start();
        new Thread(runnable,"非禮讓線程D:").start();
    }
}
class MyThreadI implements Runnable{
    @Override
    public void run() {
        for(int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+i);
            if(i%3==0){
                Thread.yield();//線程禮讓
            }
        }
    }
}

(6)線程優(yōu)先級(jí)

在java線程操作時(shí),所有線程運(yùn)行前都保持就緒狀態(tài),此時(shí)那個(gè)線程的優(yōu)先級(jí)高就有可能優(yōu)先被執(zhí)行

所有創(chuàng)建的線程都是子線程,啟動(dòng)時(shí)都是同樣的優(yōu)先級(jí)。

主線程是中等優(yōu)先級(jí) 5

public final void setPriority ( int newPriority ) 設(shè)置線程優(yōu)先級(jí)

MAX_PRIORITY 最高優(yōu)先級(jí) 10

NORM_PRIORITY 中等優(yōu)先級(jí) 5

MIN_PRIORITY 最低優(yōu)先級(jí) 1

public final int getPriority ( ) 獲取線程優(yōu)先級(jí)

public class JavaDemoI {
    public static void main(String[] args) {
        Runnable runnable=(()->{
            for (int i=1;i<=5;i++){
                System.out.println(Thread.currentThread().getName()+i);
            }
        });
        Thread threadA=new Thread(runnable,"線程A");
        Thread threadB=new Thread(runnable,"線程B");
        Thread threadC=new Thread(runnable,"線程C");
        threadA.setPriority(Thread.MIN_PRIORITY);
        threadB.setPriority(Thread.NORM_PRIORITY);
        threadC.setPriority(Thread.MAX_PRIORITY);
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

(7)如何停止線程

(1)停止線程的3個(gè)方法(了解

  • 停止多線:public void stop ()
  • 掛起多線程::public final void suspend()
  • 恢復(fù)掛起的多線程:public final void resume()

(2)對多線程中 stop (), suspend(), resume() 方法在jdk1.2開始不建議使用,主要是因?yàn)檫@三個(gè)方法在操作時(shí)會(huì)產(chǎn)生死鎖的問題。

(3)優(yōu)雅的停止一個(gè)線程

代碼演示:

package ThreadTest;
public class JavaDemoO {
    private static boolean flag=true;
    public static void main(String[] args) throws InterruptedException {
        new Thread(()->{
            long num=0;
            while (flag){
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在運(yùn)行"+num++);
            }
        },"執(zhí)行線程").start();
        Thread.sleep(200);
        flag=false;
    }
}

(8)后臺(tái)守護(hù)線程

(1)java中的線程分為兩類:

  • 用戶線程和守護(hù)線程,守護(hù)線程:是一種運(yùn)行在后臺(tái)的線程服務(wù)線程,當(dāng)用戶線程存在時(shí),守護(hù)線程也可以用時(shí)存在;當(dāng)用戶線程消失時(shí),守護(hù)線程也會(huì)消失。
  • public final void setDaemon(boolean on) 設(shè)置為守護(hù)線程
  • public final boolean idDaemon() 判斷是否是守護(hù)線程

代碼:

package ThreadTest;
public class JavaDemoP {
    public static void main(String[] args) {
        Thread threadA=new Thread(()->{
            for(int i=1;i<=100;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+i);
            }
        },"用戶線程");
        Thread threadB=new Thread(()->{
            for(int i=1;i<=200;i++){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+i);
            }
        },"守護(hù)線程");
        threadB.setDaemon(true);//設(shè)置為守護(hù)線程
        threadA.start();
        threadB.start();
    }
}

結(jié)果:用戶線程結(jié)束時(shí),守護(hù)線程也結(jié)束

在這里插入圖片描述

(9)volatile 關(guān)鍵字

在多線程編程中,若干個(gè)線程為了可以實(shí)現(xiàn)公共資源的操作,往往是復(fù)制相應(yīng)變量的副本,待完成操作后再將副本變量的數(shù)據(jù)與原始數(shù)據(jù)進(jìn)行同步處理,如果開發(fā)者不希望通過副本數(shù)據(jù)進(jìn)行操作,而是希望可以直接通過原始變量的操作(節(jié)省了復(fù)制變量副本與同步的時(shí)間),則可以在變量聲明時(shí)使用volatile關(guān)鍵字.

使用volatile關(guān)鍵字

代碼:

package ThreadTest;
public class JavaDemoQ {
    public static void main(String[] args) {
        new Thread(new MyThreadQ(),"線程A售票成功_剩余票數(shù):").start();
        new Thread(new MyThreadQ(),"線程B售票成功_剩余票數(shù):").start();
        new Thread(new MyThreadQ(),"線程C售票成功_剩余票數(shù):").start();
        new Thread(new MyThreadQ(),"線程D售票成功_剩余票數(shù):").start();
    }
}
class MyThreadQ implements Runnable{
    private volatile int ticket=50;//直接內(nèi)存操作
    @Override
    public void run() {
        while (ticket>0){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            ticket--;
            System.out.println(Thread.currentThread().getName()+ticket);
        }
    }
}

結(jié)果:

在這里插入圖片描述

volatile與synchronized的區(qū)別:

  • volatile無法進(jìn)行同步處理操作,它只是一種直接內(nèi)存的處理,避免副本操作
  • volatile主要是在屬性上使用,而synchronize是在方法和代碼塊上使用。

線程的同步和死鎖

(1)線程同步問題

(1) 同步問題的引出

public class JavaDemoJ {
    private static int ticket=5;//總票數(shù)5張
    public static void main(String[] args) {
        Runnable runnable=(()->{
            while (true){//持續(xù)賣票
                if(ticket>0){
                    try {
                        Thread.sleep(100);//模擬網(wǎng)絡(luò)延遲
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"賣票成功\t剩余票數(shù):"+ticket--);
                }
                if(ticket<=0){
                    System.out.println(Thread.currentThread().getName()+"賣票失敗\t剩余票數(shù):"+ticket);
                    break;
                }
            }
        }) ;
        new Thread(runnable,"售票員A:").start();
        new Thread(runnable,"售票員B:").start();
        new Thread(runnable,"售票員C:").start();
        //假設(shè)此時(shí)還有一張票,當(dāng)?shù)谝粋€(gè)線程滿足售票條件時(shí)(還未減少票數(shù)),其他線程也可能滿足售票條件,
        //就有可能造成同一張票賣出去兩次。
    }
}

問題所在:

假設(shè)此時(shí)還有一張票,當(dāng)?shù)谝粋€(gè)線程滿足售票條件時(shí)(還未減少票數(shù)),其他線程也可能滿足售票條件就有可能造成同一張票賣出去兩次。

在這里插入圖片描述

(2) 解決同步問題

Java使用Synchronized關(guān)鍵字實(shí)現(xiàn)同步操作,同步的關(guān)鍵是要給代碼上“鎖”,而對于鎖的操作有兩種:同步代碼塊,同步方法。

(1)同步代碼塊

同步代碼塊是指使用synchronized關(guān)鍵字定義的代碼塊,在代碼執(zhí)行時(shí),往往需要設(shè)置一個(gè)同步對象,由于線程操作的不確定性所以這時(shí)候的同步對象可以選擇 this。

將票數(shù)判斷和票數(shù)自減放在同一代碼塊中,當(dāng)多線程并發(fā)執(zhí)行時(shí),只允許一個(gè)線程執(zhí)行該部分代碼塊,就實(shí)現(xiàn)啦同步處理操作、

public class JavaDemoK {
    public static void main(String[] args) {
        MyThreadK myThreadK=new MyThreadK();
        new Thread(myThreadK,"線程A售票成功,剩余票數(shù):").start();
        new Thread(myThreadK,"線程B售票成功,剩余票數(shù):").start();
        new Thread(myThreadK,"線程C售票成功,剩余票數(shù):").start();
        new Thread(myThreadK,"線程D售票成功,剩余票數(shù):").start();
    }
}
class MyThreadK implements Runnable{
    private static int ticket=10;//總票數(shù)
    @Override
    public void run() {
        while (true){
            synchronized (this){//同步代碼塊
                if(ticket>0){
                    try {
                        Thread.sleep(100);//模擬網(wǎng)絡(luò)延遲
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+(--ticket));
                }else {
                    System.out.println("全部售空");
                    break;
                }
            }
        }
    }
}

結(jié)果:

在這里插入圖片描述

(2)同步方法解決

public class JavaDemoL {
    public static void main(String[] args) {
        MyThreadL myThreadL=new MyThreadL();
        new Thread(myThreadL,"售票員A售票成功,剩余票數(shù):").start();
        new Thread(myThreadL,"售票員B售票成功,剩余票數(shù):").start();
        new Thread(myThreadL,"售票員C售票成功,剩余票數(shù):").start();
        new Thread(myThreadL,"售票員D售票成功,剩余票數(shù):").start();
    }
}
class MyThreadL implements Runnable{
    private static int ticket=10;
    @Override
    public void run() {
        sale();
    }
    public synchronized void sale() {//同步方法
        while (true){
        if(this.ticket>0){
            try {
                Thread.sleep(100);//網(wǎng)絡(luò)延遲
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.ticket--;
            System.out.println(Thread.currentThread().getName()+this.ticket);
        } else {
            System.out.println("票已售空");
            break;
        }
    }
    }
}

結(jié)果:

在這里插入圖片描述

(2)線程死鎖問題

(1)死鎖問題的引出

代碼:

package ThreadTest;
public class JavaDemoJ implements Runnable{
    private book book=new book();
    private money money=new money();
    public JavaDemoJ(){
        new Thread(this).start();
        book.tell(money);
    }
    public void run(){
        money.tell(book);
    }
    public static void main(String[] args) {
        new JavaDemoJ();
    }
}
class book{
    public synchronized void tell(money money){
        System.out.println("你先給我錢,我才給你書!");
        money.get();
    }
    public synchronized void get(){
        System.out.println("收到錢,把書給買家!");
    }
}
class money{
    public synchronized void tell(book book){
        System.out.println("你先給我書,我再給你錢!");
        book.get();
    }
    public synchronized void get(){
        System.out.println("收到書,把錢給賣家!");
    }
}

結(jié)果:

在這里插入圖片描述

結(jié)論:本程序中采用大量同步處理操作,而死鎖一旦出現(xiàn)線程將進(jìn)入等待操作,并且不會(huì)向下繼續(xù)執(zhí)行。

(3)生產(chǎn)者消費(fèi)者問題

(1)生產(chǎn)者生產(chǎn),消費(fèi)者取出,生產(chǎn)者生產(chǎn)一個(gè),消費(fèi)者取出一個(gè)

未同步代碼:

package ThreadTest;
public class JavaDemoM {
    public static void main(String[] args) {
        Message message=new Message();
        new Thread(new Producer(message)).start();
        new Thread(new Consumer(message)).start();
    }
}
class Message{//消息類
    private String title;//標(biāo)題
    private String content;//內(nèi)容
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getContent() {
        return content;
    }
    public void setContent(String content) {
        this.content = content;
    }
}
class Producer implements Runnable{//定義生產(chǎn)者
    private Message msg=null;
    public Producer(Message msg){
        this.msg=msg;
    }
    @Override
    public void run() {
        for(int i=1;i<=50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i%2==0){
                msg.setTitle(i+"英雄");
                msg.setContent("名垂千古");
            }else {
                msg.setTitle(i+"小人");
                msg.setContent("遺臭萬年");
            }
        }
    }
}
class Consumer implements Runnable{
    private Message msg;
    public Consumer(Message msg){
        this.msg=msg;
    }
    @Override
    public void run() {
        for (int i=1;i<=50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("【"+msg.getTitle()+","+msg.getContent()+"】");
        }
    }
}

錯(cuò)誤產(chǎn)生結(jié)果:

在這里插入圖片描述

問題分析:

數(shù)據(jù)錯(cuò)位:假設(shè)生產(chǎn)者線程還剛在存儲(chǔ)空間添加一個(gè)數(shù)據(jù)的標(biāo)題,還未添加內(nèi)容,程序就切換到消費(fèi)者線程,消費(fèi)者就會(huì)把沒有生產(chǎn)者沒有添加的內(nèi)容和上一組生產(chǎn)的內(nèi)容聯(lián)系在一起導(dǎo)致數(shù)據(jù)錯(cuò)位。重復(fù)操作:生產(chǎn)者線程放入多組數(shù)據(jù),消費(fèi)者線程才開始取出,或者是消費(fèi)者還沒生產(chǎn)數(shù)據(jù),消費(fèi)者就已經(jīng)重復(fù)取出數(shù)據(jù)。

同步代碼解決:

package ThreadTest;
public class JavaDemoN {
    public static void main(String[] args) {
        Message2 msg=new Message2();
        new Thread(new Producer2(msg)).start();
        new Thread(new Consumer2(msg)).start();
    }
}
class Message2 {
    private String title;
    private String content;
    private boolean key=true;
    //true 允許生產(chǎn)不允許消費(fèi)
    //false 允許消費(fèi)不允許生產(chǎn)
    public synchronized void set(String title,String content) {//同步方法
        if(this.key==false){//允許消費(fèi)不允許生產(chǎn)
            try {
                super.wait();//線程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.title = title;
        this.content = content;
        this.key=false;//生產(chǎn)完畢
        super.notify();//喚醒線程
    }
    public synchronized String get() {
        if(this.key==true){//允許生產(chǎn)不允許消費(fèi)
            try {
                super.wait();//線程等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
       try {
           return title +","+ content;
       }finally {
           this.key=true;
           super.notify();
       }
    }
}
class Producer2 implements Runnable{//定義生產(chǎn)者
    private Message2 msg=null;
    public Producer2(Message2 msg){
        this.msg=msg;
    }
    @Override
    public void run() {
        for(int i=1;i<=50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(i%2==0){
                this.msg.set(i+"英雄","萬古流芳");
            }else {
                this.msg.set(i+"小人","遺臭萬年");
            }
        }
    }
}
class Consumer2 implements Runnable{
    private Message2 msg;
    public Consumer2(Message2 msg){
        this.msg=msg;
    }
    @Override
    public void run() {
        for (int i=1;i<=50;i++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("【"+msg.get()+"】");
        }
    }
}

正確執(zhí)行結(jié)果:

在這里插入圖片描述

原理解釋:

將同步操作設(shè)置在Message類中,使用synchronized關(guān)鍵字可以使消息內(nèi)容同步,不會(huì)造成消息錯(cuò)位。

在生產(chǎn)者和消費(fèi)者線程中根據(jù)key判斷,是否可以生產(chǎn)或者取出,如果不能生產(chǎn)或者取出,可使用 wait( ) 線程等待方法 等待另一方線程取出或者生產(chǎn)后,使用 notify( ) 線程喚醒,繼續(xù)執(zhí)行操作。notifyAll( ) 是喚醒全部等待的線程 

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。 

相關(guān)文章

  • springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn)

    springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn)

    在Spring-Boot項(xiàng)目開發(fā)中,存在著本模塊的代碼需要訪問外面模塊接口,本文就來介紹一下springboot模塊里面調(diào)用另外一個(gè)模塊的方法實(shí)現(xiàn),感興趣的可以了解一下
    2023-11-11
  • Springboot?上傳文件或頭像(MultipartFile、transferTo)

    Springboot?上傳文件或頭像(MultipartFile、transferTo)

    本文主要介紹了Springboot?上傳文件或頭像(MultipartFile、transferTo),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Spring中的代理ProxyFactory解析

    Spring中的代理ProxyFactory解析

    這篇文章主要介紹了Spring中的ProxyFactory解析,在Java中,代理模式的實(shí)現(xiàn)通常依靠Proxy類和InvocationHandler接口,本文將介紹如何使用ProxyFactory來創(chuàng)建代理模式,需要的朋友可以參考下
    2023-12-12
  • 深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    深入理解Java設(shè)計(jì)模式之職責(zé)鏈模式

    這篇文章主要介紹了JAVA設(shè)計(jì)模式之職責(zé)鏈模式的的相關(guān)資料,文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解
    2021-11-11
  • springboot實(shí)現(xiàn)攔截器之驗(yàn)證登錄示例

    springboot實(shí)現(xiàn)攔截器之驗(yàn)證登錄示例

    本篇文章主要介紹了springboot實(shí)現(xiàn)攔截器之驗(yàn)證登錄示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-02-02
  • 解決springcloud 配置gateway 出現(xiàn)錯(cuò)誤的問題

    解決springcloud 配置gateway 出現(xiàn)錯(cuò)誤的問題

    今天給大家分享springcloud 配置gateway 出現(xiàn)錯(cuò)誤的問題,其實(shí)解決方法很簡單,只需要降低springcloud版本,改成Hoxton.SR5就好了,再次改成Hoxton.SR12,也不報(bào)錯(cuò)了,下面給大家展示下,感興趣的朋友一起看看吧
    2021-11-11
  • Java中的RestTemplate使用詳解

    Java中的RestTemplate使用詳解

    這篇文章主要介紹了Java中的RestTemplate使用詳解,Spring內(nèi)置了RestTemplate作為Http請求的工具類,簡化了很多操作,雖然Spring5推出了WebClient,但是整體感覺還是RestTemplate用起來更簡單方便一些,需要的朋友可以參考下
    2023-10-10
  • mybatis如何批量更新list對象

    mybatis如何批量更新list對象

    這篇文章主要介紹了mybatis如何批量更新list對象問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Java中初始化List集合的八種方式匯總

    Java中初始化List集合的八種方式匯總

    List?是?Java?開發(fā)中經(jīng)常會(huì)使用的集合,下面這篇文章主要給大家介紹了關(guān)于Java中初始化List集合的八種方式,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-06-06
  • 淺析Java8的函數(shù)式編程

    淺析Java8的函數(shù)式編程

    函數(shù)式編程,這個(gè)詞語由兩個(gè)名詞構(gòu)成,函數(shù),編程。這篇文章主要介紹了Java8的函數(shù)式編程 ,需要的朋友可以參考下
    2017-05-05

最新評論