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("全部線程結束");
}
}
符合實際生活中軟件的使用場景,返回的是一個都完成的完整數據不是零散。