真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

java多線程機(jī)制是什么

本篇內(nèi)容主要講解“java多線程機(jī)制是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“java多線程機(jī)制是什么”吧!

10年的亞東網(wǎng)站建設(shè)經(jīng)驗(yàn),針對設(shè)計(jì)、前端、開發(fā)、售后、文案、推廣等六對一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。全網(wǎng)整合營銷推廣的優(yōu)勢是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整亞東建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“亞東網(wǎng)站設(shè)計(jì)”,“亞東網(wǎng)站推廣”以來,每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。

java多線程機(jī)制是什么

一、程序、進(jìn)程、線程

1.1 什么是程序

程序(program):是為完成特定任務(wù)、用某種語言編寫的一組指令的集合,是一段靜態(tài)的代碼。 (程序是靜態(tài)的)

java多線程機(jī)制是什么

1.2 什么是進(jìn)程

進(jìn)程(process):是程序的一次執(zhí)行過程,正在運(yùn)行的一個(gè)程序,進(jìn)程作為資源分配的單位,在內(nèi)存中會(huì)為每個(gè)進(jìn)程分配不同的內(nèi)存區(qū)域。 (進(jìn)程是動(dòng)態(tài)的)是一個(gè)動(dòng)的過程 ,進(jìn)程的生命周期 : 有它自身的產(chǎn)生、存在和消亡的過程

java多線程機(jī)制是什么
目前操作系統(tǒng)都是支持多進(jìn)程,可以同時(shí)執(zhí)行多個(gè)進(jìn)程,通過進(jìn)程ID區(qū)分
java多線程機(jī)制是什么

1.3 什么是線程

線程(thread):進(jìn)程中的一條執(zhí)行路徑,也是CUP的基本調(diào)度單位,一個(gè)進(jìn)程由一個(gè)或多個(gè)線程組成,彼此間完成不同的工作,多個(gè)線程同時(shí)執(zhí)行,稱為多線程。

java多線程機(jī)制是什么
java多線程機(jī)制是什么
線程的組成

任何一個(gè)線程都具有的基本組成部分:

  • CPU時(shí)間片:操作系統(tǒng)(OS)會(huì)為每一個(gè)線程分配執(zhí)行時(shí)間。

  • 運(yùn)行數(shù)據(jù):堆空間(存儲(chǔ)線程需要使用的對象,多個(gè)線程可以共享堆中的對象);??臻g(存儲(chǔ)線程需要使用的局部變量,每個(gè)線程都擁有獨(dú)立的棧)

線程的特點(diǎn)

  • 線程搶占式執(zhí)行(效率高、可防止單一線程長時(shí)間獨(dú)占CPU)

  • 單核CPU在執(zhí)行的時(shí)候,是按照時(shí)間片執(zhí)行的,一個(gè)時(shí)間片只能執(zhí)行一個(gè)線程,因?yàn)闀r(shí)間片特別的短,我們感受到的就是“同時(shí)”進(jìn)行的。

  • 多核CPU真正意義上做到了一個(gè)時(shí)間片多個(gè)線程同時(shí)進(jìn)行

  • 在單核CPU中,宏觀上同時(shí)進(jìn)行,微觀上順序執(zhí)行

1.4 進(jìn)程和線程的區(qū)別

  • 進(jìn)程是操作系統(tǒng)中資源分配的基本單位,而線程是CPU的基本調(diào)度單位

  • 一個(gè)程序運(yùn)行后至少有一個(gè)進(jìn)程

  • 一個(gè)進(jìn)程可以包含多個(gè)線程,但是至少需要有一個(gè)線程,否則這個(gè)進(jìn)程是沒有意義的

  • 進(jìn)程間不能共享數(shù)據(jù)段地址,但通進(jìn)程的線程之間可以。

二、創(chuàng)建線程的三種方式

2.1 繼承Thread類重寫run()方法

具體實(shí)現(xiàn)

