日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

多线程 JUC

發(fā)布時(shí)間:2025/3/21 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 多线程 JUC 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2019獨(dú)角獸企業(yè)重金招聘Python工程師標(biāo)準(zhǔn)>>>

多線程 JUC

object.wait()

在其他線程調(diào)用此對象的notify()或者notifyAll()方法,或超過指定時(shí)間量前,當(dāng)前線程T等待(線程T必須擁有該對象的鎖)。線程T被放置在該對象的等待區(qū)中,并釋放鎖。在被喚醒、中斷、超時(shí)的情況下,從對象的等待區(qū)中刪除線程T,并重新進(jìn)行線程調(diào)度。一旦線程T獲得該對象的鎖,該對象上的所有同步申明都被恢復(fù)到調(diào)用wait()方法時(shí)的狀態(tài),然后線程Twait()方法返回。如果當(dāng)前線程在等待之前或在等待時(shí)被任何線程中斷,則會拋出 InterruptedException。在按上述形式恢復(fù)此對象的鎖定狀態(tài)時(shí)才會拋出此異常。在拋出此異常時(shí),當(dāng)前線程的中斷狀態(tài)被清除。

object.notify()

喚醒在此對象鎖上等待的單個(gè)線程。此方法只能由擁有該對象鎖的線程來調(diào)用。

Thread.sleep()

在指定的毫秒數(shù)內(nèi)讓當(dāng)前正在執(zhí)行的線程休眠(暫停執(zhí)行),此操作受到系統(tǒng)計(jì)時(shí)器和調(diào)度程序精度和準(zhǔn)確性的影響。監(jiān)控狀態(tài)依然保持、會自動恢復(fù)到可運(yùn)行狀態(tài),不會釋放對象鎖。如果任何線程中斷了當(dāng)前線程。當(dāng)拋出InterruptedException異常時(shí),當(dāng)前線程的中斷狀態(tài)被清除。讓出CPU分配的執(zhí)行時(shí)間

thread.join():在一個(gè)線程對象上調(diào)用,使當(dāng)前線程等待這個(gè)線程對象對應(yīng)的線程結(jié)束之后繼續(xù)執(zhí)行。

Thread.yield():暫停當(dāng)前正在執(zhí)行的線程對象,并執(zhí)行其他線程(讓出cpu)。

thread.interrupt():中斷線程,停止其正在進(jìn)行的一切。中斷一個(gè)不處于活動狀態(tài)的線程不會有任何作用。如果線程在調(diào)用Object類的wait()方法、或者join()、sleep()方法過程中受阻,則其中斷狀態(tài)將被清除,并收到一個(gè)InterruptedException。

Thread.interrupted()檢測當(dāng)前線程是否已經(jīng)中斷,并且清除線程的中斷狀態(tài)(回到非中斷狀態(tài))。

Synchronized關(guān)鍵字:編譯后會在同步塊前后分別形成monitorenter和monitorexit這兩個(gè)字節(jié)碼指令。這兩個(gè)指令都需要一個(gè)引用類型的參數(shù)來指明要鎖定和解鎖的對象。如果沒有明確指定對象參數(shù),那就根據(jù)synchronized修飾的是實(shí)例方法還是類方法,去取對應(yīng)的對象實(shí)例或Class對象來作為鎖對象。在執(zhí)行monitorenter指令時(shí),首先嘗試獲取對象的鎖,如果沒有被鎖定或者當(dāng)前線程已經(jīng)擁有了該對象的鎖,則將鎖計(jì)數(shù)器加1,相應(yīng)的執(zhí)行moniterexit時(shí),將鎖計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí),鎖就被釋放了。如果獲取對象鎖失敗,則當(dāng)前線程就要阻塞等待。

?

在?Java?5.0 提供了?Java.util.concurrent (簡稱JUC )包,在此包中增加了在并發(fā)編程中很常用的實(shí)用工具類,用于定義類似于線程的自定義子系統(tǒng),包括線程池、異步 IO 和輕量級任務(wù)框架。提供可調(diào)的、靈活的線程池。還提供了設(shè)計(jì)用于多線程上下文中的 Collection 實(shí)現(xiàn)等。

一、volatile關(guān)鍵字、內(nèi)存可見性

內(nèi)存可見性

內(nèi)存可見性(Memory Visibility)是指當(dāng)某個(gè)線程正在使用對象狀態(tài)而另一個(gè)線程在同時(shí)修改該狀態(tài),需要確保當(dāng)一個(gè)線程修改了對象狀態(tài)后,其他線程能夠看到發(fā)生的狀態(tài)變化。

