熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java高級技術 >> 正文

Java多線程提升

2022-06-13   來源: Java高級技術 

  一多線程基本概念

  獨占:通過阻止多個並發行為間的有害干擾來維護狀態的一致性通常使用異步方法sychronized

  狀態依賴:觸發阻止延遲恢復某些行為是由一些對象是否處在這些行為可能成功或者已經

  成功的狀態決定的主要通過監視器(monitor)實現 objectwait objectnotify objectnotifyAll

  客戶Client: Client Object 期待某個動作的執行

  服務Serverce :包括執行這個動作的代碼

  主體客戶和服務都可以是一個對象這個對象就是一個主體而又他調用的對象通常就是輔助對象(help)

  Synchronized: 防止並法導致數據不一致可以修飾一個方法或者代碼塊

  Object 或子類的每一個實例都會在進入這個synchronized方法前加鎖並在離開這個方法時自動釋放鎖

  使用synchronized關鍵字來定義方法就會鎖定類中所有使用synchronzied關鍵字定義的靜態方法或非靜態方法

  不同的對象實例的synchronized方法是不相干擾的也就是說其它線程照樣可以同時訪問相同類的另一個對象實例中的synchronized方法

  對於修飾靜態的方法它可以對類的所有對象實例起作用

  對代碼塊的加鎖也是只是需要指明對那個對象加鎖在靜態代碼塊中使用類class來指明

  在非靜態代碼塊中多數是用this

  synchronized關鍵字是不能繼承的也就是說基類的方法synchronized f(){} 在繼承類中並不自動是synchronized f(){}而是變成了f(){}繼承類需要你顯式的指定它的某個方法為synchronized方法

  規則

  )永遠只是在更新對象的成員變量是加鎖

  )永遠只是在訪問有可能被更新對象的成員變量時加鎖

  )永遠不要在調用其它對象的方法時加鎖

  二 ncurent 包介紹

  執行器(Executor) 用來管理Thread 對象下面介紹三種Executor

  () newCachedThreadPool

  ExecutorService exec = ExecutorsnewCachedThreadPool();//創建一個可根據需要創建新線程的線程池

  for(int  = ; i<; i++){

  execexecute(new LiftOff());//LiftOff implements Runnable接口的類

  execshutdown()

  }

  () newFixedThreadPool

  ExecutorService exec = ExecutorsnewFixedThreadPool();//創建一個可重用固定線程數的線程池以共享的無界隊列方式來運行這些線程

  for(int  = ; i<; i++){

  execexecute(new LiftOff());//LiftOff implements Runnable接口的類

  execshutdown()

  }

  () newSingleThreadPool

  SingleThreadPool 就好象是線程數量為的FixedThreadPool如果向SingleThreadPool提交多個任務那麼這些任務都將排列每個任務都會在下一個任務開始之前運行結束

  ExecutorService exec = ExecutorsnewSingleThreadPool();//創建一個使用單個 worker 線程的 Executor以無界隊列方式來運行該線程

  for(int  = ; i<; i++){

  execexecute(new LiftOff());//LiftOff implements Runnable接口的類

  execshutdown()

  }

  從任務中產生返回值

  Runnable是執行工作的獨立任務但是不返回任何值如果實現Callable 接口則Callable接口方法call()可以返回一個值

  package cdlchen;

  /**

  * @ Class CallableDemojava

  * @ Description

  * @ Company OpenData

  * @ Author yinlei

  * @ Version

  * @ Date Mar

  */

  import javautilArrayList;

  import ncurrentCallable;

  import ncurrentExecutorService;

  import ncurrentExecutors;

  import ncurrentFuture;

  public class CallableDemo{

  public static void main(String[] args){

  ExecutorService exec = ExecutorsnewCachedThreadPool();

  ArrayList<Future<String>> results = new ArrayList<Future<String>>();

  for(int i = ; i < ; i++){

  resultsadd(execsubmit(new TaskWithResult(i)));//這裡使用submit 方法會產生Future 對象

  }

  for(Future<String> fs:results){

  try{

  if(fsisDone()){//檢查Future是否已經完成或者不用isDone()來檢查那麼get()將會阻塞直到結果准備就緒

  Systemoutprintln(fsget());//通過get()得到call()方法返回的值

  }

  } catch(Exception ex){

  exprintStackTrace();

  } finally{

  execshutdown();

  }

  }

  }

  }

  class TaskWithResult implements Callable<String>{

  private int id;

  public TaskWithResult(int id){

  thisid = id;

  }

  public String call(){

  return result of TaskWithResult + id;

  }

  }

  //結果

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  result of TaskWithResult

  sleep() 和wait()方法的區別

  sleep是Thread類的靜態方法sleep的作用是讓線程休眠制定的時間在時間到達時恢復也就是說sleep將在接到時間到達事件事恢復線程執行其控制范圍是由當前線程塊

  wait是Object的方法也就是說可以對任意一個對象調用wait方法調用wait方法將會將調用者的線程掛起直到其他線程調用同一個對象的notify方法才會重新激活調用者

  sleep()是讓某個線程暫停運行一段時間其控制范圍是由當前線程決定也就是說在線程裡面決定好比如說我要做的事情是 點火>燒水>煮面而當我點完火之後我不立即燒水我要休息一段時間再燒對於運行的主動權是由我的流程來控制

  而wait()首先這是由某個確定的對象來調用的將這個對象理解成一個傳話的人當這個人在某個線程裡面說暫停!也是 thisOBJwait()這裡的暫停是阻塞還是點火>燒水>煮飯thisOBJ就好比一個監督我的人站在我旁邊本來該線 程應該執行後執行再執行

  而在處被那個對象喊暫停那麼我就會一直等在這裡而不執行但正個流程並沒有結束我一直想去煮飯但還沒被允許 直到那個對象在某個地方說通知暫停的線程啟動!也就是thisOBJnotify()的時候那麼我就可以煮飯了這個被暫停的線程就會從暫停處 繼續執行

  本質的區別是一個線程的運行狀態控制一個是線程之間的通訊的問題

  tjoin();方法

  package cdlchen;

  /**

  * @ Class Joiningjava

  * @ Description

  * @ Company OpenData

  * @ Author yinlei

  * @ Version

  * @ Date Mar

  */

  class Sleeper extends Thread{

  private int duration;

  public Sleeper(String name int sleepTime){

  super(name);

  duration = sleepTime;

  start();

  }

  public void run(){

  try{

  sleep(duration);

  }catch(InterruptedException ex){

  Systemoutprintln(thisgetName()+ was interrupted+isInterrupted():+thisisInterrupted());

  }

  Systemoutprintln(thisgetName()+ has awakened);

  }

  }

  class Joiner extends Thread{

  private Sleeper sleeper;

  public Joiner(String name Sleeper sleeper){

  super(name);

  thissleeper = sleeper;

  start();

  }

  public void run(){

  try{

  sleeperjoin();//線程Sleeper在線程Joiner上執行join()則Joiner 線程將被掛起直到sleeper 線程結束(isAlive()==false)才恢復

  } catch(InterruptedException ex){

  Systemoutprint(Interrupted);

  }

  Systemoutprintln(thisgetName()+ join completed);

  }

  }

  public class Joining {

  public static void main(String []args){

  Sleeper sleepy = new Sleeper(Sleepy);

  Sleeper grumpy = new Sleeper(Grumpy);

  Joiner dopey = new Joiner(Dopeysleepy);

  Joiner doc = new Joiner(Docgrumpy);

  grumpyinterrupt();//grumpy 中斷doc 將繼續

  }

  }

  out put:

  Grumpy was interruptedisInterrupted():false//當線程在該線程上調用interrupt()時將給該線程設定一個標志然而異常在捕獲時會清理這個標志所以在catch 代碼塊裡調用interrupt()總是false

  Grumpy has awakened

  Doc join completed

  Sleepy has awakened

  Dopey join completed

  顯示加鎖ncurrentlocks

  public synchronized int next(){

  

  try{

  return

  }catch(){

  }

  }

  private Lock lock = new ReentrantLock();

  public  int next(){

  try{

  locklock();

  

  return

  } catch(){

  lockunlock();

  }

  使用synchronized關鍵字和顯示Lock都可以解決資源同步的問題並且synchronized 關鍵字比顯示加鎖代碼量少更少但是當synchronized關鍵字不

  能嘗試獲取鎖一段時間然後放棄它

  線程本地化javalangThreadLocal

  它的功用非常簡單就是為每一個使用該變量的線程都提供一個變量值的副本是每一個線程都可以獨立地改變自己的副本而不會和其它線程的副本沖突從線程的角度看就好像每一個線程都完全擁有該變量

  通常通過get()set()方法來訪問該對象的內容get()方法返回與其線程相關聯的對象副本而set()方法將參數值插入到為其線程存儲的對象裡

  package cdlchen;

  import javautilRandom;

  import ncurrentExecutorService;

  import ncurrentExecutors;

  import ncurrentTimeUnit;

  class Accessor implements Runnable{

  private final int id;

  public Accessor(int idn){id = idn;}

  public void run(){

  while(!ThreadcurrentThread()isInterrupted()){

  ThreadLocalVariableHolderincrement();

  Systemoutprintln(this);

  Threadyield();

  }

  }

  public String toString(){

  return #+id+:+ThreadLocalVariableHolderget();

  }

  }

  public class ThreadLocalVariableHolder{

  private static ThreadLocal<Integer> value=

  new ThreadLocal<Integer>(){

  private Random rand = new Random();

  protected synchronized Integer initialValue(){

  return randnextInt();

  }

  };

  public static void increment(){

  valueset(valueget() + );

  }

  public static int get(){return valueget();}

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

  ExecutorService exec = ExecutorsnewCachedThreadPool();

  for(int i = ; i < ; i++){

  execexecute(new Accessor(i));

  }

  TimeUnitSECONDSsleep();

  execshutdownNow();

  }

  }

  //output:

  #:

  #:

  #:

  #:

  #:

  #:

  #:

  #:

  #:

  線程中捕獲異常

  任務的run()方法通常總會有某種形式的循環使得任務一直運行下去直到不再需要所以要設定跳出循環的條件

  通常它被寫成無限循環的形式這就意味著除非有個條件使用它終止否則它將永遠運行下去

  由於線程的本質特性所以我們不能捕獲到從線程中逃逸的異常比如:

  package chenlly;

  public class ThreadException implements Runnable {

  public void run() {

  throw new RuntimeException(Im throw Exception);

  }

  public static void main(String []args){

  try{

  Thread t = new Thread(new ThreadException());

  tstart();

  } catch(Exception ex){

  Systemoutprintln(Exception has been handled!);

  exprintStackTrace();

  }

  }

  }

  /* output

  Exception in thread Thread javalangRuntimeException: Im throw Exception

  at chenllyThreadExceptionrun(ThreadExceptionjava:)

  at javalangThreadrun(Unknown Source)

  也就是說tyr catch 無法捕獲到從run 方法裡逃逸的異常

  ThreadUncaughtExceptionHandler這個接口就是當 Thread 因未捕獲的異常而突然終止時調用處理程序的接口

  它允許你在每個Thread對象上都附著一個異常處理器

  package chenlly;

  public class ThreadException implements Runnable {

  public void run() {

  throw new RuntimeException(Im throw Exception );

  }

  public static void main(String[] args) {

  Thread t = new Thread(new ThreadException());

  tsetUncaughtExceptionHandler(new MyUncaughtException());//添加一個異常處理器

  tstart();

  }

  }

  //異常處理器

  class MyUncaughtException implements ThreadUncaughtExceptionHandler {

  public void uncaughtException(Thread t Throwable e) {

  Systemoutprintln(t + : + egetMessage());

  }

  }

  /* output

  Thread[Threadmain] : Im throw Exception

  線程之間的協助

  多個任務可以通過使用鎖或者synchronized(互斥)的方法來同步兩個線程的行為從而使的一個任務不會干涉另外一個任務的資源

  那麼如何使得多個任務可以在一起彼此協作去解決某個問題使用相同的互斥基礎互斥互斥能夠確保只有一個任務可以響應某個信號

  eg:一個是將蠟塗到Car上一個是拋光它拋光任務在塗蠟完成之前是不能操作的而塗蠟任務在塗另外一層蠟之前必須等待拋光結束

  car 對象通過wait和notifyAll來掛起和重新啟動這些任務

  package chenlly;

  import ncurrentExecutorService;

  import ncurrentExecutors;

  import ncurrentTimeUnit;

  class Car{

  private boolean waxOn = true;//默認為拋光狀態同步信號

  //打蠟完成設置狀態並且喚醒打蠟

  public synchronized void waxed(){

  waxOn = false;

  notifyAll();

  }

  //拋光完成設置狀態並且喚醒打蠟

  public synchronized void buffing(){

  waxOn = true;

  notifyAll();

  }

  //是否拋光結束

  public synchronized void waitForWaxed() throws InterruptedException{

  while(!waxOn){

  wait();

  }

  }

  //是否打蠟結束

  public synchronized void waitForBuffing() throws InterruptedException{

  while(waxOn){

  wait();

  }

  }

  }

  //打蠟

  class WaxOn  implements Runnable{

  private Car car;

  public WaxOn(Car car){

  thiscar = car;

  }

  public void run() {

  try{

  while(!Threadinterrupted()){

  carwaitForWaxed();

  Systemoutprintln(Wax On!);

  TimeUnitSECONDSsleep();

  carwaxed();

  }

  } catch (Exception ex){

  exprintStackTrace();

  }

  }

  }

  //拋光

  class Buffing  implements Runnable{

  private Car car;

  public Buffing(Car car){

  thiscar = car;

  }

  public void run() {

  try{

  while(!Threadinterrupted()){

  carwaitForBuffing();

  Systemoutprintln(Buffing On!);

  TimeUnitSECONDSsleep();

  carbuffing();

  }

  } catch (Exception ex){

  exprintStackTrace();

  }

  }

  }

  public class WaxOMatic {

  public static void main(String []args) {

  try{

  Car car = new Car();

  ExecutorService exec = ExecutorsnewCachedThreadPool();

  execexecute(new WaxOn(car));

  execexecute(new Buffing(car));

  TimeUnitSECONDSsleep();

  execshutdownNow();

  } catch (InterruptedException ex){

  exprintStackTrace();

  }

  }

  }

  /* output

  Wax On!

  Buffing On!

  Wax On!

  Buffing On!

  Wax On!

  Buffing On!

  打蠟和拋光相繼進行控制權在這兩者之間相互傳遞時這兩個步驟過程在不斷地重復秒後中斷這兩個任務

  線程死鎖問題

  死鎖某個任務在等待另外一個任務而後者又在等待別的任務這樣一直下去任務之間連續的循環就是死鎖問題

  典型的哲學家就餐就是死鎖問題

  死鎖的四個必要條件

  () 互斥條件:資源不能被共享只能由一個進程使用

  () 請求與保持條件(Hold and wait):已經得到資源的進程可以再次申請新的資源

  () 非剝奪條件:經分配的資源不能從相應的進程中被強制地剝奪

  () 循環等待條件(Circular wait):系統中若干進程組成環路該環路中每個進程都在等待相鄰進程正占用的資源

  處理死鎖的策略

  通過破除死鎖四個必要條件之一來防止死鎖產生

  哲學家就餐問題:該問題涉及到五個哲學家他們交替地進行思考和進餐

  他們分別坐在位於一個圓形餐桌周圍的五把椅子上圓桌中央是一碗米飯餐桌上共有五根筷子

  分別擺放在每兩個相鄰座位的中間

  當哲學家思考時他不與其他人交談當哲學家饑餓時他將拿起和他相鄰的兩根筷子進行進餐

  但他很可能僅拿到一根此時旁邊的另一根正在他鄰居的手中

  只有他同時拿到兩根筷子時他才能開始進餐完成進餐後

  他將兩根筷子分別放回原位然後再次開始思考


From:http://tw.wingwit.com/Article/program/Java/gj/201311/27658.html
  • 上一篇文章:

  • 下一篇文章:
  • 推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.