溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例

發(fā)布時(shí)間:2021-02-24 10:59:09 來源:億速云 閱讀:157 作者:小新 欄目:編程語言

這篇文章主要介紹了Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。


線程安全基礎(chǔ)

  • 1.線程安全問題

  • 2.賬戶取款案例

  • 3.同步代碼塊synchronized


    • synchronized的理解

    • java中有三大變量的線程安全問題

    • 在實(shí)例方法上使用synchronized

    • 總結(jié)

    • 面試題

  • 4.死鎖

  • 5.開發(fā)中應(yīng)該怎么解決線程安全問題

  • 6.守護(hù)線程

  • 7.定時(shí)器

  • 8.實(shí)現(xiàn)線程的第三種方式:實(shí)現(xiàn)Callable接口

  • 9.Object類中的wait和notify方法

  • 10.生產(chǎn)者和消費(fèi)者

1.線程安全問題

2.1、為什么這個(gè)是重點(diǎn)?
??以后在開發(fā)中,我們的項(xiàng)目都是運(yùn)行在服務(wù)器當(dāng)中,而服務(wù)器已經(jīng)將線程的定義,線程對(duì)象的創(chuàng)建,線程的啟動(dòng)等,都已經(jīng)實(shí)現(xiàn)完了。這些代碼我們都不需要編寫。
??最重要的是:你要知道,你編寫的程序需要放到一個(gè)多線程的環(huán)境下運(yùn)行,你更需要關(guān)注的是這些數(shù)據(jù)在多線程并發(fā)的環(huán)境下是否是安全的。(重點(diǎn):*****)
2.2、什么時(shí)候數(shù)據(jù)在多線程并發(fā)的環(huán)境下會(huì)存在安全問題呢?
Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例
??三個(gè)條件:
??條件1:多線程并發(fā)。
??條件2:有共享數(shù)據(jù)。
??條件3:共享數(shù)據(jù)有修改的行為。

??滿足以上3個(gè)條件之后,就會(huì)存在線程安全問題。
2.3、怎么解決線程安全問題呢?
當(dāng)多線程并發(fā)的環(huán)境下,有共享數(shù)據(jù),并且這個(gè)數(shù)據(jù)還會(huì)被修改,此時(shí)就存在線程安全問題,怎么解決這個(gè)問題?
??線程排隊(duì)執(zhí)行。(不能并發(fā))。
??用排隊(duì)執(zhí)行解決線程安全問題。
??這種機(jī)制被稱為:線程同步機(jī)制。

??專業(yè)術(shù)語叫做:線程同步,實(shí)際上就是線程不能并發(fā)了,線程必須排隊(duì)執(zhí)行
??怎么解決線程安全問題呀?
??使用“線程同步機(jī)制”。
??線程同步就是線程排隊(duì)了,線程排隊(duì)了就會(huì)犧牲一部分效率,沒辦法,數(shù)據(jù)安全第一位,只有數(shù)據(jù)安全了,我們才可以談效率。數(shù)據(jù)不安全,沒有效率的事兒。
2.4、說到線程同步這塊,涉及到這兩個(gè)專業(yè)術(shù)語:
異步編程模型:
??線程t1和線程t2,各自執(zhí)行各自的,t1不管t2,t2不管t1,
??誰也不需要等誰,這種編程模型叫做:異步編程模型。
??其實(shí)就是:多線程并發(fā)(效率較高。)

同步編程模型:
??線程t1和線程t2,在線程t1執(zhí)行的時(shí)候,必須等待t2線程執(zhí)行結(jié)束,或者說在t2線程執(zhí)行的時(shí)候,必須等待t1線程執(zhí)行結(jié)束,兩個(gè)線程之間發(fā)生了等待關(guān)系,這就是同步編程模型。效率較低。線程排隊(duì)執(zhí)行。

異步就是并發(fā)。同步就是排隊(duì)。

2.賬戶取款案例

Account類

