多線程面試題總結+個人解答

1.進程和線程的關係和區別?

進程是表示資源分配的最小單位,但是因爲在創建,撤銷過程中資源開銷比較大。進程不宜頻繁切換。
爲了可以進行多個程序併發執行,又可以減少系統的資源開銷,所以產生了線程概念。
線程是進程的一個實體,是系統調度的基本單位,線程基本上不擁有系統資源,只有一些運行中必不可少的資源(程序計數器,棧,寄存器),但是線程可以與同處於一個進程下的其他線程共享擁有的數據。
引入線程,更好的提高了併發性,不僅進程之間併發執行,而且進程中的線程也可以同時執行。

2.進程線程的狀態:

進程的狀態:
在這裏插入圖片描述
線程的狀態:
在這裏插入圖片描述

線程之間的通訊方式:

synchronized,wait/notify,管道通信:java.io.PipedInputStream 和 java.io.PipedOutputStream進行通信,while輪詢的操作。
進程之間的通訊方式:
管道:雙半工:具有固定的讀端和寫端
信號量:信號量是一個計數器,用於多線程對共享數據的訪問,信號量的意圖在於進程間的同步,爲了獲取共享資源,進程需要執行:
1.創建一個信號量
2.等待一個信號量
3.掛出一個信號量
消息隊列:存放在內核中的消息鏈表,每個消息隊列由消息隊列表識符表示
共享內存:是多個進程同時讀寫同一塊內存,是最快的ipc形式針對其他通信機制運行效率低而設計的
套接字:域,端口,協議類型(socket客戶端和服務器之間的通信)

sleep爲什麼是靜態的?

1.sleep是靜態方法,呢麼實現Runnable的線程類也能調用
2.sleep是靜態方法,所以sleep的時候只是讓出來cpu但是並沒有釋放對象鎖,因爲獲取不到對象???
3.線程和實例並不是對等的,不是一個線程是一個實例,是你創建的實例繼承了Thread或者Runnable 實現了run(),並調用start()的時候能夠執行多個線程,實例還是一個,線程卻是多個,所以實例休眠了 則線程就休眠了 這個假設不成立。

public class Thread_test extends Thread {
    @Override
    public void run() {
        while (true){
            System.out.println("執行中");
        }
        }

    public static void main(String[] args) {
        Thread_test test = new Thread_test();
        test.start();
        while (true){
            System.out.println("main線程");
            try {
                test.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我們使用實例來.sleep並沒有使子線程休眠,休眠的是我們的main線程

在這裏插入圖片描述

什麼情況下會出現死鎖?

哲學家問題,我覺得當線程之間互相持有其他線程需要使用的資源,並且都不會釋放的時候都在互相等,就會出現死鎖。

在這裏插入圖片描述
死鎖是需要同時滿足4個條件:
1.互斥條件:就是資源只能由一個進程佔有,不能被2個或多個進程佔有
2.不可搶佔條件:進程已經獲得的資源在未使用完之前,不可被搶佔,只能在使用完後自己釋放。
3.佔有申請條件:進程自己已經保持一個資源了,此時又在請求其他資源,但申請的資源被其他進程佔有,而且也不釋放自己現在所佔有的資源
4.循環等待條件:發生死鎖時,必定會形成一個進程一一資源的環路。如上圖。

如何解決死鎖?

解決死鎖只需要破壞掉滿足死鎖的4個必要條件任何一個即可。
資源一次性分配:破壞請求和保持條件
可剝奪資源:即當某進程新的資源未滿足時,釋放已佔有的資源。
資源分配有序法:系統給每類資源賦予一個編號,每一個進程按照順序編號請求資源

怎麼分析自己的程序死鎖了?

測試死鎖代碼:

public class Blocked {
    public static void main(String[] args) {
        Object a1 = new Object();
        Object a2 = new Object();
        new Thread() {
            @Override
            public void run() {
                synchronized (a1) {
                    try {
                        System.out.println("我現在持有a1");
                        Thread.sleep(1000);
                        System.out.println("我現在準備持有a2");
                        synchronized (a2) {
                            System.out.println("我現在持有a2了");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                synchronized (a2) {
                    try {
                        System.out.println("我現在持有a2");
                        Thread.sleep(1000);
                        System.out.println("我現在準備持有a1");
                        synchronized (a1) {
                            System.out.println("我現在持有a1了");
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}

使用jconsole查看線程狀態:
在這裏插入圖片描述
在這裏插入圖片描述
相互持有。

怎麼確定是什麼原因導致的死鎖?

會根據死鎖的必要條件來分析。

現在已經確定程序產生了死鎖,有什麼工具可以直接分析?

jconsole

Java中的Timer類是用來幹嘛的,是如何使用的?

Timer類是一個定時器類,這個類安排調試一個線程,使線程可以在將來時刻執行,Java的Timer類可以調度線程運行一次,或者定期運行。
Timer對象可以作爲守護線程的運行相關的任務,Timer的cancel()方法用於終止定時器,此時也會摒棄掉待運行的線程,但是不會干擾到現在正在執行的線程,會等待用戶線程結束後再終止。
Timer的方法:sechedule()方法用於調度一個線程在指定時間運行一次或者延時之後運行
還有scheduleAtFixedRate()方法,用於一定間隔時間週期運行線程。
使用地方:也可以想到TCP的超時重傳

import java.util.*;

public class Timertest {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.scheduleAtFixedRate(new TimerTask(){
            @Override
            public void run() {
                System.out.println("Timer start at"+new Date());
                try {
                    Thread.sleep(20*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("Timer finished at"+new Date());
            }
        },0,10*1000);
        System.out.println("TimerTask started");
        try {
            Thread.sleep(120*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
        System.out.println("TimerTask cancelled");
        try {
            Thread.sleep(30*1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Java中如果創建一個線程池?

在這裏插入圖片描述
正規一點的還是下面。
轉載自https://www.cnblogs.com/dolphin0520/p/3932921.html

如何合理配置線程池的大小?
如果是cpu密集型任務,就需要壓榨cpu,參考值可以設置爲Ncpu+1
如果是IO密集型任務,可以設置爲2*Ncpu
僅供參考

java線程池的具體過程,及線程池參數 和線程池類型具體阻塞隊列?

線程池的具體過程?
個人理解,創建線程池有幾種方法,但是一般建議使用ThreadPoolExcutor來創建,線程池的工作流程,相比較是工廠,首先一般是不預先創建線程,設定一個corePoolSize來表示正式員工,當有工作來,我們根據任務來創建線程,如果任務量大於正式員工,我們就先將任務放置在阻塞隊列中,當BlockQueue放滿了,此時我們看一下設定的maximumPoolSize 最大線程個數,如果目前線程沒有到這麼多,我們就創建臨時線程,現在考慮兩個情況,
如果加上臨時線程也不夠用,我們會實現拒絕策略:1,拒絕任務,拋除異常2,拒絕任務,不拋異常3,讓最早開始工作的老線程放棄手中工作來,接收新任務,4,當前調度指揮線程,來處理任務。
如果臨時線程完成工作後,沒有工作,我們會進入等待狀態,我們可以設置,臨時工等待時間上限,超出就會關閉掉。keepAliveTime
線程池的類型?
使用Executors可創建4種線程池:
1.FixedThreadPool是一個固定的線程池大小,阻塞隊列使用的是無界隊列,不會持續拒絕策略,keepAliveTime爲0;
2.CachedThreadPool 是一個無限擴大的線程池,它的maximumPoolSize爲無限大,keepAliveTime爲60s,使用的阻塞隊列是SynchronousQueue,來裝等待任務,這個隊列沒有存儲空間,如果有空閒線程,就讓空閒線程來操作,如果沒有就新創建。
3,SingleThreadExecutor:單例模式的線程池,只會創建一個工作線程處理任務,使用LinkedBlockingQueue來做阻塞隊列。
4.ScheduledThreadPool 它是用來處理延時執行的任務或是定時任務。
可以接收ScheduledFutureTask類型的任務,
1.scheduledAtFixedRate
2.scheduledWithFixedDelay
ScheduledFutureTask接收的參數是:
1,time任務開始時間
2,sequenceNumber:任務的序號
3,period:任務執行間隔
採用了DelayQueue內部是封裝了一個priorityQueue,根據Time排序,如果time相同,根據任務序號排序。
DelayQueue是一個無界隊列
工作過程中,工作線程會從DelayQueue取到執行時間的任務,任務執行完後再次調整需要執行時間放入到等待隊列中。

volatile關鍵字?

volatile是java虛擬機提供的最輕量級的同步機制,如果出現變量運算的話,也不能保證線程安全,volatile是沒有辦法保證原子性的,原子性可以通過AtomicInteger這類API來保證,volatile可以保證可見性,即對變量的操作是共享的。cpu在讀取信息過程中,是cash的,java的內存模型,是有工作內存和主內存的,保證可見性,也就是我們不管是讀還是寫數據的時候,都是跳過工作內存,直接操作主內存。可保證每次數據是最新的準確數據。
voaltile可以禁止指令重排序。指令重排序用圖更好的說明。
在讀的效率上和普通的變量沒有什麼區別,在修改的過程中慢一點,因爲需要在本地代碼保證不亂序執行。
在這裏插入圖片描述

Java中多線程是如果實現數據共享的?

可以通過map方法來進行數據共享:

未使用map
public class ThreadScopeDataShare {
	private static int data = 0;
 
	public static void main(String[] args) {
		// 共啓動2個線程
		for (int i = 0; i < 2; i++) {
			// 啓動一個線程
			new Thread(new Runnable() {
				@Override
				public void run() {
					data = new Random().nextInt();
					System.out.println(Thread.currentThread().getName() + " has put data :" + data);
					
					new A().get();
					new B().get();
				}
			}).start();
		}
	}
 
	static class A {
		public void get() {
			System.out.println("A from " + Thread.currentThread().getName() + " get data :" + data);
		}
	}
 
	static class B {
		public void get() {
			System.out.println("B from " + Thread.currentThread().getName() + " get data :" + data);
		}
	}
}
使用map
import java.util.*;
public class ThreadScopeDataShare {
    private static Map<Thread,Integer> map=new HashMap<>();
    public static void main(String[] args) {
        for (int i = 0; i <2 ; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    int data=new Random().nextInt();
                    map.put(Thread.currentThread(),data);
                    System.out.println(Thread.currentThread().getName() +"has put data"+data);
                    new A().get();
                    new B().get();
                }
            }).start();
        }
    }
    static class A{
        private void get(){
            int data=map.get(Thread.currentThread());
            System.out.println("A from"+Thread.currentThread().getName() +"get data "+data);
        }
    }
    static class B{
        private void get(){
            int data=map.get(Thread.currentThread());
            System.out.println("B from"+Thread.currentThread().getName() +"get data "+data);
        }
    }
}

可以使用ThreadLocal
//在內部,幫助做了隔離,每個線程都有自己的獨有值

Java創建線程池的接口是什麼?參數LinkedBlockingQueue的作用是什麼?

補充一個問題,爲什創建線程池,不推薦是用Executors.new XXX ThreadPool();?
因爲這種方式會使用無界的任務隊列爲了避免OOM,我們手動給阻塞隊列一個長度。
接口是Executor,BlockingQueue是阻塞隊列,來存放待處理任務,使用LinkedBlockingQueue,鏈表的隊列一般比基於數組的隊列有更高的吞吐率但是可預測性差。

synchronized和lock獲取不到鎖分別進入什麼狀態?

當一個線程使用synchronize獲取鎖時,若鎖被其他線程佔用着,那麼當前只能被阻塞,直到成功獲取鎖。在阻塞隊列是沒有搶鎖資格的。而Lock則提供超時鎖和可中斷等更加靈活的方式,在未能獲取鎖的 條件下提供一種退出的機制。如果搶鎖失敗的話,我們會進入等待隊列,循環等待,一直到搶到鎖爲止。

在多線程下,如果對一個數進行疊加,該怎麼做?

我會使用門閂來做:代碼如下

import	java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Num{
    static final AtomicInteger COUNT=new AtomicInteger(0);
    static CountDownLatch countDownLatch=new CountDownLatch(1000);
     static ThreadPoolExecutor pool = new ThreadPoolExecutor(100, 1000, 100, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>(10) {
    });

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            pool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread()+","+COUNT.incrementAndGet());
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        System.out.println(COUNT);
        pool.shutdown();
    }
}
Servlet是否是線程安全的?

servlet不是線程安全的,當Client發送一個HTTP請求,Tomcat從線程池中取出一個線程,找到對應的servlet進行初始化,然後調用service()方法,需要注意每一個Servlet對象在Tomcat中只有一個實例化對象。如果多個HTTP請求,請求的是同一個servlet的話會併發調用service()方法,此時如果servlet中定義了實例變量或者靜態變量,呢麼就會發生線程不安全的問題。

Thread和Runnable的區別和聯繫?

兩者的聯繫:Thread實現了Runnable接口,都需要重寫裏面的Run();
兩個的區別很簡單:就是Thread是繼承Thread類,Runnable是實現接口
無論是繼承還是實現接口,都會有新的線程,執行run(),使用上如果有複雜的線程操作需求,就繼承Thread 如果只是簡單的執行一個任務,就實現Runnable 的接口

多次start一個線程會怎麼樣?

多次調用start()會出現:
在這裏插入圖片描述
一個線程對象只能調用一次start(),從new到等待運行 是單行道,所以如果對一個已經啓動的線程再次調用start(),就會出現非法線程狀態異常。
可以被重複調用的是run()
Thread類中run()和start()方法區別是:
run( )方法:在本線程內調用該Runnable對象的run()方法可重複調用。
start()是啓動一個線程,不能多次啓動一個線程。

有synchronized兩個方法,兩個線程分別同時用這個方法,請問會發生什麼?
public class SyncMethod {
    public synchronized void SyncMethod1(){
        try {
            System.out.println("SyncMethod1,以獲取內置鎖 SyncMethod.this");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("SyncMethod1,即將釋放 SyncMethod.this");
    }
    public synchronized void SyncMethod2(){
        System.out.println("SyncMethod2,以獲取內置鎖 SyncMethod.this,並退出");
    }
    static class MyThread1 extends Thread {
        SyncMethod syncMethod;
        MyThread1 (SyncMethod syncMethod){
            this.syncMethod=syncMethod;
        }
        @Override
        public void run() {
            syncMethod.SyncMethod1();
        }
    }
    static class MyThread2 extends Thread {
        SyncMethod syncMethod;
        MyThread2 (SyncMethod syncMethod){
            this.syncMethod=syncMethod;
        }
        @Override
        public void run() {
            System.out.println("Thread running ....");
            syncMethod.SyncMethod2();
        }
    }
    public static void main(String[] args) throws InterruptedException {
    SyncMethod syncMethod=new SyncMethod();
    MyThread1 myThread1=new MyThread1(syncMethod);
    MyThread2 myThread2=new MyThread2(syncMethod);
    myThread1.start();
    Thread.sleep(500);
    myThread2.start();
    }
}

在這裏插入圖片描述
通過以上的運行結果我們可以看出,首先我們在同一個對象中有兩個方法加有synchronized,synchronized是java類的內置鎖,首先讓Thread1啓動執行裏面的方法,一進去打印完後就會調用sleep的方法放棄cpu,此時Thread2佔有CPU啓動Thread2,我們會發現它立刻打印出Thread running這個語句,但是不會執行下面的,因爲此時的鎖還被Thread1所持有,只有當Thread1釋放鎖,我們Thread2纔可以獲取鎖繼續執行。
如果此時的Thread1長期維持競爭激烈的鎖,就會導致其他的線程因等待鎖的釋放而被掛起,我們CPU會被閒置,吞吐量會降低。

死鎖,如何避免,避免算法?實際解決過沒有?

死鎖的避免方法我們可以知道造成死鎖的4個必要條件,存在互斥,不可被搶佔,請求和保持(請求需要資源是對已持有的資源不會釋放),循環等待
只要破壞其一條件即可,解決死鎖。
銀行家算法是著名的避免死鎖算法。
銀行家算法的思想是:將操作系統看成是銀行家,操作系統管理的資源相當於銀行家管理的資金,進程向操作系統請求資源分配,相當與用戶向銀行家貸款。操作系統按照銀行家制定的規則爲進程分配資源。進程首次向操作操作系統申請資源,需要先測試進程對資源的最大需求量,如果操作系統現持有的資源可以滿足進程的最大需求量,則按照申請量分配,否則就推遲分配。當進程再次向操作系統申請資源是,需要先測試看現在進程所持有的資源+現在要申請的資源是否超出了進程的最大需求量,如果超過就拒絕分配,否則再測試操作系統現存的資源是否滿足進程的尚需的資源量,若能滿足,按需求分配,如果不滿足就推遲分配。

鎖策略

樂觀鎖 vs 悲觀鎖

悲觀鎖的設計者:認爲持有鎖的線程一旦釋放掉鎖,鎖就馬上被其他線程搶走了。
樂觀鎖的設計者:認爲就算是有100次釋放鎖的操作,99次還是會被自己搶到的甚至是100次。
釋放鎖一定意味着這個代碼在執行,一定是線程在佔用CPU,呢麼剛好這個線程被從CPU上調度走的概率比較小。
所以樂觀鎖的防範比較嚴密,一定可以保證正確性,但是效率會低一些。
樂觀鎖,雖然不能保證100%正確,效率比較高。

讀鎖 vs 寫鎖

一個變量如果大家都在讀,之間是不存在互斥的,但是如果有一個數據是在寫的,呢麼所有的讀操作都需要停下來等待寫操作完成。
正常情況下:讀的操作是遠遠大於寫的操作的,所以如果出現寫,就會很影響效率。

爲了可以提升速度,我們分離出專門的讀鎖和寫鎖。
讀鎖之間不存在互斥。
讀鎖和寫鎖,寫鎖和寫鎖之間都存在互斥。

自旋鎖(spin lock)

之前學過的synchronized 這種鎖,如果搶不到鎖,是必須放棄cpu的。
等別人釋放完鎖,你被選中重新爲就緒態(第一次中獎),被選中調度到cpu上(第二次中獎),然後才能繼續搶鎖。
從一開始的沒有搶到鎖,你的命運就不由自己掌握—效率比較低。
線程調度的耗時巨大。
自旋鎖的話:就是我命由我不由天,自己控制。
(一般不會放棄cpu,即使是放棄掉,自身的狀態也是Runnable狀態)
搶鎖失敗之後不放棄cpu。循環等待。
之所以自旋鎖的廣泛應用離不開CPU多核的出現。
自旋鎖是基於一個假設的:如果這個鎖會很快的被釋放掉,的話使用,否則會一直佔着寶貴的CPU不做事。
自旋鎖是不公平的:都去火車站買票,窗口沒有票了,老實人會重新排隊,而自旋鎖的話大概率情況下,會在一釋放的瞬間再次搶到鎖。相當於睡在窗口。所以也要考慮到如果票很快回來的情況下,自旋鎖可以,如果票需要等很久,這樣會很浪費資源 效率極低。

可重入鎖:

當A線程已經搶到了lock這把鎖的情況,A再次去搶鎖(鎖持有的線程再次搶同一把鎖)
1.六親不認:鎖已經鎖上了,A搶就進入Block狀態。–不可重入鎖
2.開後門:認識A了,知道現在這把鎖是A持有的,則允許A繼續搶到鎖(搶鎖成功)–可重入鎖
synchronized
ReentrantLock 都是可重入鎖
可重入鎖的作用:可避免死鎖
如果我們現在是A以及搶到鎖,但是是不可重入鎖,現在A再次搶鎖,失敗進入Block狀態,此時鎖一直就不會被釋放掉。

CAS:Compare and swap

硬件(CPU)上支持的一個指令(在cpu上一定是原子的,沒有重排序問題)
cas 是取一個值與某個內存地址上值做比較,如果相等,就把內存上的值做一次更新,否則失敗

僞代碼
cas(address,oldValue,newValue);
if(*address==oldValue){
	*address==newValue;
	return true;
}else{
	return false;
}
import sun.misc.Unsafe;
import java.lang.reflect.Field;
public class CAS_test {
    static class Person {
        int age;
    }
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
       //開啓權限
        Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
        theUnsafe.setAccessible(true);
        Unsafe unsafe = (Unsafe) theUnsafe.get(null);
        //通過反射獲取到offset
        Person person = new Person();
        person.age = 10;
        Field field = Person.class.getDeclaredField("age");
        long offset = unsafe.objectFieldOffset(field);
        System.out.println(person.age);
        //傳入對象 boolean compareAndSwapInt
        // (Object var1, long var2, int var4, int var5);
        unsafe.compareAndSwapInt(person,offset,10,90);
        //只有var4==我們的Object時我們進行交換
        System.out.println(person.age);
    }
}

在這裏插入圖片描述

僞代碼
int lock=-1;//代表沒有線程持有鎖
//id ==998 要請求鎖
if(!cas(*lock,-1,998)){
	代表請求鎖失敗
	線程狀態修改爲block
	將線程放入阻塞隊列
}
	程序繼續執行

//現在如果想要釋放鎖
我們只需要將lock=-1就好不需要考慮其他的。
//之所以可以釋放鎖,肯定現在是持有鎖的。
//這個是後lock一定是998

在這裏插入圖片描述

ABA
int a =10;
//甲線程將a=10 交換a=20
//甲線程一會又將a=20 交換爲10;
此時乙線程跑來了
//判斷如果a=10的話,將a=30;
但是此時乙根本不清楚a之前的操作。
爲了解決這個問題,我們只需要加入版本號即可。
int a =10.1;
//甲線程將a=10.1 交換a=20.2
//甲線程一會又將a=20.2 交換爲10.3;
現在乙線程要的是
cas(10.1,30.2)就會交換失敗,因爲現在是10.3

請問:CAS是硬件上的機制,還是軟件上的機制?
同時也滿足軟件上的機制
MySQL:先用CAS在做可重複讀的時候,一定程度上避免了幻讀,也有ABA的問題。

synchronized 升級策略

無鎖 》偏向鎖 》輕量級鎖 》重量級鎖
synchronized文章

Callable+Furture 可以使線程具有返回值。submit.get();
import java.util.concurrent.Future;
import java.util.concurrent.*;

public class Callable_test {
    private static class Add implements Callable<Integer> {
        int a;
        int b;

        Add(int a, int b) {
            this.a = a;
            this.b = b;
        }

        @Override
        public Integer call() throws Exception {
            Thread.sleep(3000);
            return a + b;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10, 
        200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
        Add add = new Add(10, 20);
        System.out.println("開始計算");
        Future<Integer> submit = pool.submit(add);
        //Future未來將執行???
        System.out.println(submit.get());
        pool.shutdown();
        
        System.out.println("開始計算");
        FutureTask<Integer> ft = new FutureTask<>(new Add(100,200));
        //調用run()是調用到了線程池
        ft.run();
        System.out.println(ft.get());
    }
}

JUC下的機制(java.util.concurrent包下的類)

1.各種鎖
代碼組織更容易 ,更加靈活
之前有過說法,這裏的lock比synchronized效率更高,但是現在不存在這個問題JVM已經優化的很好了。
2.原子類
3.上層的一些封裝
4.Thread.lock
5.幫助實現鎖的的工具:AQS

Lock_test代碼

import java.util.concurrent.locks.*;

public class Lock_test {
    private static Lock lock = new ReentrantLock();

    private static void lockMethod() {
        lock.lock();
    }
    private static void UnLockMethod(){
        lock.unlock();
    }

    public static void main(String[] args) {
        synchronized (Lock_test.class){//這裏爭搶鎖

        }
        //這裏釋放鎖
    }

}

ThreadLock_test代碼

import java.util.*;

public class Thread_Local {

    public static void main(String[] args) {
    //在內部,幫你做了隔離,每個線程都有自己的獨有值
    //所以不會共享,不需要考慮線程安全問題
    ThreadLocal<Integer>local=new ThreadLocal<>();
    local.set(10);
    ThreadLocal<Map<String ,Integer>>mapThreadLocal=new ThreadLocal<>();
    mapThreadLocal.set(new Map<String, Integer>() {
        @Override
        public int size() {
            return 0;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public boolean containsKey(Object key) {
            return false;
        }

        @Override
        public boolean containsValue(Object value) {
            return false;
        }

        @Override
        public Integer get(Object key) {
            return null;
        }

        @Override
        public Integer put(String key, Integer value) {
            return null;
        }

        @Override
        public Integer remove(Object key) {
            return null;
        }

        @Override
        public void putAll(Map<? extends String, ? extends Integer> m) {

        }

        @Override
        public void clear() {

        }

        @Override
        public Set<String> keySet() {
            return null;
        }

        @Override
        public Collection<Integer> values() {
            return null;
        }

        @Override
        public Set<Entry<String, Integer>> entrySet() {
            return null;
        }
    });
    }
}

semaphore 信號量
import java.util.concurrent.Semaphore;

public class 信號量test {
    private static Semaphore semaphore=new Semaphore(3);
    static  class MyThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            semaphore.release();
        }
    }
    public static void main(String[] args) throws InterruptedException {
    //啓動一個線程定時去釋放semaphore
        MyThread thread1=new MyThread();
        thread1.start();
        semaphore.acquire();
        System.out.println(1);
        semaphore.acquire();
        System.out.println(2);
        semaphore.acquire();
        System.out.println(3);
        semaphore.acquire();
        System.out.println(4);
    }
}

CountDownLatch 門閂計數
import java.util.concurrent.CountDownLatch;

public class CountDown_Latch {
    //門閂
    private static CountDownLatch countDownLatch=new CountDownLatch(3);
    static class MyThread extends Thread {
        @Override
        public void run() {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread1 =new MyThread();
        MyThread myThread2 =new MyThread();
        MyThread myThread3 =new MyThread();
        myThread1.start();
        myThread2.start();
        myThread3.start();
        System.out.println("開始等待");
        countDownLatch.await();
        System.out.println("全部線程結束");
    }
}

在這裏插入圖片描述
符合實際生活中軟件的使用場景,返回的是一個都完成的完整數據不是零散。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章