可見性錯(cuò)誤是指當(dāng)讀操作與寫操作在不同的線程中執(zhí)行時(shí),我們無法確保執(zhí)行讀操作的線程能適時(shí)地看到其他線程寫入的值,有時(shí)甚至是根本不可能的事情。

我們可以通過同步來保證對象被安全地發(fā)布。除此之外我們也可以使用一種更加輕量級的 volatile 變量。

volatile 關(guān)鍵字

Java 提供了一種稍弱的同步機(jī)制,即 volatile 變量,用來確保多個(gè)線程將變量的更新操作通知到其他線程,可以保證內(nèi)存中的數(shù)據(jù)可見。可以將 volatile 看做一個(gè)輕量級的鎖,但是又與鎖有些不同:

  • 對于多線程,不是一種互斥關(guān)系
  • 不能保證變量狀態(tài)的“原子性操作”

public class TestVolatile {

??? public static void main(String[] args){

???? ???ThreadDemo td=new ThreadDemo();

??????? new Thread(td).start();

??????? while(true){

??????????? if(td.isFlag()){

??????????????? System.out.println("-----------");

??????????????? break;

??????????? }

??????? }

??? }

?

}

class ThreadDemo implements Runnable{

??? private volatile boolean flag=false;

??? public void run() {

??????? try {

??????????? Thread.sleep(200);

??????? } catch (InterruptedException e) {

??????????? e.printStackTrace();

??????? }

??????? flag=true;

??????? System.out.println("flag="+isFlag());

??? }

??? public boolean isFlag(){

??????? return flag;

??? }

??? public void setFlag(boolean flag){

??????? this.flag=flag;

??? }

}

二、原子變量 、CAS

原子變量:jdk1.5 后 java.util.concurrent.atomic 類的小工具包,支持在單個(gè)變量上解除鎖的線程安全編程,包下提供了常用的原子變量:?
- AtomicBoolean 、AtomicInteger 、AtomicLong 、 AtomicReference?
- AtomicIntegerArray 、AtomicLongArray?
- AtomicMarkableReference?
- AtomicReferenceArray?
- AtomicStampedReference

1.類中的變量都是volatile類型:保證內(nèi)存可見性?
2.使用CAS算法:保證數(shù)據(jù)的原子性

CAS (Compare-And-Swap) 是一種硬件對并發(fā)的支持,針對多處理器操作而設(shè)計(jì)的處理器中的一種特殊指令,用于管理對共享數(shù)據(jù)的并發(fā)訪問。?
CAS 是一種無鎖的非阻塞算法的實(shí)現(xiàn)。?
CAS包含三個(gè)操作數(shù):?
內(nèi)存值 V?
預(yù)估(舊)值 A?
更新值 B?
當(dāng)且僅當(dāng)V==A時(shí),B的值才更新給A,否則將不做任何操作。

public class TestAtomicDemo {

?

??? public static void main(String[] args) {

??????? AtomicDemo ad = new AtomicDemo();

?

??????? for (int i = 0; i < 10; i++) {

??????????? new Thread(ad).start();

??????? }

??? }

?

}

?

class AtomicDemo implements Runnable{

?

//? private volatile int serialNumber = 0;

?

??? private AtomicInteger serialNumber = new AtomicInteger(0);

?

??? @Override

??? public void run() {

?

??????? try {

??????????? Thread.sleep(200);

??????? } catch (InterruptedException e) {

??????? }

?

??????? System.out.println(getSerialNumber());

??? }

?

public int getSerialNumber(){

// return serialNumber++;

??????? return serialNumber.getAndIncrement();//i++ 實(shí)際是int temp=i;i=i+1;i=temp; 需要原子性操作

??? }

}

使用synchronized方法模擬CAS 算法,實(shí)際是由硬件機(jī)制完成的,用10個(gè)線程代表對內(nèi)存中數(shù)據(jù)的10次修改請求。只有上個(gè)線程修改完,這個(gè)線程從內(nèi)存中獲取的內(nèi)存值當(dāng)成期望值,才等于內(nèi)存值,才能對內(nèi)存值進(jìn)行修改。

public class TestCompareAndSwap {

??? public static void main(String[] args) {

??????? final CompareAndSwap cas=new CompareAndSwap();

?

??????? for(int i=0;i<10;i++){

??????????? new Thread(new Runnable(){

??????????????? @Override

??????????????? public void run() {

??????????????????? int expectedValue=cas.get();

??????????????????? boolean b=cas.compareAndSwap(expectedValue, (int)(Math.random()*101));

??????????????????? System.out.println(b);

??????????????? }

??????????? }).start();

??????? }

??? }

}

?