package ThreadSafe;public class Account {
	//賬號(hào)
	private String actno;
	//余額
	private double balance;
	
	public Account(String actno, double balance) {
		super();
		this.actno = actno;
		this.balance = balance;
	}
	public String getActno() {
		return actno;
	}
	public void setActno(String actno) {
		this.actno = actno;
	}
	public double getBalance() {
		return balance;
	}
	public void setBalance(double balance) {
		this.balance = balance;
	}
	//取款的方法
	public void withdraw(double money){
		//t1和t2并發(fā)執(zhí)行這個(gè)方法(t1和t2是兩個(gè)棧 ,兩個(gè)棧操作堆中同一個(gè)對(duì)象)
		//取款之前的余額
		double before=this.getBalance();
		//取款之后的余額
		double after=before-money;
		//模擬一下網(wǎng)絡(luò)延遲,會(huì)出現(xiàn)問題
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//更新余額
		//思考:t1執(zhí)行到這里了,但還沒有來得及執(zhí)行這行代碼,t2線程進(jìn)來withdraw方法了,此時(shí)一定出問題
		this.setBalance(after);
	}}AccountThread類public class AccountThread extends Thread{
	//兩個(gè)線程必須共享一個(gè)賬戶對(duì)象
	private Account act;
	//通過構(gòu)造方法傳遞過來賬戶對(duì)象
	public AccountThread(Account act) {
		this.act = act;
	}
	@Override
	public void run() {
		//假設(shè)取款5000
		double money=5000;
		//多線程執(zhí)行這個(gè)方法
		act.withdraw(money);
		System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功"+act.getBalance());
	}}

Test類

public class Test {
	public static void main(String[] args) {
		//創(chuàng)建賬戶對(duì)象
		Account act=new Account("act-001",10000);
		//創(chuàng)建兩個(gè)線程
		Thread t1=new AccountThread(act);
		Thread t2=new AccountThread(act);
		
		t1.setName("t1");
		t2.setName("t2");
		//啟動(dòng)兩個(gè)線程執(zhí)行
		t1.start();
		t2.start();
	}}

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例

3.同步代碼塊synchronized

synchronized的理解