1.繼承Thread類
2.重寫run()方法
3.創(chuàng)建子類對象
4.調(diào)用start()方法(PS:不要調(diào)用run()方法,這樣相當(dāng)于普通調(diào)用對象的方法,并為啟動(dòng)線程

繼承類

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 1; i <= 50; i++) {
            System.out.println("子線程:==>" + i);
        }
    }}

測試類

public class TestThread {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 1; i <= 50; i++) {
            System.out.println("主線程:-->"+i);
        }
    }}

結(jié)果
java多線程機(jī)制是什么

獲取線程ID和名稱

getId()//獲取線程的id,每個(gè)線程都有自己的id
getName()//獲取線程名字
Thread.currentThread()//獲取當(dāng)前線程

代碼

public class TestThread {

	public static void main(String[] args) {
		MyThread t=new MyThread();
		t.start();
        //只能在繼承Thread類的情況下用
		System.out.println("線程id:"+t.getId());
		System.out.println("線程名字:"+t.getName());
        //調(diào)用Thread類的靜態(tài)方法獲取當(dāng)前線程(這里獲取的是主線程)
		System.out.println("線程id:"+Thread.currentThread().getId());
		System.out.println("線程名字:"+Thread.currentThread().getName());
	}}

java多線程機(jī)制是什么

修改線程名稱

只能修改線程的名稱,不能修改線程的id(id是由系統(tǒng)自動(dòng)分配)
1、使用線程子類的構(gòu)造方法賦值
2、調(diào)用線程對象的setName()方法

代碼

public class MyThread extends Thread{
	public MyThread() {}//無參構(gòu)造器
	public MyThread(String name) {
		super(name);
	}
	public void run() {
		for(int i=1;i<=50;i++) {}
	}}
public class TestThread {

	public static void main(String[] args) {
		MyThread t1=new MyThread("子線程1");//通過構(gòu)造方法
		MyThread t2=new MyThread();
		t2.setName("子線程2");
		System.out.println("線程t1的id:"+t1.getId()+" 名稱:"+t1.getName());
		System.out.println("線程t2的id:"+t2.getId()+" 名稱:"+t2.getName());
	}}

java多線程機(jī)制是什么

2.2 實(shí)現(xiàn)Runnable接口實(shí)現(xiàn)run()方法

具體實(shí)現(xiàn)

1.實(shí)現(xiàn)Runnable接口
2.實(shí)現(xiàn)run()方法
3.創(chuàng)建實(shí)現(xiàn)類對象
4.創(chuàng)建線程類對象
5.調(diào)用start()方法

實(shí)現(xiàn)接口

public class MyRunnable implements Runnable{//實(shí)現(xiàn)接口
	@Override
	public void run() {//實(shí)現(xiàn)run方法
		// TODO Auto-generated method stub
		for(int i=1;i<=10;i++) {
			System.out.println("子線程:"+i);
		}
	}}

測試類

public class TestRunnable {
	public static void main(String[] args) {
		//1.創(chuàng)建MyRunnable對象,表示線程執(zhí)行的功能
		Runnable runnable=new MyRunnable();
		//2.創(chuàng)建線程對象
		Thread th=new Thread(runnable);
		//3.啟動(dòng)線程
		th.start();
		for(int i=1;i<=10;i++) {
			System.out.println("主線程:"+i);
		}		
	}}

java多線程機(jī)制是什么

使用匿名內(nèi)部類

如果一個(gè)線程方法我們只使用一次,那么就不必設(shè)置一個(gè)單獨(dú)的類,就可以使用匿名內(nèi)部類實(shí)現(xiàn)該功能

public class TestRunnable {
	public static void main(String[] args) {
		Runnable runnable=new Runnable() {			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for(int i=1;i<=10;i++) {
					System.out.println("子線程:"+ i);
				}
			}
		};
		
		Thread th=new Thread(runnable);
		th.start();
	}}

2.3 實(shí)現(xiàn)Callable接口

Callable和Thread、Runnable比較

對比繼承Thread類和實(shí)現(xiàn)Runnable接口創(chuàng)建線程的方式發(fā)現(xiàn),都需要有一個(gè)run()方法,但是這個(gè)run()方法有不足:

  • 沒有返回值

  • 不能拋出異常

