溫馨提示×

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

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

什么是synchronized

發(fā)布時(shí)間:2021-09-29 16:39:50 來(lái)源:億速云 閱讀:142 作者:iii 欄目:大數(shù)據(jù)

這篇文章主要講解了“什么是synchronized”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“什么是synchronized”吧!

synchronized使用

  1. 上面也說(shuō)了,synchronized可以寫在方法上,也可以寫在代碼塊上,只是有可能鎖的類型不同,如果寫在方法上,鎖的就是當(dāng)前類的對(duì)象,在代碼塊上使用,可以指定鎖的對(duì)象。如果作用在靜態(tài)代碼塊上,鎖的就是指定的類的class對(duì)象了

  2. synchronized鎖屬于全自動(dòng)類型的,自動(dòng)上鎖解鎖,而且在執(zhí)行代碼的過(guò)程中,如果拋異常了,synchronized也會(huì)自動(dòng)釋放鎖

  3. synchronized鎖是可重入鎖,就是當(dāng)前線程調(diào)用A的方法,A方法中調(diào)用B方法,可以直接調(diào)用,不用競(jìng)爭(zhēng)鎖。jvm這樣設(shè)計(jì),也是為了防止死鎖的產(chǎn)生,所以,在這里你應(yīng)該也能明白了,為什么當(dāng)前線程訪問(wèn)A方法時(shí),其他線程訪問(wèn)其他方法時(shí),不能讓其訪問(wèn)的原因了?;蛘哌@樣說(shuō),T1線程訪問(wèn)A方法時(shí),T2線程也來(lái)訪問(wèn)A方法,肯定不被允許的,這不不用多說(shuō),因?yàn)殒i的是一個(gè)對(duì)象,那個(gè)對(duì)象被T1線程鎖著呢,T2線程當(dāng)然獲取不了鎖。那相同的,T2線程去訪問(wèn)B方法時(shí),B方法中鎖的不也是同一個(gè)對(duì)象嗎,也就是說(shuō)T2線程還要去獲取T1線程在A方法中鎖定的對(duì)象的鎖。

  4. threadlocal線程私有變量,舉個(gè)例子你就知道了:

public class ThreadLocalTest {
	class Person {
		String name = "zhangsan";
	}
	@Test
	public void threadLocal1() {
		ThreadLocal<Person> tl = new ThreadLocal<Person>();
		Person person = new Person();
		tl.set(person);
		new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("新啟線程"+tl.get());
			}
		}).start();
		System.out.println(tl.get());
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

新啟的線程打印person為null,也就是說(shuō),通過(guò)threadlocal設(shè)置的對(duì)象,只能在當(dāng)前線程中使用,其他線程獲取為空。

  1. volatile關(guān)鍵字,請(qǐng)點(diǎn)擊跳轉(zhuǎn)

  2. 重點(diǎn)說(shuō)下notify和wait,因?yàn)檫@里面的坑,稍不注意就掉進(jìn)去了,之前寫過(guò)一篇,我再舉個(gè)生產(chǎn)者、消費(fèi)者的例子: 需求:有2個(gè)生產(chǎn)者線程、10個(gè)消費(fèi)者線程,如果容器滿了,則生產(chǎn)者暫停,如果容器空了,則消費(fèi)者暫停