//以下這幾行代碼必須是線程排隊(duì)的,不能并發(fā)
//一個(gè)線程把這里的代碼全部執(zhí)行結(jié)束后,另一個(gè)線程才能進(jìn)來
/*
線程同步機(jī)制的語法是
synchronized(){
//線程同步代碼塊。
}

synchronized后面小括號(hào)中傳的這個(gè)數(shù)據(jù)是相當(dāng)關(guān)鍵的,
這個(gè)數(shù)據(jù)必須是多線程共享的數(shù)據(jù),才能達(dá)到多線程排隊(duì)。
()中寫什么?
那要看你想讓哪些線程同步
假設(shè)t1、t2、t3、t4、t5有5個(gè)線程
你只希望t1 t2 t3排隊(duì),t4 t5不需要排隊(duì),怎么辦
你一定要在()中寫一個(gè)t1 t2 t3共享的對(duì)象,而這個(gè)對(duì)象對(duì)于t4 t5來說不是共享的
這里的共享對(duì)象是賬戶對(duì)象
賬戶對(duì)象是共享的,this是賬戶對(duì)象
有時(shí)不一定是this,這里只要是多線程共享的那個(gè)對(duì)象就行

		synchronized(this){
			double before=this.getBalance();
			double after=before-money;
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.setBalance(after);
		}

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例在java語言中,任何對(duì)象都有一把鎖,其實(shí)這把鎖就是一個(gè)標(biāo)記,(只是把它叫做鎖)
100個(gè)對(duì)象,100個(gè)鎖,1個(gè)對(duì)象1把鎖。
以上代碼的執(zhí)行原理是什么呢?
1.假設(shè)t1和t2線程并發(fā),開始執(zhí)行以上代碼的時(shí)候,肯定有一個(gè)先一個(gè)后,
2.假設(shè)t1先執(zhí)行了,遇到了synchronized,這個(gè)時(shí)候自動(dòng)找后面共享對(duì)象的對(duì)象鎖,找到之后,并占有這把鎖,然后執(zhí)行同步代碼塊中的程序,在程序執(zhí)行過程中一直都占有這把鎖,直到同步代碼塊執(zhí)行結(jié)束,這把鎖才會(huì)釋放。
3.假設(shè)t1已經(jīng)占有這把鎖,此時(shí)t2也遇到synchronized關(guān)鍵字,也會(huì)去占有后面共享對(duì)象的這把鎖,結(jié)果這把鎖被t1占有,t2只能在同步代碼塊外邊等待t1的結(jié)束,直到t1把同步代碼塊執(zhí)行結(jié)束了,t1會(huì)歸還這把鎖,此時(shí)t2終于等到這把鎖,然后t2占有這把鎖之后,進(jìn)入同步代碼塊執(zhí)行程序。
這樣就達(dá)到了線程排隊(duì)執(zhí)行
這里需要注意的是:這個(gè)共享對(duì)象一定要選好了,這個(gè)共享對(duì)象一定是你需要排隊(duì)執(zhí)行的這些線程對(duì)象所共享的。
//對(duì)象
Object obj=new Object(); //實(shí)例變量(Account對(duì)象是多線程共享的,Account對(duì)象中的實(shí)例變量obj也是共享的)
synchronized(obj){}

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例
括號(hào)里邊只要是共享對(duì)象就行。
Object obj2=new Object(); //局部變量
synchronized(obj2){}
這樣寫就不安全了,因?yàn)閛bj2是局部變量,不是共享對(duì)象。
synchronized(“abc”){}
這樣寫時(shí)可以的。存在字符串常量池中
寫"abc"的話所有線程都會(huì)同步

而如果是寫synchronized(this){}的話,我們創(chuàng)建了一個(gè)新的對(duì)象act2可不用共享對(duì)象。
所以最好是寫synchronized(this){},比如你要取款,要讓其他取別的賬戶的人也要等嗎?不應(yīng)該,只有同時(shí)對(duì)你這1個(gè)賬戶取款的時(shí)候,需要等待,別人取錢的時(shí)候,需要從其他賬戶中取錢,就不需要等待。

java中有三大變量的線程安全問題

實(shí)例變量,在堆中
靜態(tài)變量,在方法區(qū)
局部變量,在棧中

以上三大變量
局部變量永遠(yuǎn)不會(huì)存在線程安全問題
因?yàn)榫植孔兞坎还蚕恚ㄒ粋€(gè)線程一個(gè)棧)
局部變量在棧中,所以局部變量永遠(yuǎn)都不會(huì)共享
實(shí)例變量在堆中,堆只有1個(gè)。
靜態(tài)變量在方法區(qū)中,方法區(qū)只有1個(gè)

堆和方法區(qū)都是多線程共享的,所以可能存在線程安全問題

局部變量+常量:不會(huì)有線程安全問題。
成員變量:可能會(huì)有線程安全問題

同步代碼塊越小,效率越高。

//多線程執(zhí)行這個(gè)方法//synchronized(this)//這里的this是AccountThread對(duì)象,這個(gè)對(duì)象不共享。synchronized(act){
			act.withdraw(money);
			System.out.println(Thread.currentThread().getName()+"賬戶"+act.getActno()+"取款成功,余額:"+act.getBalance());}

在實(shí)例方法上使用synchronized

在實(shí)例方法上可以使用synchronized嗎?可以的。
synchronized出現(xiàn)在實(shí)例方法上,一定鎖的是this
沒得挑,只能是this,不能是其他的對(duì)象了
所以這種方式不靈活。
另外還有一個(gè)缺點(diǎn):synchronized出現(xiàn)在實(shí)例方法上,表示整個(gè)方法體都需要同步
可能會(huì)擴(kuò)大同步的范圍,導(dǎo)致程序的執(zhí)行效率降低。所以這種方式不常用。
synchronized使用在實(shí)例方法上有什么優(yōu)點(diǎn)?
代碼寫的少了,節(jié)儉了。
如果共享的對(duì)象是this,并且需要同步的代碼是整個(gè)方法體,建議使用這種方式
StringBuffer就是在每個(gè)方法上加了synchronized關(guān)鍵字

使用局部變量的話,最好使用StringBuilder
因?yàn)榫植孔兞坎淮嬖诰€程安全問題,選擇StringBuilder,StringBuffer效率比較低。
ArrayList是非線程安全的。
Vector是線程安全的。
HashMap HashSet是非線程安全的。
Hashtable是線程安全的

總結(jié)

synchronized有三種寫法:

??第一種:同步代碼塊
????靈活
??????synchronized(線程共享對(duì)象){
????????同步代碼塊;
??????}
??第二種:在實(shí)例方法上使用synchronized
??????表示共享對(duì)象一定是this
??????并且同步代碼塊是整個(gè)方法體。
??第三種:在靜態(tài)方法上使用synchronized
??????表示找類鎖。
??????類鎖永遠(yuǎn)只有1把。
??????就算創(chuàng)建了100個(gè)對(duì)象,那類鎖也只有一把。

對(duì)象鎖:1個(gè)對(duì)象1把鎖,100個(gè)對(duì)象100把鎖。
類鎖:100個(gè)對(duì)象,也可能只是1把類鎖。

面試題

//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//不需要,因?yàn)閐oother方法沒有synchronized

public class exam01 {
	public static void main(String[] args) {
		MyClass mc=new MyClass();
		Thread t1=new MyThread(mc);
		Thread t2=new MyThread(mc);
		t1.setName("t1");
		t2.setName("t2");
		t1.start();
		try {
			Thread.sleep(1000);		//這個(gè)睡眠的作用是:為了保證t1線程先執(zhí)行
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		t2.start();
	}}class MyThread extends Thread{
	private MyClass mc;
	public MyThread(MyClass mc) {
		super();
		this.mc = mc;
	}
	public void run(){
		if(Thread.currentThread().getName().equals("t1")){
			mc.dosome();
		}
		if(Thread.currentThread().getName().equals("t2")){
			mc.doOther();
		}
	}
	}class MyClass{
	public synchronized void dosome(){
		System.out.println("doSome begin");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("doSome end");
	}
	public void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}}

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例 當(dāng)在doother上面加了synchronized呢
//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//需要,因?yàn)閐oother方法沒有synchronized

	public synchronized void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}

//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//不用排隊(duì),誰也不用管誰

MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);

//面試題:doother方法的執(zhí)行需不需要等待dosome方法的結(jié)束。
//需要,因?yàn)殪o態(tài)方法是類鎖,類鎖不管創(chuàng)建了幾個(gè)對(duì)象,類鎖只有一把

MyClass mc1=new MyClass();MyClass mc2=new MyClass();Thread t1=new MyThread(mc1);Thread t2=new MyThread(mc2);public synchronized static void dosome(){
		System.out.println("doSome begin");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("doSome end");
	}
	public synchronized static void doOther(){
		System.out.println("doOther begin");
		System.out.println("doOther end");
	}

這種鎖叫排他鎖:

4.死鎖

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例
synchronized在開發(fā)中最好不要嵌套使用,一不小心就可能導(dǎo)致死鎖。
死鎖代碼要會(huì)寫。
一般面試官要求你會(huì)寫
只有會(huì)寫的,才會(huì)在以后的開發(fā)中注意這個(gè)事兒
因?yàn)樗梨i很難調(diào)試。

public class DeadLock {
	public static void main(String[] args) {
		Object o1=new Object();
		Object o2=new Object();
		//t1線程和t2線程共享o1,o2
		Thread t1=new MyThread1(o1,o2);
		Thread t2=new MyThread2(o1,o2);
		t1.start();
		t2.start();
	}}class MyThread1 extends Thread{
	Object o1;
	Object o2;
	public MyThread1(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		synchronized (o1) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (o2) {
				
			}
		}
	}}class MyThread2 extends Thread{
	Object o1;
	Object o2;
	public MyThread2(Object o1,Object o2){
		this.o1=o1;
		this.o2=o2;
	}
	@Override
	public void run() {
		synchronized (o2) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			synchronized (o1) {
				
			}
		}
	}}

5.開發(fā)中應(yīng)該怎么解決線程安全問題

聊一聊,我們以后開發(fā)中應(yīng)該怎么解決線程安全問題?
??是一上來就選擇線程同步嗎?synchronized
??不是,synchronized會(huì)讓程序的執(zhí)行效率降低,用戶體驗(yàn)不好。
??系統(tǒng)的用戶吞吐量降低。用戶體驗(yàn)差。在不得已的情況下再選擇
??線程同步機(jī)制。
??第一種方案:盡量使用局部變量代替“實(shí)例變量和靜態(tài)變量”。
??第二種方案:如果必須是實(shí)例變量,那么可以考慮創(chuàng)建多個(gè)對(duì)象,這樣實(shí)例變量的內(nèi)存就不共享了。(一個(gè)線程對(duì)應(yīng)1個(gè)對(duì)象,100個(gè)線程對(duì)應(yīng)100個(gè)對(duì)象,對(duì)象不共享,就沒有數(shù)據(jù)安全問題了。)
??第三種方案:如果不能使用局部變量,對(duì)象也不能創(chuàng)建多個(gè),這個(gè)時(shí)候就只能選擇synchronized了。線程同步機(jī)制。
線程這塊還有那些內(nèi)容呢?列舉一下
7.1、守護(hù)線程
7.2、定時(shí)器
7.3、實(shí)現(xiàn)線程的第三種方式:FutureTask方式,實(shí)現(xiàn)Callable接口。(JDK8新特性。)
7.4、關(guān)于Object類中的wait和notify方法。(生產(chǎn)者和消費(fèi)者模式!)

6.守護(hù)線程

守護(hù)線程
java語言中線程分為兩大類:
??一類是:用戶線程
??一類是:守護(hù)線程(后臺(tái)線程)

其中具有代表性的就是:垃圾回收線程(守護(hù)線程)。
??守護(hù)線程的特點(diǎn):
??一般守護(hù)線程是一個(gè)死循環(huán),所有的用戶線程只要結(jié)束,
??守護(hù)線程自動(dòng)結(jié)束。
??注意:主線程main方法是一個(gè)用戶線程。
??守護(hù)線程用在什么地方呢?
??每天00:00的時(shí)候系統(tǒng)數(shù)據(jù)自動(dòng)備份。
??這個(gè)需要使用到定時(shí)器,并且我們可以將定時(shí)器設(shè)置為守護(hù)線程。
??一直在那里看著,沒到00:00的時(shí)候就備份一次。所有的用戶線程如果結(jié)束了,守護(hù)線程自動(dòng)退出,沒有必要進(jìn)行數(shù)據(jù)備份了。

package testThread;/*
實(shí)現(xiàn)守護(hù)線程
 * */
	public class ThreadTest13 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Thread t=new BakDataThread();
		t.setName("備份數(shù)據(jù)的線程");
		//啟動(dòng)之前,將線程設(shè)置為守護(hù)線程
		t.setDaemon(true);
		t.start();
		//主線程:主線程是用戶線程
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+"---->"+i);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				// TODO: handle exception
			}
		}
	}}class BakDataThread extends Thread{
	@Override
	public void run() {
		int i=0;
		//即使是死循環(huán),但由于該線程是守護(hù)者,當(dāng)用戶線程結(jié)束,守護(hù)線程自動(dòng)終止
		while(true){
			System.out.println(Thread.currentThread().getName()+"---->"+(++i));
			try {
				Thread.sleep(1000);
			} catch (Exception e) {
				// TODO: handle exception
			}
			
		}
	}}