基于上面的兩個(gè)不足,在JDK1.5以后出現(xiàn)了第三種創(chuàng)建線程的方式:實(shí)現(xiàn)Callable接口

實(shí)現(xiàn)Callable接口的好處:

  • 有返回值

  • 能拋出異常

缺點(diǎn):

  • 創(chuàng)建線程比較麻煩

1.實(shí)現(xiàn)Callable接口,可以不帶泛型,如果不帶泛型,那么call方法的返回值就是Object類型

2.如果帶泛型,那么call的返回值就是泛型對應(yīng)的類型

3.從call方法看到:方法有返回值,可以拋出異常

具體實(shí)現(xiàn)

實(shí)現(xiàn)接口

import java.util.Random;import java.util.concurrent.Callable;import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class TestCallable implements Callable{

	@Override
	public Integer call() throws Exception {
		// TODO Auto-generated method stub
		return new Random().nextInt(10);
	}}

測試類

import java.util.concurrent.ExecutionException;import java.util.concurrent.FutureTask;public class Test {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		TestCallable tc=new TestCallable();
		FutureTask ft=new FutureTask<>(tc);
		//創(chuàng)建線程對象
		Thread th=new Thread(ft);
		th.start();
		//獲取線程得到的返回值
		Integer In=ft.get();
		System.out.println(In);
	}}

三、線程的狀態(tài)

3.1 基本四狀態(tài)

java多線程機(jī)制是什么

3.2 等待狀態(tài)

java多線程機(jī)制是什么

3.3 阻塞狀態(tài)

java多線程機(jī)制是什么

四、線程常用的方法

  • 休眠(當(dāng)前線程主動(dòng)休眠millis毫秒)public static void sleep(long millis)

  • 放棄(當(dāng)前線程主動(dòng)放棄時(shí)間片,回到就緒狀態(tài),競爭下一次時(shí)間片)public static void yield()

  • 加入(當(dāng)一個(gè)線程調(diào)用了join方法,這個(gè)線程就會(huì)先被執(zhí)行,它執(zhí)行結(jié)束以后才可以去執(zhí)行其余的線程)public final void join()//必須先start(),在join(),才有效

  • 優(yōu)先級(jí)(線程優(yōu)先級(jí)為1–10,默認(rèn)為5,優(yōu)先級(jí)越高,表示獲取CPU機(jī)會(huì)越多)線程對象.setPriority()

  • 守護(hù)線程

    • 線程對象.setDaemon(true);設(shè)置為守護(hù)線程

    • 線程有兩類:用戶線程(前臺(tái)線程)、守護(hù)線程(后臺(tái)線程)

    • 如果程序中所有前臺(tái)線程都執(zhí)行完畢了,后臺(tái)線程也會(huì)自動(dòng)結(jié)束

    • 垃圾回收器線程屬于守護(hù)線程

4.1 線程休眠(sleep)

public static void sleep(long millis)當(dāng)前線程主動(dòng)休眠millis毫秒

子線程

public class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}

PS:sleep()的異常在run方法中是不能拋出的,只能用try–catch處理
測試類

public class Test01 {
    public static void main(String[] args) {
        SleepThread sleepThread = new SleepThread();
        sleepThread.start();
    }}

結(jié)果:每次間隔100ms輸出一次
java多線程機(jī)制是什么

4.2 線程放棄(yield)

public static void yield()當(dāng)前線程主動(dòng)放棄時(shí)間片,回到就緒狀態(tài),競爭下一次時(shí)間片

子線程

public class YieldThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            Thread.yield();//主動(dòng)放棄資源
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) {
        YieldThread yieldThread01 = new YieldThread();
        YieldThread yieldThread02 = new YieldThread();
        yieldThread01.start();
        yieldThread02.start();
    }}

結(jié)果:基本都會(huì)交替進(jìn)行,也會(huì)有一個(gè)線程連輸出
java多線程機(jī)制是什么

4.3 線程加入(join)