class CompareAndSwap{

??? private int value;//內(nèi)存值

?

??? //獲取內(nèi)存值

??? public synchronized int get(){

??????? return value;

??? }

?

??? //比較

??? public synchronized boolean compareAndSwap(int expectedValue,int newValue){

??????? int oldValue=value;//線程讀取內(nèi)存值,與預(yù)估值比較

?????? ?if(oldValue==expectedValue){

??????????? this.value=newValue;

??????????? return true;

??????? }

??????? return false;

??? }

}

三、ConcurrentHashMap、鎖分段

HashMap 線程不安全?
Hashtable 內(nèi)部采用獨(dú)占鎖,線程安全,但效率低?
ConcurrentHashMap同步容器類是java5 新增的一個(gè)線程安全的哈希表,效率介于HashMap和Hashtable之間。內(nèi)部采用“鎖分段”機(jī)制。


java.util.concurrent 包還提供了設(shè)計(jì)用于多線程上下文中的Collection實(shí)現(xiàn):

當(dāng)期望許多線程訪問一個(gè)給定 collection 時(shí),?
ConcurrentHashMap 通常優(yōu)于同步的 HashMap,?
ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap?
ConcurrentSkipListSet通常優(yōu)于同步的 TreeSet.

當(dāng)期望的讀數(shù)和遍歷遠(yuǎn)遠(yuǎn)大于列表的更新數(shù)時(shí),?
CopyOnWriteArrayList 優(yōu)于同步的 ArrayList。因?yàn)槊看翁砑訒r(shí)都會進(jìn)行復(fù)制,開銷非常的大,并發(fā)迭代操作多時(shí) ,選擇。

四、CountDownLatch 閉鎖

CountDownLatch 一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或多個(gè)線程一直等待。

閉鎖可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài),閉鎖可以用來確保某些活動直到其他活動都完成才繼續(xù)執(zhí)行:?
?確保某個(gè)計(jì)算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;
?確保某個(gè)服務(wù)在其依賴的所有其他服務(wù)都已經(jīng)啟動之后才啟動;?
?等待直到某個(gè)操作所有參與者都準(zhǔn)備就緒再繼續(xù)執(zhí)行。

/*

?* CountDownLatch:閉鎖,在完成某些運(yùn)算時(shí),只有其他所有線程的運(yùn)算全部完成,當(dāng)前運(yùn)算才繼續(xù)執(zhí)行

?*/

public class TestCountDownLatch {

??? public static void main(String[] args) {

??????? final CountDownLatch latch=new CountDownLatch(50);

??????? LatchDemo ld=new LatchDemo(latch);

?

??????? long start=System.currentTimeMillis();

?

??????? for(int i=0;i<50;i++){

??????????? new Thread(ld).start();

??????? }

?

??????? try {

??????????? latch.await(); //直到50個(gè)人子線程都執(zhí)行完,latch的值減到0時(shí),才往下執(zhí)行

??????? } catch (InterruptedException e) {

???????? ???e.printStackTrace();

??????? }

?

??????? long end=System.currentTimeMillis();

??????? System.out.println("耗費(fèi)時(shí)間為:"+(end-start));

??? }

}

?

class LatchDemo implements Runnable{

??? private CountDownLatch latch;

?

??? public LatchDemo(CountDownLatch latch){

??????? this.latch=latch;

??? }

??? @Override

??? public void run() {

??????? try{

??????????? for(int i=0;i<50000;i++){

??????????????? if(i%2==0){

??????????????????? System.out.println(i);

??????????????? }

??????????? }

??????? }finally{

??????????? latch.countDown();//latch的值減一

??????? }

??? }

}

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

java?5.0 在 java.util.concurrent 提供了一個(gè)新的創(chuàng)建執(zhí)行線程的方式:Callable 接口

實(shí)現(xiàn)Callable 接口,相較于實(shí)現(xiàn) Runnable接口的方式,方法可以有返回值,并且可以拋出異常。

Callable 需要依賴FutureTask ,用于接收返回值,FutureTask 也可以用作閉鎖。

/*

?* 一、創(chuàng)建執(zhí)行線程的方式三:實(shí)現(xiàn)Callable接口。相較于實(shí)現(xiàn)Runnable接口的方式,方法可以有返回值,并且可以拋出異常。

?* 二、執(zhí)行Callable方式,需要FutureTask實(shí)現(xiàn)類的支持,用于接收運(yùn)算結(jié)果。FutureTask是Future接口的實(shí)現(xiàn)類

?*/

public class TestCallable {

??? public static void main(String[] args) {

??????? ThreadDemo2 td=new ThreadDemo2();

?

??????? //1.執(zhí)行Callable方式,需要FutureTask實(shí)現(xiàn)類的支持,用于接收運(yùn)行結(jié)果。

??????? FutureTask<Integer> result=new FutureTask<>(td);

??????? new Thread(result).start();

?

??????? //2.接收線程運(yùn)算后的結(jié)果

??????? try {

??????????? Integer sum = result.get();//FutureTask 可用于 閉鎖? 當(dāng)子線程執(zhí)行完畢,才會執(zhí)行此后語句

??????????? System.out.println(sum);

??????????? System.out.println("----------------------");

??????? } catch (InterruptedException | ExecutionException e) {

??????????? e.printStackTrace();

??????? }

??? }

}