7.定時(shí)器

定時(shí)器的作用:
??間隔特定的時(shí)間,執(zhí)行特定的程序。
??每周要進(jìn)行銀行賬戶的總賬操作。
??每天要進(jìn)行數(shù)據(jù)的備份操作。
??在實(shí)際的開發(fā)中,每隔多久執(zhí)行一段特定的程序,這種需求是很常見的,
那么在java中其實(shí)可以采用多種方式實(shí)現(xiàn):
??可以使用sleep方法,睡眠,設(shè)置睡眠時(shí)間,沒到這個(gè)時(shí)間點(diǎn)醒來,執(zhí)行任務(wù)。這種方式是最原始的定時(shí)器。(比較low)
??在java的類庫中已經(jīng)寫好了一個(gè)定時(shí)器:java.util.Timer,可以直接拿來用。不過,這種方式在目前的開發(fā)中也很少用,因?yàn)楝F(xiàn)在有很多高級(jí)框架都是支持定時(shí)任務(wù)的。

??在實(shí)際的開發(fā)中,目前使用較多的是Spring框架中提供的SpringTask框架,這個(gè)框架只要進(jìn)行簡(jiǎn)單的配置,就可以完成定時(shí)器的任務(wù)。

8.實(shí)現(xiàn)線程的第三種方式:實(shí)現(xiàn)Callable接口