當(dāng)一個(gè)線程調(diào)用了join方法,這個(gè)線程就會(huì)先被執(zhí)行,它執(zhí)行結(jié)束以后才可以去執(zhí)行其余的線程,必須先start,再join才有效

子線程

public class JoinThread extends Thread{
    @Override
    public void run() {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            if(i==5){
                JoinThread joinThread = new JoinThread();
                joinThread.start();
                joinThread.join();
            }
        }
    }}

結(jié)果:當(dāng)主線程打印到5的時(shí)候,這時(shí)候子線程加入進(jìn)來,就先執(zhí)行完子線程,在執(zhí)行主線程
java多線程機(jī)制是什么

4.4 守護(hù)線程(setDaemon)

將子線程設(shè)置為主線程的伴隨線程,主線程停止的時(shí)候,子線程也不要繼續(xù)執(zhí)行了
注意:先設(shè)置,在啟動(dòng)

子線程

public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=1000;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }}

測試類

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread daemonThread = new TestThread();
        daemonThread.setDaemon(true);//設(shè)置守護(hù)線程
        daemonThread.start();
        for (int i=1;i<=10;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
            Thread.sleep(100);
        }
    }}

結(jié)果:當(dāng)主線程結(jié)束時(shí),子線程也跟著結(jié)束,并不會(huì)繼續(xù)執(zhí)行下去打印輸出
java多線程機(jī)制是什么

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

線程優(yōu)先級(jí)為1-10,默認(rèn)為5,優(yōu)先級(jí)越高,表示獲取CPU機(jī)會(huì)越多
java多線程機(jī)制是什么

子線程

public class TestThread extends Thread{
    @Override
    public void run() {
        for(int i=1;i<=100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }}

測試

public class Test01 {
    public static void main(String[] args) throws InterruptedException {
        TestThread th2 = new TestThread();
        TestThread th3 = new TestThread();
        TestThread th4 = new TestThread();
        th2.setPriority(10);//設(shè)置線程1優(yōu)先級(jí)10
        th2.start();
        th3.start();//線程2優(yōu)先級(jí)默認(rèn)不變,為5
        th4.setPriority(1);//設(shè)置線程3優(yōu)先級(jí)為1
        th4.start();
    }}

結(jié)果:優(yōu)先級(jí)(th2>th3>th4)線程3應(yīng)該在最后打印
java多線程機(jī)制是什么

五、線程安全問題

5.1 賣票案例

需求:模擬三個(gè)窗口,每個(gè)窗口有100個(gè)人,同時(shí)搶10張票
java多線程機(jī)制是什么
使用繼承Runnable接口的方法

public class BuyTicketRunnable implements Runnable{
	
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<=100;i++) {
			if(ticketNum<=0) break;
			System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
			ticketNum--;
		}
	}}

測試方法

public class BuyTicketTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Runnable runnable=new BuyTicketRunnable();
		
		Thread th2=new Thread(runnable,"窗口1");
		Thread th3=new Thread(runnable,"窗口2");
		Thread th4=new Thread(runnable,"窗口3");
		
		th2.start();
		th3.start();
		th4.start();
	}}

結(jié)果
java多線程機(jī)制是什么

我們發(fā)現(xiàn),不同窗口會(huì)搶到同一張票?。?!,這在實(shí)際情況是不允許的,這是因?yàn)槎鄠€(gè)線程,在爭搶資源的過程中,導(dǎo)致共享的資源出現(xiàn)問題。一個(gè)線程還沒執(zhí)行完,另一個(gè)線程就參與進(jìn)來了,開始爭搶。(但窗口2搶到第10張票,還沒來得及ticketNum--操作,時(shí)間片就用完了,隨后被窗口三搶到CPU資源,此時(shí)的票數(shù)還是10,窗口三也搶到第十張票,也還沒來得及ticketNum--操作窗口三時(shí)間片由完了,窗口一搶到CPU資源,還是買到了第10張票)