?

class ThreadDemo2 implements Callable<Integer>{

?

??? @Override

??? public Integer call() throws Exception {

??????? int sum=0;

??????? for(int i=0;i<=100000;i++){

??????????? sum+=i;

??????? }

??????? return sum;

??? }

?

}

六、Lock 同步鎖

在 Java 5.0 之前,協(xié)調(diào)共享對象的訪問時(shí)可以使用的機(jī)制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的機(jī)制,但并不是一種替代內(nèi)置鎖的方法,而是當(dāng)內(nèi)置鎖不適用時(shí),作為一種可選擇的高級功能。

ReentrantLock 實(shí)現(xiàn)了 Lock 接口,并提供了與synchronized 相同的互斥性和內(nèi)存可見性。但相較于synchronized 提供了更高的處理鎖的靈活性。

/*

?* 一、用于解決多線程安全問題的方式:

?* synchronized:隱式鎖

?* 1、同步代碼塊

?* 2、同步方法

?* jdk 1.5后

?* 3、同步鎖 Lock?

?* 注意:是一個(gè)顯式鎖,通過lock()方式上鎖,必須通過unlock()方法釋放鎖

?*/

?

/*

?* 賣票

?*/

public class TestLock {

??? public static void main(String[] args) {

??????? Ticket ticket=new Ticket();

??????? new Thread(ticket,"1號窗口").start();

??????? new Thread(ticket,"2號窗口").start();

??????? new Thread(ticket,"3號窗口").start();

??? }

}

?

class Ticket implements Runnable{

??? private int tick=100;

??? private Lock lock=new ReentrantLock();

??? @Override

??? public void run() {

??????? while(true){

??????????? lock.lock();

?? ?????????try{

??????????????? if(tick>0){

??????????????????? try {

??????????????????????? Thread.sleep(200);

??????????????????? } catch (InterruptedException e) {

??????????????????????? e.printStackTrace();

??????????????????? }

??????????????????? System.out.println(Thread.currentThread().getName()+"完成售票為:"+--tick);

??????????????? }

??????????????? else{

??????????????????? break;

??????????????? }

??????????? }finally{

??????????????? lock.unlock();//釋放鎖一定要放在finally里,保證一定執(zhí)行

??????????? }

??????? }

??? }

?

}

?

?

?

/*

?* 生產(chǎn)者和消費(fèi)者案例,優(yōu)化,防止出現(xiàn)虛假喚醒,線程無法停止

?*/

public class TestProductorAndConsumer {

??? public static void main(String[] args) {

??????? Clerk clerk=new Clerk();

?

??????? Productor pro=new Productor(clerk);

??????? Consumer cus=new Consumer(clerk);

?

??????? new Thread(pro,"生產(chǎn)者 A").start();

??????? new Thread(cus,"消費(fèi)者 B").start();

??????? new Thread(pro,"生產(chǎn)者 C").start();

??????? new Thread(cus,"消費(fèi)者 D").start();

??? }

}

?

//店員 假如只有一個(gè)商品位置

class Clerk{

??? private int product=0;

?

??? //進(jìn)貨

??? public synchronized void get(){

??????? while(product>=1){//為了避免虛假喚醒問題,應(yīng)該總是使用在循環(huán)中

??????????? System.out.println("產(chǎn)品已滿!");

??????????? try{

??????????????? this.wait();

??????????? }catch(InterruptedException e){

??????????? }

??????? }

??????? System.out.println(Thread.currentThread().getName()+" : "+ ++product);

?? ?????this.notifyAll();

??? }

?

??? //賣貨

??? public synchronized void sale(){

??????? while(product<=0){ //為了避免虛假喚醒問題,應(yīng)該總是使用在循環(huán)中

??????????? System.out.println("缺貨!");

??????????? try {

??????????????? this.wait();

??????????? } catch (InterruptedException e) {

??????????????? e.printStackTrace();

??????????? }

??????? }

??????? System.out.println(Thread.currentThread().getName()+" : "+ --product);

??????? this.notifyAll();

??? }

}

?

//生產(chǎn)者

class Productor implements Runnable{

??? private Clerk clerk;

??? public Productor(Clerk clerk){

??????? this.clerk=clerk;

??? }

??? @Override

??? public void run() {

??????? for(int i=0;i<20;i++){

??????????? try{

??????????????? Thread.sleep(100);

??????????? }catch(InterruptedException e){

??????????? }

??????????? clerk.get();

??????? }

??? }

}

?

//消費(fèi)者

class Consumer implements Runnable{

??? private Clerk clerk;

??? public Consumer(Clerk clerk){

??????? this.clerk=clerk;

??? }

??? @Override

??? public void run() {

??????? for(int i=0;i<20;i++){

??????????? clerk.sale();

??????? }

??? }

}

?

七、Condition 控制線程通信

Condition 接口描述了可能會與鎖有關(guān)聯(lián)的條件變量。這些變量在用法上與使用 Object.wait 訪問的隱式監(jiān)視器類似,但提供了更強(qiáng)大的功能。需要特別指出的是,單個(gè) Lock 可能與多個(gè) Condition 對象關(guān)聯(lián)。為了避免兼容性問題,Condition 方法的名稱與對應(yīng)的 Object 版本中的不同。

在 Condition 對象中,與 wait、notify 和 notifyAll 方法對應(yīng)的分別是await、signal 和 signalAll。

Condition 實(shí)例實(shí)質(zhì)上被綁定到一個(gè)鎖上。要為特定 Lock 實(shí)例獲得Condition 實(shí)例,請使用其 newCondition() 方法。

public class TestProductorAndConsumerForLock {

??? public static void main(String[] args) {

??????? Clerk clerk=new Clerk();

?

??????? Productor pro=new Productor(clerk);

??????? Consumer cus=new Consumer(clerk);

?

??????? new Thread(pro,"生產(chǎn)者 A").start();

??????? new Thread(cus,"消費(fèi)者 B").start();

??????? new Thread(pro,"生產(chǎn)者 C").start();

??????? new Thread(cus,"消費(fèi)者 D").start();

??? }

}

?

//店員 假如只有一個(gè)商品位置

class Clerk{

??? private int product=0;

??? private Lock lock=new ReentrantLock();

??? private Condition condition=lock.newCondition();

?

??? //進(jìn)貨

??? public void get(){

??????? lock.lock();

?

??????? try{

??????????? while(product>=1){//為了避免虛假喚醒問題,應(yīng)該總是使用在循環(huán)中

??????????????? System.out.println("產(chǎn)品已滿!");

? ??????????????try{

??????????????????? condition.await();//this.wait();

??????????????? }catch(InterruptedException e){

??????????????? }

??????????? }

??????????? System.out.println(Thread.currentThread().getName()+" : "+ ++product);

??????????? condition.signalAll();//this.notifyAll();

??????? }finally{

??????????? lock.unlock();

??????? }

??? }

?

??? //賣貨

??? public void sale(){

??????? lock.lock();

?

??????? try{

??????????? while(product<=0){

??????????????? System.out.println("缺貨!");

??????????????? try {

??????????????????? condition.await();//this.wait();

??????????????? } catch (InterruptedException e) {

??????????????????? e.printStackTrace();

??????????????? }

??????????? }

??????????? System.out.println(Thread.currentThread().getName()+" : "+ --product);

??????????? condition.signalAll();//this.notifyAll();

??????? }finally{

??????????? lock.unlock();

??????? }

??? }

}

?

//生產(chǎn)者

class Productor implements Runnable{

??? private Clerk clerk;

??? public Productor(Clerk clerk){

??????? this.clerk=clerk;

??? }

??? @Override

??? public void run() {

??????? for(int i=0;i<20;i++){

??????????? try{

???????? ???????Thread.sleep(100);

??????????? }catch(InterruptedException e){

??????????? }

??????????? clerk.get();

??????? }

??? }

}

?

//消費(fèi)者

class Consumer implements Runnable{

??? private Clerk clerk;

??? public Consumer(Clerk clerk){

??????? this.clerk=clerk;

??? }

??? @Override

??? public void run() {

??????? for(int i=0;i<20;i++){

??????????? clerk.sale();

??????? }

??? }

}

八、線程按序交替

編寫一個(gè)程序,開啟 3 個(gè)線程,這三個(gè)線程的 ID 分別為A、B、C,每個(gè)線程將自己的 ID 在屏幕上打印 10 遍,要求輸出的結(jié)果必須按順序顯示。如:ABCABCABC…… 依次遞歸

public class TestABCAlternate {

??? public static void main(String[] args) {

??????? AlternateDemo ad=new AlternateDemo();

??????? new Thread(new Runnable(){

??????????? @Override

??????????? public void run() {

??????????????? for(int i=1;i<=20;i++){

??????????????????? ad.loopA(i);

?????? ?????????}

??????????? }

?

??????? },"A").start();

?

??????? new Thread(new Runnable(){

??????????? @Override

??????????? public void run() {

??????????????? for(int i=1;i<=20;i++){

??????????????????? ad.loopB(i);

??????????????? }

??????????? }

?

??????? },"B").start();

?

??????? new Thread(new Runnable(){

??????????? @Override

??????????? public void run() {

??????????????? for(int i=1;i<=20;i++){

??????????????????? ad.loopC(i);

??????????????????? System.out.println("-----------------------------------");

??????????????? }

??????????? }

?

??????? },"C").start();

??? }

}

?

class AlternateDemo{

??? private int number=1;//當(dāng)前正在執(zhí)行線程的標(biāo)記

??? private Lock lock=new ReentrantLock();

??? private Condition condition1=lock.newCondition();

??? private Condition condition2=lock.newCondition();

??? private Condition condition3=lock.newCondition();

?

??? /*

???? * @param totalLoop:循環(huán)第幾輪

???? */

??? public void loopA(int totalLoop){

??????? lock.lock();

??????? try{

??????????? //1.判斷

??????????? if(number!=1){

??????????????? condition1.await();

??????????? }

?

??????????? //2.打印

??????????? System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);

?

??????????? //3.喚醒

??????????? number=2;

??????????? condition2.signal();

??????? } catch (InterruptedException e) {

??? ????????e.printStackTrace();

??????? } finally{

??????????? lock.unlock();

??????? }

??? }

?

??? public void loopB(int totalLoop){

??????? lock.lock();

??????? try{

??????????? //1.判斷

??????????? if(number!=2){

??????????????? condition2.await();

????????? ??}

?

??????????? //2.打印

??????????? System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);

?

??????????? //3.喚醒

??????????? number=3;

??????????? condition3.signal();

??????? } catch (InterruptedException e) {

??????????? e.printStackTrace();

??????? } finally{

??????????? lock.unlock();

??????? }

??? }

?

??? public void loopC(int totalLoop){

??????? lock.lock();

??????? try{

??????????? //1.判斷

??????????? if(number!=3){

??????????????? condition3.await();

??????????? }

?

??????????? //2.打印

???? ???????System.out.println(Thread.currentThread().getName()+"\t"+totalLoop);

?

??????????? //3.喚醒

??????????? number=1;

??????????? condition1.signal();

??????? } catch (InterruptedException e) {

??????????? e.printStackTrace();

??????? } finally{

????????? ??lock.unlock();

??????? }

??? }

}

九、ReadWriteLock 讀寫鎖

ReadWriteLock 維護(hù)了一對相關(guān)的鎖,一個(gè)用于只讀操作,另一個(gè)用于寫入操作。只要沒有 writer,讀取鎖可以由多個(gè) reader 線程同時(shí)保持。寫入鎖是獨(dú)占的。

ReadWriteLock 讀取操作通常不會改變共享資源,但執(zhí)行寫入操作時(shí),必須獨(dú)占方式來獲取鎖。對于讀取操作占多數(shù)的數(shù)據(jù)結(jié)構(gòu)。 ReadWriteLock 能提供比獨(dú)占鎖更高的并發(fā)性。而對于只讀的數(shù)據(jù)結(jié)構(gòu),其中包含的不變性可以完全不需要考慮加鎖操作。

/*

?* 1.ReadWriteLock:讀寫鎖

?* 寫寫|讀寫?? 需要"互斥"

?* 讀讀?????? 不需要"互斥"

?*/

public class TestReadWriteLock {

??? public static void main(String[] args) {

??????? ReadWriteLockDemo rw=new ReadWriteLockDemo();

??????? new Thread(new Runnable() {

??????????? @Override

??????????? public void run() {

??????????????? rw.set((int)(Math.random()*101));

??????????? }

??????? },"Write").start();

?

??????? for(int i=0;i<100;i++){

??????????? new Thread(new Runnable() {

??????????????? @Override

??????????????? public void run() {

??????????????????? rw.get();

??????????????? }

??????????? }).start();

??????? }

??? }

}

?

class ReadWriteLockDemo{

??? private int number=0;

private ReadWriteLock lock=new ReentrantReadWriteLock();

private readLock = lock. readLock();

private writeLock = lock. writeLock();

?

?

?

??? //讀

??? public void get(){

??????? readLock.lock();//上鎖

??????? try{

??????????? System.out.println(Thread.currentThread().getName()+" : "+number);

??????? }finally{

??????????? readLock.unlock();//釋放鎖

??????? }

??? }

?

??? //寫

??? public void set(int number){

??????? writeLock.lock();

??????? try{

??????????? System.out.println(Thread.currentThread().getName());

??????????? this.number=number;

??????? }finally{

??????????? writeLock.unlock();

??????? }

??? }

}

十、線程八鎖

  • 一個(gè)對象里面如果有多個(gè)synchronized實(shí)例方法,某一個(gè)時(shí)刻內(nèi),只要一個(gè)線程去調(diào)用其中的一個(gè)synchronized實(shí)例方法了,其它的線程都只能等待,換句話說,某一個(gè)時(shí)刻內(nèi),只能有唯一一個(gè)線程去訪問這些synchronized實(shí)例方法。鎖的是當(dāng)前對象this,被鎖定后,其它的線程都不能進(jìn)入到當(dāng)前對象的其它的synchronized實(shí)例方法。
  • 加個(gè)普通方法后發(fā)現(xiàn)和同步鎖無關(guān)
  • 換成兩個(gè)對象后,不是同一把鎖了,情況立刻變化。
  • 都換成靜態(tài)同步方法后,情況又變化
  • 所有的非靜態(tài)同步方法用的都是同一把鎖——實(shí)例對象本身,也就是說如果一個(gè)實(shí)例對象的非靜態(tài)同步方法獲取鎖后,該實(shí)例對象的其他非靜態(tài)同步方法必須等待獲取鎖的方法釋放鎖后才能獲取鎖,可是別的實(shí)例對象的非靜態(tài)同步方法因?yàn)楦搶?shí)例對象的非靜態(tài)同步方法用的是不同的鎖,所以毋須等待該實(shí)例對象已獲取鎖的非靜態(tài)同步方法釋放鎖就可以獲取他們自己的鎖。
  • 所有的靜態(tài)同步方法用的也是同一把鎖——類對象本身,這兩把鎖是兩個(gè)不同的對象,所以靜態(tài)同步方法與非靜態(tài)同步方法之間是不會有競態(tài)條件的。但是一旦一個(gè)靜態(tài)同步方法獲取鎖后,其他的靜態(tài)同步方法都必須等待該方法釋放鎖后才能獲取鎖,而不管是同一個(gè)實(shí)例對象的靜態(tài)同步方法之間,還是不同的實(shí)例對象的靜態(tài)同步方法之間,只是它們同一個(gè)類的實(shí)例對象!

/*

?* 實(shí)驗(yàn):觀察打印的"one" or "two" ?

?*

?* 1.兩個(gè)普通同步方法,兩個(gè)線程,標(biāo)準(zhǔn)打印,打印?? //one two? 因?yàn)橥芥i是this(調(diào)用對象本身),被鎖定后,其它的線程都不能進(jìn)入到當(dāng)前對象的其它的synchronized方法

?* 2.新增Thread.sleep()給 getOne(),打印? //等3秒后? one two

?* 3.新增普通方法(非同步) getThree(),打印?//three 等3秒 one two 因?yàn)橥芥i不影響普通方法的執(zhí)行

?* 4.兩個(gè)普通同步方法,兩個(gè)Number對象,打印?//two 等3秒 one? 因?yàn)橛玫牟皇峭话焰i,互不影響

?* 5.修改 getOne() 為靜態(tài)同步方法,使用一個(gè)Number對象打印?? //two 等3秒 one? 因?yàn)殪o態(tài)同步方法用的鎖是類對象本身,Number.class; 和對象用的是不同的鎖

?* 6.修改兩個(gè)方法均為靜態(tài)同步方法,一個(gè)Number對象?//等3秒 one two 用的鎖都是Number類對象本身

?* 7.一個(gè)靜態(tài)同步方法,一個(gè)非靜態(tài)同步方法,兩個(gè)Number對象?//two 等3秒one

?* 8.兩個(gè)靜態(tài)同步方法,兩個(gè)Number對象?//等3秒后? one two 用的鎖都是Number類對象本身

?*

?* 線程八鎖的關(guān)鍵:

?* ①非靜態(tài)方法的鎖默認(rèn)為? this,? 靜態(tài)方法的鎖為 對應(yīng)的 Class 實(shí)例

?* ②某一個(gè)時(shí)刻內(nèi),只能有一個(gè)線程持有同一把鎖,無論幾個(gè)方法。

?*/

?

public class TestThread8Monitor {

??? public static void main(String[] args) {

??????? Number number = new Number();

??????? Number number2 = new Number();

??????? new Thread(new Runnable() {

??????????? public void run() {

??????????????? number.getOne();

??????????? }

??????? }).start();

??????? new Thread(new Runnable() {

??????????? public void run() {

//????????????? number.getTwo();

??????????????? number2.getTwo();

??????????? }

??????? }).start();

//? ????new Thread(new Runnable() {

//????????? public void run() {

//????????????? number.getThree();

//????????? }

//????? }).start();

?

??? }

?

}

?

class Number {

??? public static synchronized void getOne() {

??????? try {

??????????? Thread.sleep(3000);

??? ????} catch (InterruptedException e) {

??????? }

??????? System.out.println("one");

??? }

?

??? public static synchronized void getTwo() {

??????? System.out.println("two");

??? }

?

//? public void getThree(){

//????? System.out.println("three");

//? }

}

?

?

十一、線程池

獲取線程第四種方法。?
線程池可以解決兩個(gè)不同問題:由于減少了每個(gè)任務(wù)調(diào)用的開銷,它們通常可以在執(zhí)行大量異步任務(wù)時(shí)提供增強(qiáng)的性能,并且還可以提供綁定和管理資源(包括執(zhí)行任務(wù)集時(shí)使用的線程)的方法。每個(gè) ThreadPoolExecutor 還維護(hù)著一些基本的統(tǒng)計(jì)數(shù)據(jù),如完成的任務(wù)數(shù)。

為了便于跨大量上下文使用,此類提供了很多可調(diào)整的參數(shù)和擴(kuò)展鉤子 (hook)。但是,強(qiáng)烈建議程序員使用較為方便的 Executors 工廠方法 :

Executors.newCachedThreadPool()(無界線程池,可以進(jìn)行自動線程回收)

Executors.newFixedThreadPool(int)(固定大小線程池)

Executors.newSingleThreadExecutor()(單個(gè)后臺線程)

它們均為大多數(shù)使用場景預(yù)定義了設(shè)置。

/*

?* 一、線程池:提供了一個(gè)線程隊(duì)列,隊(duì)列中保存著所有等待狀態(tài)的線程。避免了創(chuàng)建與銷毀額外開銷,提高了響應(yīng)的速度。

?* 二、線程池的體系結(jié)構(gòu):

?*??? java.util.concurrent.Executor:負(fù)責(zé)線程的使用與調(diào)度的根接口

?*??? ????|--**ExecutorService 子接口:線程池的主要接口

?*???????????? |--ThreadPoolExecutor 線程池的實(shí)現(xiàn)類

?*???????????? |--ScheduledExecutorService 子接口:負(fù)責(zé)線程的調(diào)度

?*????????????????? |--ScheduledThreadPoolExecutor:繼承ThreadPoolExecutor,實(shí)現(xiàn)ScheduledExecutorService接口

?* 三、工具類:Executors

?* 方法有:

?* ExecutorService newFixedThreadPool(): 創(chuàng)建固定大小的線程池

?* ExecutorService newCachedThreadPool():緩存線程池,線程池的數(shù)量不固定,可以根據(jù)需要自動的更改數(shù)量。

?* ExecutorService newSingleThreadExecutor():創(chuàng)建單個(gè)線程池。線程池中只有一個(gè)線程

?*

?* ScheduledExecutorService newScheduledThreadPool():創(chuàng)建固定大小的線程,可以延遲或定時(shí)的執(zhí)行任務(wù)。

?*

?*/

public class TestThreadPool {

??? public static void main(String[] args) {

??????? //1.創(chuàng)建線程池

??????? ExecutorService pool=Executors.newFixedThreadPool(5);

?

??????? List<Future<Integer>> list=new ArrayList<>();

??????? for (int i = 0; i < 10; i++) {

??????????? Future<Integer> future=pool.submit(new Callable<Integer>(){

??????????????? @Override

??????????????? public Integer call() throws Exception {

??????????????????? int sum=0;

??????????????????? for(int i=0;i<=100;i++){

??????????????????????? sum+=i;

??????????????????? }

??????????????????? return sum;

??????????????? }

?

??????????? });

??????????? list.add(future);

??????? }

??????? pool.shutdown(); // 任務(wù)執(zhí)行完畢關(guān)閉

??????? for(Future<Integer> future:list){

??????????? System.out.println(future.get());

??????? }

??? }

}

十二、線程調(diào)度

public class TestScheduledThreadPool {

??? public static void main(String[] args) throws Exception {

??????? ScheduledExecutorService pool=Executors.newScheduledThreadPool(5);

??????? for (int i = 0; i < 5; i++) {

??????????? Future<Integer> result=pool.schedule(new Callable<Integer>() {

??????????????? @Override

??????????????? public Integer call() throws Exception {

??????????????????? int num=new Random().nextInt(100);//生成隨機(jī)數(shù)

??????????????????? System.out.println(Thread.currentThread().getName()+" : "+num);

??????????????????? return num;

??????????????? }

?

??????????? }, 2, TimeUnit.SECONDS);//每次延遲兩秒后運(yùn)行

??????????? System.out.println(result.get());

??????? }

??? }

}

?

轉(zhuǎn)載于:https://my.oschina.net/dslcode/blog/1591214

總結(jié)

以上是生活随笔為你收集整理的多线程 JUC的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。