實(shí)現(xiàn)線程的第三種方式:實(shí)現(xiàn)Callable接口。(JDK8新特性。)
這種方式實(shí)現(xiàn)的線程可以獲取線程的返回值
之前講解的那兩種方式是無法獲取線程返回值的,因?yàn)閞un方法返回void。
思考:
系統(tǒng)委派一個(gè)線程去執(zhí)行一個(gè)任務(wù),該線程執(zhí)行完任務(wù)之后,可能會(huì)有一個(gè)執(zhí)行結(jié)果,我們?cè)趺茨苣玫竭@個(gè)執(zhí)行結(jié)果呢?
使用第三種方式:實(shí)現(xiàn)Callable接口方式。

public class ThreadTest14 {
	public static void main(String[] args) throws Exception, ExecutionException {
		//第一步:創(chuàng)建一個(gè)未來任務(wù)類對(duì)象
		//參數(shù)非常重要,需要給一個(gè)callable接口的實(shí)現(xiàn)類對(duì)象
		FutureTask task=new FutureTask(new Callable(){
			@Override
			//call方法相當(dāng)于是run方法,只不過這個(gè)有返回值,線程執(zhí)行一個(gè)任務(wù),執(zhí)行之后可能會(huì)有一個(gè)執(zhí)行結(jié)果。
			public Object call() throws Exception {
				System.out.println("call method begin");
				Thread.sleep(1000);
				System.out.println("call method begin");
				int a=100;
				int b=200;
				return a+b;			//自動(dòng)裝箱
			}		
		});
		//創(chuàng)建線程對(duì)象
		Thread t=new Thread(task);
		
		//啟動(dòng)線程
		t.start();
		
		//這里是main方法,這是在主線程中
		//在線程中,怎么獲取t線程的執(zhí)行結(jié)果
		//get方法的執(zhí)行會(huì)導(dǎo)致當(dāng)前線程阻塞
		Object obj=task.get();
		System.out.println("線程執(zhí)行結(jié)果"+obj);
		//main方法這里的程序要想執(zhí)行必須等待get()方法的結(jié)束
		//而get方法可能需要很久。因?yàn)間et()方法是為了拿另一個(gè)線程的執(zhí)行結(jié)果。
		//另一個(gè)線程的執(zhí)行是需要時(shí)間的
		System.out.println("hello,world");
	}}