多線程安全問題:

  • 當(dāng)多線程并發(fā)訪問臨界資源時(shí),如果破壞原子操作,可能會(huì)造成數(shù)據(jù)不一致

  • 臨界資源:共享資源(同一對象),一次只能允許一個(gè)線程使用,才可以保證其正確性

  • 原子操作:不可分割的多步操作,被視作一個(gè)整體,其順序和步驟不可被打亂或缺省

5.2 同步代碼塊

synchronized(同步監(jiān)視器)

  • 必須是引用數(shù)據(jù)類型,不能是基本數(shù)據(jù)類型

  • 也可以創(chuàng)建一個(gè)專門的同步監(jiān)視器,沒有任何業(yè)務(wù)含義 (new Object)

  • 一般使用共享資源做同步監(jiān)視器即可

  • 在同步代碼塊中不能改變同步監(jiān)視器對象的引用

  • 盡量不要String和包裝類Integer做同步監(jiān)視器,建議使用final修飾同步監(jiān)視器

對賣票案例改進(jìn)

public class BuyTicketRunnable implements Runnable{
	static Object obj=new Object();
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
            //把具有安全隱患的代碼鎖住即可,如果鎖多了就會(huì)效率低 
			synchronized (obj) {//鎖必須多個(gè)線程用的是同一把鎖?。∫部梢允褂胻his,表示的是該對象本身
				System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
				ticketNum--;	
			}
		}
	}}
  • 多個(gè)代碼塊使用了同一個(gè)同步監(jiān)視器(鎖),鎖住一個(gè)代碼塊的同時(shí),也鎖住所有使用該鎖的所有代碼塊,其他線程無法訪問其中的任何一個(gè)代碼塊

  • 多個(gè)代碼塊使用了同一個(gè)同步監(jiān)視器(鎖),鎖住一個(gè)代碼塊的同時(shí),也鎖住所有使用該鎖的所有代碼塊, 但是沒有鎖住使用其他同步監(jiān)視器的代碼塊,其他線程有機(jī)會(huì)訪問其他同步監(jiān)視器的代碼塊

5.3 同步方法

synchronized(同步方法)

  • 不要將run()定義為同步方法

  • 非靜態(tài)同步方法的同步監(jiān)視器是this;靜態(tài)同步方法(static)的同步監(jiān)視器是 類名.class 字節(jié)碼信息對象

  • 同步代碼塊的效率要高于同步方法(原因:同步方法是將線程擋在了方法的外部,而同步代碼塊鎖將線程擋在了代碼塊的外部,但是卻是方法的內(nèi)部)

  • 同步方法的鎖是this,一旦鎖住一個(gè)方法,就鎖住了所有的同步方法;同步代碼塊只是鎖住使用該同步監(jiān)視器的代碼塊,而沒有鎖住使用其他監(jiān)視器的代碼塊

買票案例改進(jìn)

public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
			BuyTicket();
		}
	}
	public synchronized void BuyTicket() {//鎖住的是:this,如果是靜態(tài)方法:當(dāng)前類.class
		if(ticketNum>0) {
			System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
			ticketNum--;	
		}
	}}

5.4 Lock鎖

Lock鎖:

  • DK1.5后新增新一代的線程同步方式:Lock鎖,與采用synchronized相比,lock可提供多種鎖方案,更靈活

  • synchronized是Java中的關(guān)鍵字,這個(gè)關(guān)鍵字的識(shí)別是靠JVM來識(shí)別完成的呀。是虛擬機(jī)級(jí)別的。
    但是Lock鎖是API級(jí)別的,提供了相應(yīng)的接口和對應(yīng)的實(shí)現(xiàn)類,這個(gè)方式更靈活,表現(xiàn)出來的性能優(yōu)于之前的方式。

對買票案例改進(jìn)

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class BuyTicketRunnable implements Runnable{
	private int ticketNum=10;
	Lock lock=new ReentrantLock();//接口=實(shí)現(xiàn)類  可以使用不同的實(shí)現(xiàn)類
	@Override
	public void run() {
		for(int i=1;i<100;i++) {
			lock.lock();//打開鎖
			try {
				if(ticketNum>0) {
					System.out.println("在"+Thread.currentThread().getName()+"買到了第"+ticketNum+"張票!");
					ticketNum--;	
				}
			}catch(Exception e) {
				e.printStackTrace();
			}finally {
				 //關(guān)閉鎖:--->即使有異常,這個(gè)鎖也可以得到釋放
				lock.unlock();
			}
		}
	}}

Lock和synchronized的區(qū)別

  • Lock是顯式鎖(手動(dòng)開啟和關(guān)閉鎖,別忘記關(guān)閉鎖),synchronized是隱式鎖

  • Lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖

  • 使用Lock鎖,JVM將花費(fèi)較少的時(shí)間來調(diào)度線程,性能更好。并且具有更好的擴(kuò)展性(提供更多的子類)

5.5 線程死鎖

  • 不同的線程分別占用對方需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就形成了線程的死鎖

  • 出現(xiàn)死鎖后,不會(huì)出現(xiàn)異常,不會(huì)出現(xiàn)提示,只是所有的線程都處于阻塞狀態(tài),無法繼續(xù)

*案例:男孩女孩一起去吃飯,但是桌子上只有兩根筷子,如果兩個(gè)人同時(shí)搶到一根筷子而不放棄,這樣兩個(gè)人都吃不上飯,這樣就形成死鎖了;必須要有一個(gè)人放棄爭搶,等待另一個(gè)人用完,釋放資源,這個(gè)人之后才會(huì)獲得兩根筷子,兩個(gè)人才能都吃上飯 *

package 多線程;class Eat{
    //代表兩個(gè)筷子
	public static Object o1=new Object();
	public static Object o2=new Object();
	public static void eat() {
		System.out.println("可以吃飯了");
	}}class BoyThread extends Thread{
	public void run() {
		synchronized (Eat.o1) {
			System.out.println("男孩拿到了第一根筷子!");
			synchronized (Eat.o2) {
				System.out.println("男孩拿到了第二根筷子!");
				Eat.eat();
			}
		}
	}}class GirlThread extends Thread{
	public void run() {
		synchronized (Eat.o2) {
			System.out.println("女孩拿到了第二根筷子!");
			synchronized (Eat.o1) {
				System.out.println("女孩拿到了第一根筷子!");
				Eat.eat();
			}
		}
	}}public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		girl.start();
	}}

結(jié)果
java多線程機(jī)制是什么
解決辦法

先讓男孩拿到筷子,線程休眠一下,等待男孩用完筷子,在啟動(dòng)女孩線程

public class MyLock {
	public static void main(String[] args) {
		BoyThread boy=new BoyThread();
		GirlThread girl=new GirlThread();
		boy.start();
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		girl.start();
	}}

java多線程機(jī)制是什么
在寫程序中要避免這種死鎖:減少同步資源的定義,避免嵌套同步

六、線程通信問題

在Java對象中,有兩種池

  • 鎖池(synchronized

  • 等待池(wait();notify();notifyAll()

如果一個(gè)線程調(diào)用了某個(gè)對象的wait方法,那么該線程進(jìn)入到該對象的等待池中(并且已經(jīng)將鎖釋放);
如果未來的某個(gè)時(shí)刻,另外一個(gè)線程調(diào)用了相同的對象notify方法或者notifyAll方法,那么該等待池中的線程就會(huì)被喚醒,然后進(jìn)入到對象的鎖池里面去獲得該對象的鎖;
如果獲得鎖成功后,那么該線程就會(huì)沿著wait方法之后的路徑繼續(xù)執(zhí)行。注意:沿著wait方法之后執(zhí)行

6.1 wait()和wait(long timeout)

  • wait():的作用是讓當(dāng)前線程進(jìn)入等待狀態(tài),同時(shí),wait()也會(huì)讓當(dāng)前線程釋放它所持有的鎖。直到其他線程調(diào)用此對象的notify() 方法或 notifyAll() 方法,當(dāng)前線程被喚醒(進(jìn)入就緒狀態(tài))

  • wait(long timeout):讓當(dāng)前線程處于“等待(阻塞)狀態(tài),直到其他線程調(diào)用此對象的notify()方法或 notifyAll() 方法,或者超過指定的時(shí)間量”,當(dāng)前線程被喚醒(進(jìn)入就緒狀態(tài))

sleep和wait的區(qū)別:sleep進(jìn)入阻塞狀態(tài)沒有釋放鎖,wait進(jìn)入阻塞狀態(tài)但是同時(shí)釋放了鎖

6.2 notify()和notifyAll()

notify()和notifyAll()的作用,則是喚醒當(dāng)前對象上的等待線程

  • notify()是喚醒單個(gè)線程

  • notifyAll()是喚醒所有的線程

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

案例:
假設(shè)倉庫中只能存放一件產(chǎn)品,生產(chǎn)者將生產(chǎn)出來的產(chǎn)品放入倉庫,消費(fèi)者將倉庫中產(chǎn)品取走消費(fèi)。
如果倉庫中沒有產(chǎn)品,則生產(chǎn)者將產(chǎn)品放入倉庫,否則停止生產(chǎn)并等待,直到倉庫中的產(chǎn)品被消費(fèi)者取走為止。
如果倉庫中放有產(chǎn)品,則消費(fèi)者可以將產(chǎn)品取走消費(fèi),否則停止消費(fèi)并等待,直到倉庫中再次放入產(chǎn)品為止。
java多線程機(jī)制是什么

功能分解一:商品類

public class Product {//商品類
    private String name;//名字
    private String brand;//品牌
    boolean flag = false;//設(shè)置標(biāo)記,false表示商品沒有,等待生產(chǎn)者生產(chǎn)

    public synchronized void setProduct(String name, String brand) {//生產(chǎn)商品,同步方法,鎖住的是this
        if (flag == true) {//如果flag為true,代表有商品,不生產(chǎn),等待消費(fèi)者消費(fèi)
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //生產(chǎn)商品
        this.setName(name);
        this.setBrand(brand);

        System.out.println("生產(chǎn)者生產(chǎn)了" +this.getBrand() +this.getName());
        //生產(chǎn)完,設(shè)置標(biāo)志
        flag = true;
        //喚醒消費(fèi)線程
        notify();
    }

    public synchronized void getProduct() {
        if (flag == false) {//如果是false,則沒有商品,等待生產(chǎn)者生產(chǎn)
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果有商品,消費(fèi)
        System.out.println("消費(fèi)者消費(fèi)了" + this.getBrand() +this.getName());
        //設(shè)置標(biāo)志
        flag = false;
        //喚醒線程
        notify();
    }


    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }}

功能分解二:生產(chǎn)者線程

public class ProducterThread extends Thread {//生產(chǎn)者線程
    private Product p;

    public ProducterThread(Product p) {
        this.p = p;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            if(i%2==0){//如果是奇數(shù),就生產(chǎn)巧克力,如果是偶數(shù),就生產(chǎn)方便面
                p.setProduct("巧克力","德芙");
            }else{
                p.setProduct("方便面","康師傅");
            }
        }
    }}

功能分解三:消費(fèi)者線程

public class CustomerThread extends Thread {//消費(fèi)者線程
    private Product pro;

    public CustomerThread(Product pro) {
        this.pro = pro;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            pro.getProduct();
        }
    }}

功能分解四:測試類

public class Test {
    public static void main(String[] args) {
        Product p = new Product();
        ProducterThread pth = new ProducterThread(p);
        CustomerThread cth = new CustomerThread(p);
        pth.start();
        cth.start();
    }}

結(jié)果:生產(chǎn)者生產(chǎn)一件商品,消費(fèi)者消費(fèi)一件商品,交替進(jìn)行

java多線程機(jī)制是什么

到此,相信大家對“java多線程機(jī)制是什么”有了更深的了解,不妨來實(shí)際操作一番吧!這里是創(chuàng)新互聯(lián)網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!


當(dāng)前題目:java多線程機(jī)制是什么
鏈接地址:http://weahome.cn/article/igipsc.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部