public class ProducerAndConsumer {
	//有2個(gè)生產(chǎn)者線程、10個(gè)消費(fèi)者線程,如果容器滿了,則生產(chǎn)者暫停,如果容器空了,則消費(fèi)者暫停
	final private LinkedList list = new LinkedList();
	final private int maxValue = 10;
	/**
	 * 生產(chǎn)者,按照要求:當(dāng)容器滿了,就停止。
	 */
	public synchronized void producer(Object o) {
		/**
		 * 最初的實(shí)現(xiàn)是用if,但是為什么又改成if而改成while呢:(這是一個(gè)坑,很容易掉里面)
		 * 	1.你想啊,如果此時(shí),容器滿了,然后第一個(gè)線程到這后,判斷if邏輯,然后執(zhí)行wait方法進(jìn)行等待,并【釋放當(dāng)前對(duì)象鎖】
		 *  2.然后cpu調(diào)起哪個(gè)線程,是隨機(jī)選擇的,如果此時(shí),又調(diào)起了一個(gè)生產(chǎn)者線程,那么也執(zhí)行到了這個(gè)wait方法。
		 *  3.目前已經(jīng)有兩個(gè)線程在wait這了,然后cpu執(zhí)行消費(fèi)者線程消費(fèi)了1個(gè),現(xiàn)在容器中還有9個(gè),
		  *        然后消費(fèi)者線程喚醒通過(guò)notifyall喚醒線程,喚醒了這兩個(gè)生產(chǎn)者線程,那么:
		 *  	3.1 首先第一個(gè)生產(chǎn)者線程開始執(zhí)行,從wait處開始往下執(zhí)行,添加元素,容器又滿了,釋放鎖
		 *  	3.2 然后第二個(gè)生產(chǎn)者線程開始執(zhí)行,也是從wait處開始執(zhí)行,因?yàn)槭褂玫牟皇莣hile循環(huán),所以
		 *  		會(huì)直接往下執(zhí)行,也就是添加元素,那么容器大小就不是10 個(gè)了。
		 *  4.所以,使用wait時(shí)的場(chǎng)景,99%都是與while循環(huán)配合使用
		 *  5.如果wait和notify不寫在synchronized鎖里面,會(huì)拋IllegalMonitorStateException異常,但是
		 *  	為什么必須在synchronized鎖中呢,那么肯定會(huì)出現(xiàn)wait調(diào)用發(fā)生在調(diào)用notify之后的情況。[參考博客](https://www.cnblogs.com/set-cookie/p/8686218.html)
		 */
//		if(list.size()==maxValue) {
		while(list.size()==maxValue) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		list.add(o);
		//容器中有東西了,喚醒等待的線程線程進(jìn)行消費(fèi)(喚醒的線程中包括生產(chǎn)者線程和消費(fèi)者線程),其主要目的是消費(fèi)者線程
		this.notifyAll();
	}
	/**
	 * 消費(fèi)者
	 */
	public  synchronized Object consumer() {
		while(list.size()==0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		//我拿走東西了,喚醒等待的線程進(jìn)行生產(chǎn)(喚醒的線程中包括生產(chǎn)者線程和消費(fèi)者線程),其主要目的是生產(chǎn)者線程
		Object o = list.removeFirst();
		this.notifyAll();
		return o;
	}
	public static void main(String[] args) {
		ProducerAndConsumer pcTest = new ProducerAndConsumer();
		//啟動(dòng)消費(fèi)者線程
		for(int i=0;i<10;i++) {
			new Thread(()->{
				for(int j=0;j<10;j++) {
					System.out.println(pcTest.consumer()+"aa");
				}
			},"c"+i) .start();
		}
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//啟動(dòng)生產(chǎn)者線程
		for(int i=0;i<2;i++) {
			new Thread(()->{
				for(int j=0;j<30;j++) {
					pcTest.producer(Thread.currentThread().getName());
				}
			},"p"+i) .start();
		}
	}
  1. 線程停止stop、interrupt方法, thread.stop(),是直接把你干掉了,沒(méi)有一點(diǎn)余地的直接kill掉,都不會(huì)進(jìn)入catch中,所以不到萬(wàn)不得已,不要用stop方法。 thread.interrupt()時(shí),線程還是可以進(jìn)下面catch中的代碼的。 但是這兩個(gè)方法都不被推薦,更理想的辦法是定義變量,:

public class ThreadStopByArgs {
	/**
	 * 通過(guò)變量的形式,是線程進(jìn)行停止
	 *
	 */
	public static void main(String[] args) {
		ThreadRunnerTest threadRunnerTest = new ThreadRunnerTest();
		Thread thread = new Thread(threadRunnerTest);
		thread.start();
		try {
			TimeUnit.SECONDS.sleep(2);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("stop");
		threadRunnerTest.changeFlag();
	}
}
class ThreadRunnerTest implements Runnable {
	private volatile boolean flag = true;
	public void run() {
		int i=0;
		while(flag) {
			//System.out.println(i++);測(cè)試多線程時(shí),如果使用syso輸出,要小心使用,因?yàn)閟yso中有sync
		}
	}
	public void changeFlag(){
		flag = false;
	}
}
  1. 其他的方法,比如sleep、yield、join、stop、interrupt等方法,請(qǐng)您自行學(xué)習(xí)吧,沒(méi)啥好記得 鎖必須鎖的是同一個(gè)對(duì)象的基礎(chǔ)上的,如同:你衛(wèi)生間還能有多個(gè)門嗎?也就是說(shuō),訪問(wèn)共享的資源時(shí),只有一個(gè)門能讓你去訪問(wèn),這樣才能控制共享資源和線程的安全性。安全的意義并不只針對(duì)車禍那種類型的,只要預(yù)期結(jié)果被改變是因?yàn)樵趫?zhí)行過(guò)程中被其他因素干擾導(dǎo)致的,就屬于不安全。

感謝各位的閱讀,以上就是“什么是synchronized”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)什么是synchronized這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(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