這種方式的優(yōu)點(diǎn):可以獲取到線程的執(zhí)行結(jié)果
這種方式的缺點(diǎn):效率比較低,在獲取t線程執(zhí)行結(jié)果的時(shí)候,當(dāng)前線程受阻塞,效率較低。

9.Object類中的wait和notify方法

(生產(chǎn)者和消費(fèi)者模式!)
??第一:wait和notify方法不是線程對(duì)象的方法,是java中任何一個(gè)java對(duì)象都有的方法,因?yàn)檫@兩個(gè)方式是Object類中自帶的。
??wait方法和notify方法不是通過線程對(duì)象調(diào)用,

??不是這樣的:t.wait(),也不是這樣的:t.notify()…不對(duì)。
??第二:wait()方法作用?
????Object o = new Object();
????o.wait();
??表示:
??讓正在o對(duì)象上活動(dòng)的線程進(jìn)入等待狀態(tài),無期限等待,直到被喚醒為止。
??o.wait();方法的調(diào)用,會(huì)讓“當(dāng)前線程(正在o對(duì)象上活動(dòng)的線程)”進(jìn)入等待狀態(tài)
??第三:notify()方法作用?
????Object o = new Object();
????o.notify();
??表示:
??喚醒正在o對(duì)象上等待的線程
??還有一個(gè)notifyAll()方法:
??這個(gè)方法是喚醒o對(duì)象上處于等待的所有線程。

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例

