主頁 > 後端開發 > 300 行代碼帶你秒懂 Java 多執行緒!

300 行代碼帶你秒懂 Java 多執行緒!

2020-10-12 06:30:32 後端開發

  執行緒的概念,百度是這樣解說的:
  執行緒(英語:Thread)是操作體系可以進行運算調度的最小單位,它被包括在行程之中,是行程中的實際運作單位,一條執行緒指的是行程中一個單一次序的操控流,一個行程中可以并發多個執行緒,每條執行緒并行履行不同的使命,在UnixSystemV及SunOS中也被稱為輕量行程(LightweightProcesses),但輕量行程更多指內核執行緒(KernelThread),而把用戶執行緒(UserThread)稱為執行緒,
  1.1執行緒與行程的差異
  行程:指在體系中正在運轉的一個運用程式;程式一旦運轉便是行程;行程——資源分配的最小單位,
  執行緒:體系分配處理器時間資源的基本單元,或許說行程之內獨立履行的一個單元履行流,執行緒——程式履行的最小單位,
  也便是,行程可以包括多個執行緒,而執行緒是程式履行的最小單位,
  1.2執行緒的狀況
  NEW:執行緒剛創立
  RUNNABLE:在JVM中正在運轉的執行緒,其間運轉狀況可以有運轉中RUNNING和READY兩種狀況,由體系調度進行狀況改變,
  BLOCKED:執行緒處于堵塞狀況,等候監督鎖,可以重新進行同步代碼塊中履行
  WAITING:等候狀況
  TIMED_WAITING:呼叫sleepjoinwait辦法或許導致執行緒處于等候狀況
  TERMINATED:執行緒履行完畢,現已退出
  1.3Notify和Wait:
  Notify和Wait的效果
  首要看原始碼給出的解說,這里翻譯了一下:
  Notify:喚醒一個正在等候這個目標的執行緒監控,假如有任何執行緒正在等候這個目標,那么它們中的一個被選擇被喚醒,選擇是任意的,發生在履行的酌情權,一個執行緒等候一個目標經過呼叫一個{@codewait}辦法進行監督,
  Notify需求在同步辦法或同步塊中呼叫,即在呼叫前,執行緒也必須獲得該目標的目標等級鎖
  Wait:導致當時執行緒等候,直到另一個執行緒呼叫{@linkjava.lang.Object#notify}辦法或{@linkjava.lang.Object#notifyAll}辦法,
  換句話說,這個辦法的行為就像它簡略相同履行呼叫{@codewait(0)},當時執行緒必須擁有該目標的監督器,
  執行緒開釋此監督器的一切權,并等候另一個執行緒告訴等候該目標的監督器的執行緒,喚醒經過呼叫{@codenotify}辦法或{@codenotifyAll}辦法,然后執行緒等候,直到它可以重新獲得監督器的一切權,然后持續履行,
  Wait的效果是使當時履行代碼的執行緒進行等候,它是Object類的辦法,該辦法用來將當時執行緒置入預履行行列中,并且在Wait地點的代碼行處中止履行,直到接到告訴或被中止為止,
  在呼叫Wait辦法之前,執行緒必須獲得該目標的目標等級鎖,即只能在同步辦法或同步塊中呼叫Wait辦法,
  Wait和Sleep的差異:
  它們最大本質的差異是,Sleep不開釋同步鎖,Wait開釋同步鎖,
  還有用法的上的不同是:Sleep(milliseconds)可以用時間指定來使他主動醒過來,假如時間不到你只能呼叫Interreput來強行打斷;Wait可以用Notify直接喚起,
  這兩個辦法來自不同的類分別是Thread和Object
  最首要是Sleep辦法沒有開釋鎖,而Wait辦法開釋了鎖,使得其他執行緒可以運用同步操控塊或許辦法,
  1.4Thread.sleep和Thread.yield的異同
  相同:Sleep和yield都會開釋CPU,
  不同:Sleep使當時執行緒進入阻滯狀況,所以履行Sleep的執行緒在指定的時間內必定不會履行;yield僅僅使當時執行緒重新回到可履行狀況,所以履行yield的執行緒有或許在進入到可履行狀況后立刻又被履行,Sleep可使優先級低的執行緒得到履行的時機,當然也可以讓同優先級和高優先級的執行緒有履行的時機;yield只能使同優先級的執行緒有履行的時機,
  1.5彌補:死鎖的概念
  死鎖:指兩個或兩個以上的行程(或執行緒)在履行程序中,因搶奪資源而形成的一種互相等候的現象,若無外力效果,它們都將無法推動下去,此刻稱體系處于死鎖狀況或體系發生了死鎖,這些永遠在互相等候的行程稱為死鎖行程,
  死鎖發生的四個必要條件(缺一不可):
  互斥條件:望文生義,執行緒對資源的拜訪是排他性,當該執行緒開釋資源后下一執行緒才可進行占用,
  懇求和堅持:簡略來說便是自己拿的不放手又等候新的資源到手,執行緒T1至少現已堅持了一個資源R1占用,但又提出對另一個資源R2懇求,而此刻,資源R2被其他執行緒T2占用,于是該執行緒T1也必須等候,但又對自己堅持的資源R1不開釋,
  不可掠奪:在沒有運用完資源時,其他線性不能進行掠奪,
  回圈等候:一向等候對方執行緒開釋資源,
  咱們可以根據死鎖的四個必要條件損壞死鎖的形成,
  1.6彌補:并發和并行的差異
  并發:是指在某個時間段內,多使命替換的履行使命,當有多個執行緒在操作時,把CPU運轉時間劃分成若干個時間段,再將時間段分配給各個執行緒履行,在一個時間段的執行緒代碼運轉時,其它執行緒處于掛起狀,
  并行:是指同一時間一起處理多使命的才能,當有多個執行緒在操作時,CPU一起處理這些執行緒懇求的才能,
  差異就在于CPU是否能一起處理一切使命,并發不能,并行能,
  1.7彌補:執行緒安全三要素
  原子性:Atomic包、CAS演算法、Synchronized、Lock,
  可見性:Synchronized、Volatile(不能確保原子性),
  有序性:Happens-before規矩,
  1.8彌補:怎么完成執行緒安全
  互斥同步:Synchronized、Lock,
  非堵塞同步:CAS,
  無需同步的方案:假如一個辦法原本就不涉及共享資料,那它天然就無需任何同步操作去確保正確性,
  1.9彌補:確保執行緒安全的機制:
  Synchronized關鍵字
  Lock
  CAS、原子變數
  ThreadLocl:簡略來說便是讓每個執行緒,對同一個變數,都有自己的獨有副本,每個執行緒實際拜訪的目標都是自己的,天然也就不存在執行緒安全問題了,
  Volatile
  CopyOnWrite寫時復制
  隨著CPU中心的增多以及互聯網迅速發展,單執行緒的程式處理速度越來越跟不上發展速度和大資料量的增長速度,多執行緒應運而生,充分利用CPU資源的一起,極大進步了程式處理速度,
  創立執行緒的辦法
  承繼Thread類:
  publicclassThreadCreateTest{
  publicstaticvoidmain(String[]args){
  newMyThread.start;
  }
  }
  classMyThreadextendsThread{
  @Override
  publicvoidrun{
  System.out.println(Thread.currentThread.getName+"t"+Thread.currentThread.getId);
  }
  }
  完成Runable介面:
  publicclassRunableCreateTest{
  publicstaticvoidmain(String[]args){
  MyRunnablerunnable=newMyRunnable;
  newThread(runnable).start;
  }
  }
  classMyRunnableimplementsRunnable{
  @Override
  publicvoidrun{
  System.out.println(Thread.currentThread.getName+"t"+Thread.currentThread.getId);
  }
  }
  經過Callable和Future創立執行緒:
  publicclassCallableCreateTest{
  publicstaticvoidmain(String[]args)throwsException{
  //將Callable包裝成FutureTask,FutureTask也是一種Runnable
  MyCallablecallable=newMyCallable;
  FutureTaskfutureTask=newFutureTask<>(callable);
  newThread(futureTask).start;
  //get辦法會堵塞呼叫的執行緒
  Integersum=futureTask.get;
  System.out.println(Thread.currentThread.getName+Thread.currentThread.getId+"="+sum);
  }
  }
  classMyCallableimplementsCallable{
  @Override
  publicIntegercallthrowsException{
  System.out.println(Thread.currentThread.getName+"t"+Thread.currentThread.getId+"t"+newDate+"tstarting...");
  intsum=0;
  for(inti=0;i<=100000;i++){
  sum+=i;
  }
  Thread.sleep(5000);
  System.out.println(Thread.currentThread.getName+"t"+Thread.currentThread.getId+"t"+newDate+"tover...");
  returnsum;
  }
  }
  執行緒池辦法創立:
  完成Runnable介面這種辦法更受歡迎,由于這不需求承繼Thread類,在運用規劃中現已承繼了其他目標的情況下,這需求多承繼(而Java不支撐多承繼,但可以多完成啊),只能完成介面,一起,執行緒池也是十分高效的,很容易完成和運用,
  實際開發中,阿里巴巴開發插件一向提倡運用執行緒池創立執行緒,原因在下方會解說,所以上面的代碼我就只簡寫了一些Demo,
  2.1執行緒池創立執行緒
  執行緒池,望文生義,執行緒寄存的當地,和資料庫連接池相同,存在的目的便是為了較少體系開銷,首要由以下幾個特點:
  下匠澩耗費,經過重復利用已創立的執行緒下降執行緒創立和毀掉形成的耗費(首要),
  進步回應速度,當使命抵達時,使命可以不需求比及執行緒創立就能立即履行,
  進步執行緒的可管理性,執行緒是稀缺資源,假如無限制地創立,不只會耗費體銑澩,還會下降體系的穩定性,
  Java供給四種執行緒池創立辦法:
  newCachedThreadPool創立一個可快取執行緒池,假如執行緒池長度超越處理需求,可靈敏識訓空閑執行緒,若無可識訓,則新建執行緒,
  newFixedThreadPool創立一個定長執行緒池,可操控線程最大并發數,超出的執行緒會在行列中等候,
  newScheduledThreadPool創立一個定長執行緒池,支撐定時及周期性使命履行,
  newSingleThreadExecutor創立一個單執行緒化的執行緒池,它只會用僅有的作業執行緒來履行使命,確保一切使命按照指定次序(FIFO,LIFO,優先級)履行,
  經過原始碼咱們得知ThreadPoolExecutor承繼自AbstractExecutorService,而AbstractExecutorService完成了ExecutorService,
  publicclassThreadPoolExecutorextendsAbstractExecutorService
  publicabstractclassAbstractExecutorServiceimplementsExecutorService
  2.2ThreadPoolExecutor介紹
  實際專案中,用的最多的便是ThreadPoolExecutor這個類,而《阿里巴巴Java開發手冊》中強制執行緒池不允許運用Executors去創立,而是經過NewThreadPoolExecutor實體的辦法,這樣的處理辦法讓寫的同學更加明確執行緒池的運轉規矩,規避資源耗盡的危險,
  咱們從ThreadPoolExecutor下手多執行緒創立辦法,先看一下執行緒池創立的最全引數,
  publicThreadPoolExecutor(intcorePoolSize,
  intmaximumPoolSize,
  longkeepAliveTime,
  TimeUnitunit,
  BlockingQueueworkQueue,
  ThreadFactorythreadFactory,
  RejectedExecutionHandlerhandler){
  if(corePoolSize<0||
  maximumPoolSize<=0||
  maximumPoolSize
  keepAliveTime<0)
  thrownewIllegalArgumentException;
  if(workQueue==null||threadFactory==null||handler==null)
  thrownewNullPointerException;
  this.corePoolSize=corePoolSize;
  this.maximumPoolSize=maximumPoolSize;
  this.workQueue=workQueue;
  this.keepAliveTime=unit.toNanos(keepAliveTime);
  this.threadFactory=threadFactory;
  this.handler=handler;
  }
  引數闡明如下:
  corePoolSize:執行緒池的中心執行緒數,即便執行緒池里沒有任何使命,也會有corePoolSize個執行緒在候著等使命,
  maximumPoolSize:最大執行緒數,不論提交多少使命,執行緒池里最多作業執行緒數便是maximumPoolSize,
  keepAliveTime:執行緒的存活時間,當執行緒池里的執行緒數大于corePoolSize時,假如等了keepAliveTime時長還沒有使命可履行,則執行緒退出,
  Unit:這個用來指定keepAliveTime的單位,比如秒:TimeUnit.SECONDS,
  BlockingQueue:一個堵塞行列,提交的使命將會被放到這個行列里,
  threadFactory:執行緒工廠,用來創立執行緒,首要是為了給執行緒起名字,默許工廠的執行緒名字:pool-1-thread-3,
  handler:回絕戰略,當執行緒池里執行緒被耗盡,且行列也滿了的時分會呼叫,
  2.2.1BlockingQueue
  對于BlockingQueue個人感徑訓需求單獨拿出來說一下,
  BlockingQueue:堵塞行列,有先進先出(重視公平性)和先進后出(重視時效性)兩種,常見的有兩種堵塞行列:ArrayBlockingQueue和LinkedBlockingQueue
  行列的資料結構大致如圖:
  行列一端進入,一端輸出,而當行列滿時,堵塞,BlockingQueue中心辦法:1.放入資料put2.獲取資料take,常見的兩種Queue:
  2.2.2ArrayBlockingQueue
  基于陣列完成,在ArrayBlockingQueue內部,保護了一個定長陣列,以便快取行列中的資料目標,這是一個常用的堵塞行列,除了一個定長陣列外,ArrayBlockingQueue內部還保存著兩個整形變數,分別標識著行列的頭部和尾部在陣列中的位置,
  一段代碼來驗證一下:
  packagemap;
  importjava.util.concurrent.*;
  publicclassMyTestMap{
  //界說堵塞行列巨細
  privatestaticfinalintmaxSize=5;
  publicstaticvoidmain(String[]args){
  ArrayBlockingQueuequeue=newArrayBlockingQueue(maxSize);
  newThread(newProductor(queue)).start;
  newThread(newCustomer(queue)).start;
  }
  }
  classCustomerimplementsRunnable{
  privateBlockingQueuequeue;
  Customer(BlockingQueuequeue){
  this.queue=queue;
  }
  @Override
  publicvoidrun{
  this.cusume;
  }
  privatevoidcusume{
  while(true){
  try{
  intcount=(int)queue.take;
  System.out.println("customer正在消費第"+count+"個產品===");
  //僅僅為了便利觀察輸出成果
  Thread.sleep(10);
  }catch(InterruptedExceptione){
  e.printStackTrace;
  }
  }
  }
  }
  classProductorimplementsRunnable{
  privateBlockingQueuequeue;
  privateintcount=1;
  Productor(BlockingQueuequeue){
  this.queue=queue;
  }
  @Override
  publicvoidrun{
  this.product;
  }
  privatevoidproduct{
  while(true){
  try{
  queue.put(count);
  System.out.println("出產者正在出產第"+count+"個產品");
  count++;
  }catch(InterruptedExceptione){
  e.printStackTrace;
  }
  }
  }
  }
  //輸出如下
  /**
  出產者正在出產第1個產品
  出產者正在出產第2個產品
  出產者正在出產第3個產品
  出產者正在出產第4個產品
  出產者正在出產第5個產品
  customer正在消費第1個產品===
  */
  2.2.3LinkedBlockingQueue
  基于鏈表的堵塞行列,內部也保護了一個資料緩沖行列,需求咱們留意的是假如結構一個LinkedBlockingQueue目標,而沒有指定其容量巨細,
  LinkedBlockingQueue會默許一個類似無限巨細的容量(Integer.MAX_VALUE),這樣的話,假如出產者的速度一旦大于顧客的速度,也許還沒有比及行列滿堵塞發生,體系記憶體就有或許已被耗費殆盡了,
  2.2.4LinkedBlockingQueue和ArrayBlockingQueue的首要差異
  ArrayBlockingQueue的初始化必須傳入行列巨細,LinkedBlockingQueue則可以不傳入,
  ArrayBlockingQueue用一把鎖操控并發,LinkedBlockingQueue倆把鎖操控并發,鎖的細粒度更細,即前者出產者顧客進出都是一把鎖,后者出產者出產進入是一把鎖,顧客消費是另一把鎖,
  ArrayBlockingQueue選用陣列的辦法存取,LinkedBlockingQueue用Node鏈表辦法存取,
  2.2.5handler回絕戰略
  Java供給了4種丟掉處理的辦法,當然你也可以自己完成,首要是要完成介面:RejectedExecutionHandler中的辦法,
  AbortPolicy:不處理,直接拋出反常,
  CallerRunsPolicy:只用呼叫者地點執行緒來運轉使命,即提交使命的執行緒,
  DiscardOldestPolicy:LRU戰略,丟掉行列里最近最久不運用的一個使命,并履行當時使命,
  DiscardPolicy:不處理,丟掉掉,不拋出反常,
  2.2.6執行緒池五種狀況
  privatestaticfinalintRUNNING=-1<
  privatestaticfinalintSHUTDOWN=0<
  privatestaticfinalintSTOP=1<
  privatestaticfinalintTIDYING=2<
  privatestaticfinalintTERMINATED=3<
  RUNNING:在這個狀況的執行緒池能判別承受新提交的使命,并且也能處理堵塞行列中的使命,
  SHUTDOWN:處于封閉的狀況,該執行緒池不能承受新提交的使命,可是可以處理堵塞行列中現已保存的使命,在執行緒處于RUNNING狀況,呼叫shutdown辦法能切換為該狀況,
  STOP:執行緒池處于該狀況時既不能承受新的使命也不能處理堵塞行列中的使命,并且能中止現在執行緒中的使命,當執行緒處于RUNNING和SHUTDOWN狀況,呼叫shutdownNow辦法就可以使執行緒變為該狀況,
  TIDYING:在SHUTDOWN狀況下堵塞行列為空,且執行緒中的作業執行緒數量為0就會進入該狀況,當在STOP狀況下時,只要執行緒中的作業執行緒數量為0就會進入該狀況,
  TERMINATED:在TIDYING狀況下呼叫terminated辦法就會進入該狀況,可以以為該狀況是最終的停止狀況,
  回到執行緒池創立ThreadPoolExecutor,咱們了解了這些引數,再來看看ThreadPoolExecutor的內部作業原理:
  判別中心執行緒是否已滿,是進入行列,否:創立執行緒
  判別等候行列是否已滿,是:查看執行緒池是否已滿,否:進入等候行列
  查看執行緒池是否已滿,是:回絕,否創立執行緒
  2.3深入了解ThreadPoolExecutor
  進入Execute辦法可以看到:
  publicvoidexecute(Runnablecommand){
  if(command==null)
  thrownewNullPointerException;
  intc=ctl.get;
  //判別當時活躍執行緒數是否小于corePoolSize,假如小于,則呼叫addWorker創立執行緒履行使命
  if(workerCountOf(c)
  if(addWorker(command,true))
  return;
  c=ctl.get;
  }
  //假如不小于corePoolSize,則將使命增加到workQueue行列,
  if(isRunning(c)&&workQueue.offer(command)){
  intrecheck=ctl.get;
  if(!isRunning(recheck)&&remove(command))
  reject(command);
  elseif(workerCountOf(recheck)==0)
  addWorker(null,false);
  }
  //假如放入workQueue失敗,則創立執行緒履行使命,假如這時創立執行緒失敗(當時執行緒數不小于maximumPoolSize時),就會呼叫reject(內部呼叫handler)回絕承受使命,
  elseif(!addWorker(command,false))
  reject(command);
  }
  AddWorker辦法:
  創立Worker目標,一起也會實體化一個Thread目標,在創立Worker時會呼叫threadFactory來創立一個執行緒,
  然后啟動這個執行緒,
  2.3.1執行緒池中CTL特點的效果是什么?
  看原始碼榜首反響便是這個CTL到底是個什么東東?有啥用?一番研究得出如下定論:
  CTL特點包括兩個概念:
  privatefinalAtomicIntegerctl=newAtomicInteger(ctlOf(RUNNING,0));
  privatestaticintctlOf(intrs,intwc){returnrs|wc;}
  runState:即rs標明當時執行緒池的狀況,是否處于Running,Shutdown,Stop,Tidying,
  workerCount:即wc標明當時有效的執行緒數,
  咱們點擊workerCount即作業狀況記載值,以RUNNING為例,RUNNING=-1<
  privatestaticfinalintCOUNT_BITS=Integer.SIZE-3;
  既然是29位那么便是Running的值為:
  11100000000000000000000000000000
  |||
  31~29位
  那低28位呢,便是記載當時執行緒的總線數啦:
  //Packingandunpackingctl
  privatestaticintrunStateOf(intc){returnc&~CAPACITY;}
  privatestaticintworkerCountOf(intc){returnc&CAPACITY;}
  privatestaticintctlOf(intrs,intwc){returnrs|wc;}
  從上述代碼可以看到workerCountOf這個函式傳入ctl之后,是經過CTL&CAPACITY操作來獲取當時運轉執行緒總數的,
  也便是RunningState|WorkCount&CAPACITY,算出來的便是低28位的值,由于CAPACITY得到的便是高3位(29-31位)位0,低28位(0-28位)都是1,所以得到的便是ctl中低28位的值,
  而runStateOf這個辦法的話,算的便是RunningState|WorkCount&CAPACITY,高3位的值,由于CAPACITY是CAPACITY的取反,所以得到的便是高3位(29-31位)為1,低28位(0-28位)為0,所以經過&運算后,所得到的值便是高3為的值,
  簡略來說便是ctl中是高3位作為狀況值,低28位作為執行緒總數值來進行存盤,
  2.3.2shutdownNow和shutdown的差異
  看原始碼發現有兩種近乎相同的辦法,shutdownNow和shutdown,規劃者這么規劃天然是有它的道理,那么這兩個辦法的差異在哪呢?
  shutdown會把執行緒池的狀況改為SHUTDOWN,而shutdownNow把當時執行緒池狀況改為STOP,
  shutdown只會中止一切空閑的執行緒,而shutdownNow會中止一切的執行緒,
  shutdown回傳辦法為空,會將當時使命行列中的一切使命履行完畢;而shutdownNow把使命行列中的一切使命都取出來回傳,
  2.3.3執行緒復用原理
  finalvoidrunWorker(Workerw){
  Threadwt=Thread.currentThread;
  Runnabletask=w.firstTask;
  w.firstTask=null;
  w.unlock;//allowinterrupts
  booleancompletedAbruptly=true;
  try{
  while(task!=null||(task=getTask)!=null){
  w.lock;
  //Ifpoolisstopping,ensurethreadisinterrupted;
  //ifnot,ensurethreadisnotinterrupted.This
  //requiresarecheckinsecondcasetodealwith
  //shutdownNowracewhileclearinginterrupt
  if((runStateAtLeast(ctl.get,STOP)||
  (Thread.interrupted&&
  runStateAtLeast(ctl.get,STOP)))&&
  !wt.isInterrupted)
  wt.interrupt;
  try{
  beforeExecute(wt,task);
  Throwablethrown=null;
  try{
  task.run;
  }catch(RuntimeExceptionx){
  thrown=x;throwx;
  }catch(Errorx){
  thrown=x;throwx;
  }catch(Throwablex){
  thrown=x;thrownewError(x);
  }finally{
  afterExecute(task,thrown);
  }
  }finally{
  task=null;
  w.completedTasks++;
  w.unlock;
  }
  }
  completedAbruptly=false;
  }finally{
  processWorkerExit(w,completedAbruptly);
  }
  }
  便是使命在并不只履行創立時指定的firstTask榜首使命,還會從使命行列的中自己主動取使命履行,并且是有或許無時間限制的堵塞等候,以確保執行緒的存活,
  默許的是不允許,
  2.4CountDownLatch和CyclicBarrier差異
  countDownLatch是一個計數器,執行緒完成一個記載一個,計數器遞減,只能只用一次,
  CyclicBarrier的計數器更像一個閥門,需求一切執行緒都抵達,然后持續履行,計數器遞加,供給Reset功用,可以多次運用,
  3.多執行緒間通訊的幾種辦法
  提及多執行緒又不得不提及多執行緒通訊的機制,首要,要短信執行緒間通訊的模型有兩種:共享記憶體和訊息傳遞,以下辦法都是基本這兩種模型來完成的,咱們來基本一道面試常見的標題來分析:
  標題:有兩個執行緒A、B,A執行緒向一個集合里面順次增加元素"abc"字串,一共增加十次,當增加到第五次的時分,希望B執行緒可以收到A執行緒的告訴,然后B執行緒履行相關的業務操作,
  3.1運用volatile關鍵字
  packagethread;
  /**
  *
  *@authorhxz
  *@deion多執行緒測驗類
  *@version1.0
  *@data2020年2月15日上午9:10:09
  */
  publicclassMyThreadTest{
  publicstaticvoidmain(String[]args)throwsException{
  notifyThreadWithVolatile;
  }
  /**
  *界說一個測驗
  */
  privatestaticvolatilebooleanflag=false;
  /**
  *計算I++,當I==5時,告訴執行緒B
  *@throwsException
  */
  privatestaticvoidnotifyThreadWithVolatilethrowsException{
  Threadthc=newThread("執行緒A"){
  @Override
  publicvoidrun{
  for(inti=0;i<10;i++){
  if(i==5){
  flag=true;
  try{
  Thread.sleep(500L);
  }catch(InterruptedExceptione){
  //TODOAuto-generatedcatchblock
  e.printStackTrace;
  }
  break;
  }
  System.out.println(Thread.currentThread.getName+"===="+i);
  }
  }
  };
  Threadthd=newThread("執行緒B"){
  @Override
  publicvoidrun{
  while(true){
  //防止偽喚醒所以運用了while
  while(flag){
  System.out.println(Thread.currentThread.getName+"收到告訴");
  System.out.println("dosomething");
  try{
  Thread.sleep(500L);
  }catch(InterruptedExceptione){
  //TODOAuto-generatedcatchblock
  e.printStackTrace;
  }
  return;
  }
  }
  }
  };
  thd.start;
  Thread.sleep(1000L);
  thc.start;
  }
  }
  個人以為這是基本上最好的通訊辦法,由于A發出告訴B可以立馬承受并DoSomething,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/168594.html

標籤:Java

上一篇:深入決議ThreadLocal和ThreadLocalMap

下一篇:C#與Java的區別

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more