10.生產(chǎn)者和消費(fèi)者

Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例
1.使用wait方法和notify方法實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者模式
2.什么是生產(chǎn)者和消費(fèi)者模式?
生產(chǎn)線程負(fù)責(zé)生產(chǎn),消費(fèi)線程負(fù)責(zé)消費(fèi)
生產(chǎn)線程和消費(fèi)線程要達(dá)到均衡
這是一種特殊的業(yè)務(wù)需求,在這種特殊的情況下需要使用wait方法和notify方法
3.wait和notify方法不是線程對(duì)象的方法,是普通java對(duì)象都有的方法
4.wait方法和notify方法是建立在線程同步的基礎(chǔ)之上。因?yàn)槎嗑€程要同時(shí)操作一個(gè)倉庫,有線程安全問題
5.wait方法作用:o.wait()讓正在o對(duì)象上活動(dòng)的線程t進(jìn)入等待狀態(tài),并且釋放掉t線程之前占有的o對(duì)象的鎖
6.notify方法的作用:o.notify()讓正在o對(duì)象上等待的線程喚醒,只是5通知,不會(huì)釋放o對(duì)象上之前占有的鎖
7.模擬這樣一個(gè)需求:
倉庫我們采用list集合
list集合中假設(shè)只能存儲(chǔ)1個(gè)元素
1個(gè)元素就表示倉庫滿了
如果list集合中的元素個(gè)數(shù)是0,就表示倉庫空了。
保證list集合中永遠(yuǎn)都是最多存儲(chǔ)1個(gè)元素
必須做到這種效果,生產(chǎn)1個(gè)消費(fèi)1個(gè)。

public class ThreadTest15 {
	public static void main(String[] args) {
		//創(chuàng)建一個(gè)倉庫獨(dú)享,共享的
		List list=new ArrayList();
		//創(chuàng)建兩個(gè)線程對(duì)象
		//生產(chǎn)者線程
		Thread t1=new Thread(new Producer(list));
		//消費(fèi)者線程
		Thread t2=new Thread(new Consumer(list));
		t1.setName("生產(chǎn)者線程");
		t2.setName("消費(fèi)者線程");
		t1.start();
		t2.start();
	}}//生產(chǎn)線程class Producer implements Runnable{
	//倉庫
	private List list;
	public Producer(List list) {
		this.list = list;
	}
	public void run() {
		//一直生產(chǎn)
		while(true){
			//給倉庫對(duì)象list加鎖
			synchronized (list) {
				if(list.size()>0){	//大于0說明倉庫中已經(jīng)有1個(gè)元素了
					//當(dāng)前線程進(jìn)入等待狀態(tài),并且釋放list集合的鎖
					try {
						list.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				//程序能夠執(zhí)行到這里說明倉庫是空的,可以生產(chǎn)
				Object obj =new Object();
				list.add(obj);
				System.out.println(Thread.currentThread().getName()+"---->"+obj);
				//喚醒消費(fèi)者進(jìn)行消費(fèi)
				list.notifyAll();
			}
			
		}
	}}//消費(fèi)線程class Consumer implements Runnable{
	//倉庫
	private List list;
	public Consumer(List list) {
		this.list = list;
	}
	public void run() {
		//一直消費(fèi)
		while(true){
			//給倉庫對(duì)象list加鎖
			synchronized (list) {
				if(list.size()==0){	//倉里已經(jīng)空了
					try {
						//倉庫已經(jīng)空了
						//消費(fèi)者線程等待,并釋放掉list集合鎖
						list.wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}
			Object obj=list.remove(0);
			System.out.println(Thread.currentThread().getName()+"---->"+obj);
			//喚醒生產(chǎn)者生產(chǎn)
			list.notifyAll();
		}
	}}

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“Java并發(fā)編程之介紹線程安全基礎(chǔ)的示例”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI