亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

淺談java多線程編程

 更新時(shí)間:2020年08月05日 14:54:51   作者:阿凡盧  
這篇文章主要介紹了java多線程編程的相關(guān)資料,文中講解非常細(xì)致,幫助大家更好的理解和學(xué)習(xí)java多線程,感興趣的朋友可以了解下

一、多線程的優(yōu)缺點(diǎn)

多線程的優(yōu)點(diǎn):

1)資源利用率更好
2)程序設(shè)計(jì)在某些情況下更簡(jiǎn)單
3)程序響應(yīng)更快

多線程的代價(jià):

1)設(shè)計(jì)更復(fù)雜
雖然有一些多線程應(yīng)用程序比單線程的應(yīng)用程序要簡(jiǎn)單,但其他的一般都更復(fù)雜。在多線程訪問(wèn)共享數(shù)據(jù)的時(shí)候,這部分代碼需要特別的注意。線程之間的交互往往非常復(fù)雜。不正確的線程同步產(chǎn)生的錯(cuò)誤非常難以被發(fā)現(xiàn),并且重現(xiàn)以修復(fù)。

2)上下文切換的開(kāi)銷(xiāo)
當(dāng)CPU從執(zhí)行一個(gè)線程切換到執(zhí)行另外一個(gè)線程的時(shí)候,它需要先存儲(chǔ)當(dāng)前線程的本地的數(shù)據(jù),程序指針等,然后載入另一個(gè)線程的本地?cái)?shù)據(jù),程序指針等,最后才開(kāi)始執(zhí)行。這種切換稱(chēng)為“上下文切換”(“context switch”)。CPU會(huì)在一個(gè)上下文中執(zhí)行一個(gè)線程,然后切換到另外一個(gè)上下文中執(zhí)行另外一個(gè)線程。上下文切換并不廉價(jià)。如果沒(méi)有必要,應(yīng)該減少上下文切換的發(fā)生。

二、創(chuàng)建java多線程

1、創(chuàng)建Thread的子類(lèi)

創(chuàng)建Thread子類(lèi)的一個(gè)實(shí)例并重寫(xiě)run方法,run方法會(huì)在調(diào)用start()方法之后被執(zhí)行。例子如下:

public class MyThread extends Thread {
  public void run(){
   System.out.println("MyThread running");
  }
}

MyThread myThread = new MyThread();
myTread.start();

也可以如下創(chuàng)建一個(gè)Thread的匿名子類(lèi):

Thread thread = new Thread(){
  public void run(){
   System.out.println("Thread Running");
  }
};
thread.start();

2、實(shí)現(xiàn)Runnable接口

第二種編寫(xiě)線程執(zhí)行代碼的方式是新建一個(gè)實(shí)現(xiàn)了java.lang.Runnable接口的類(lèi)的實(shí)例,實(shí)例中的方法可以被線程調(diào)用。下面給出例子:

public class MyRunnable implements Runnable {
  public void run(){
  System.out.println("MyRunnable running");
  }
}

Thread thread = new Thread(new MyRunnable());
thread.start();

同樣,也可以創(chuàng)建一個(gè)實(shí)現(xiàn)了Runnable接口的匿名類(lèi),如下所示:

Runnable myRunnable = new Runnable(){
  public void run(){
   System.out.println("Runnable running");
  }
}
Thread thread = new Thread(myRunnable);
thread.start();

三、線程安全

在同一程序中運(yùn)行多個(gè)線程本身不會(huì)導(dǎo)致問(wèn)題,問(wèn)題在于多個(gè)線程訪問(wèn)了相同的資源。如同一內(nèi)存區(qū)(變量,數(shù)組,或?qū)ο螅?、系統(tǒng)(數(shù)據(jù)庫(kù),web services等)或文件。實(shí)際上,這些問(wèn)題只有在一或多個(gè)線程向這些資源做了寫(xiě)操作時(shí)才有可能發(fā)生,只要資源沒(méi)有發(fā)生變化,多個(gè)線程讀取相同的資源就是安全的。

當(dāng)兩個(gè)線程競(jìng)爭(zhēng)同一資源時(shí),如果對(duì)資源的訪問(wèn)順序敏感,就稱(chēng)存在競(jìng)態(tài)條件。導(dǎo)致競(jìng)態(tài)條件發(fā)生的代碼區(qū)稱(chēng)作臨界區(qū)。

如果一個(gè)資源的創(chuàng)建,使用,銷(xiāo)毀都在同一個(gè)線程內(nèi)完成,且永遠(yuǎn)不會(huì)脫離該線程的控制,則該資源的使用就是線程安全的。

四、java同步塊

Java中的同步塊用synchronized標(biāo)記。同步塊在Java中是同步在某個(gè)對(duì)象上。所有同步在一個(gè)對(duì)象上的同步塊在同時(shí)只能被一個(gè)線程進(jìn)入并執(zhí)行操作。所有其他等待進(jìn)入該同步塊的線程將被阻塞,直到執(zhí)行該同步塊中的線程退出。

有四種不同的同步塊:

  1. 實(shí)例方法
  2. 靜態(tài)方法
  3. 實(shí)例方法中的同步塊
  4. 靜態(tài)方法中的同步塊

實(shí)例方法同步:

 public synchronized void add(int value){
this.count += value;
 }

Java實(shí)例方法同步是同步在擁有該方法的對(duì)象上。這樣,每個(gè)實(shí)例其方法同步都同步在不同的對(duì)象上,即該方法所屬的實(shí)例。只有一個(gè)線程能夠在實(shí)例方法同步塊中運(yùn)行。如果有多個(gè)實(shí)例存在,那么一個(gè)線程一次可以在一個(gè)實(shí)例同步塊中執(zhí)行操作。一個(gè)實(shí)例一個(gè)線程。

靜態(tài)方法同步:

public static synchronized void add(int value){
 count += value;
 }

靜態(tài)方法的同步是指同步在該方法所在的類(lèi)對(duì)象上。因?yàn)樵贘ava虛擬機(jī)中一個(gè)類(lèi)只能對(duì)應(yīng)一個(gè)類(lèi)對(duì)象,所以同時(shí)只允許一個(gè)線程執(zhí)行同一個(gè)類(lèi)中的靜態(tài)同步方法。

實(shí)例方法中的同步塊:

public void add(int value){
  synchronized(this){
    this.count += value;
  }
 }

注意Java同步塊構(gòu)造器用括號(hào)將對(duì)象括起來(lái)。在上例中,使用了“this”,即為調(diào)用add方法的實(shí)例本身。在同步構(gòu)造器中用括號(hào)括起來(lái)的對(duì)象叫做監(jiān)視器對(duì)象。上述代碼使用監(jiān)視器對(duì)象同步,同步實(shí)例方法使用調(diào)用方法本身的實(shí)例作為監(jiān)視器對(duì)象。一次只有一個(gè)線程能夠在同步于同一個(gè)監(jiān)視器對(duì)象的Java方法內(nèi)執(zhí)行。

下面兩個(gè)例子都同步他們所調(diào)用的實(shí)例對(duì)象上,因此他們?cè)谕降膱?zhí)行效果上是等效的。

public class MyClass {

  public synchronized void log1(String msg1, String msg2){
   log.writeln(msg1);
   log.writeln(msg2);
  }

  public void log2(String msg1, String msg2){
   synchronized(this){
     log.writeln(msg1);
     log.writeln(msg2);
   }
  }
 }

靜態(tài)方法中的同步塊:

public class MyClass {
  public static synchronized void log1(String msg1, String msg2){
    log.writeln(msg1);
    log.writeln(msg2);
  }

  public static void log2(String msg1, String msg2){
    synchronized(MyClass.class){
     log.writeln(msg1);
     log.writeln(msg2);
    }
  }
 }

這兩個(gè)方法不允許同時(shí)被線程訪問(wèn)。如果第二個(gè)同步塊不是同步在MyClass.class這個(gè)對(duì)象上。那么這兩個(gè)方法可以同時(shí)被線程訪問(wèn)。

五、java線程通信

線程通信的目標(biāo)是使線程間能夠互相發(fā)送信號(hào)。另一方面,線程通信使線程能夠等待其他線程的信號(hào)。

Java有一個(gè)內(nèi)建的等待機(jī)制來(lái)允許線程在等待信號(hào)的時(shí)候變?yōu)榉沁\(yùn)行狀態(tài)。java.lang.Object 類(lèi)定義了三個(gè)方法,wait()、notify()和notifyAll()來(lái)實(shí)現(xiàn)這個(gè)等待機(jī)制。

一個(gè)線程一旦調(diào)用了任意對(duì)象的wait()方法,就會(huì)變?yōu)榉沁\(yùn)行狀態(tài),直到另一個(gè)線程調(diào)用了同一個(gè)對(duì)象的notify()方法。為了調(diào)用wait()或者notify(),線程必須先獲得那個(gè)對(duì)象的鎖。也就是說(shuō),線程必須在同步塊里調(diào)用wait()或者notify()。

以下為一個(gè)使用了wait()和notify()實(shí)現(xiàn)的線程間通信的共享對(duì)象:

public class MyWaitNotify{

 MonitorObject myMonitorObject = new MonitorObject();
 boolean wasSignalled = false;

 public void doWait(){
  synchronized(myMonitorObject){
   while(!wasSignalled){
    try{
     myMonitorObject.wait();
     } catch(InterruptedException e){...}
   }
   //clear signal and continue running.
   wasSignalled = false;
  }
 }

 public void doNotify(){
  synchronized(myMonitorObject){
   wasSignalled = true;
   myMonitorObject.notify();
  }
 }
}

注意以下幾點(diǎn):

1、不管是等待線程還是喚醒線程都在同步塊里調(diào)用wait()和notify()。這是強(qiáng)制性的!一個(gè)線程如果沒(méi)有持有對(duì)象鎖,將不能調(diào)用wait(),notify()或者notifyAll()。否則,會(huì)拋出IllegalMonitorStateException異常。

2、一旦線程調(diào)用了wait()方法,它就釋放了所持有的監(jiān)視器對(duì)象上的鎖。這將允許其他線程也可以調(diào)用wait()或者notify()。

3、為了避免丟失信號(hào),必須把它們保存在信號(hào)類(lèi)里。如上面的wasSignalled變量。

4、假喚醒:由于莫名其妙的原因,線程有可能在沒(méi)有調(diào)用過(guò)notify()和notifyAll()的情況下醒來(lái)。這就是所謂的假喚醒(spurious wakeups)。為了防止假喚醒,保存信號(hào)的成員變量將在一個(gè)while循環(huán)里接受檢查,而不是在if表達(dá)式里。這樣的一個(gè)while循環(huán)叫做自旋鎖。

5、不要在字符串常量或全局對(duì)象中調(diào)用wait()。即上面MonitorObject不能是字符串常量或是全局對(duì)象。每一個(gè)MyWaitNotify的實(shí)例都擁有一個(gè)屬于自己的監(jiān)視器對(duì)象,而不是在空字符串上調(diào)用wait()/notify()。

六、java中的鎖

自Java 5開(kāi)始,java.util.concurrent.locks包中包含了一些鎖的實(shí)現(xiàn),因此你不用去實(shí)現(xiàn)自己的鎖了。

常用的一些鎖:

java.util.concurrent.locks.Lock;
java.util.concurrent.locks.ReentrantLock;
java.util.concurrent.locks.ReadWriteLock;
java.util.concurrent.locks.ReentrantReadWriteLock;

一個(gè)可重入鎖(reentrant lock)的簡(jiǎn)單實(shí)現(xiàn):

public class Lock {
  boolean isLocked = false;
  Thread lockedBy = null;
  int lockedCount = 0;

  public synchronized void lock() throws InterruptedException{
    Thread callingThread = Thread.currentThread();
    while(isLocked && lockedBy != callingThread){
      wait();
    }
    isLocked = true;
    lockedCount++;
    lockedBy = callingThread;
  }

  public synchronized void unlock(){
    if(Thread.currentThread() == this.lockedBy){
      lockedCount--;
      if(lockedCount == 0){
        isLocked = false;
        notify();
      }
    }
  }
}

注意的一點(diǎn):在finally語(yǔ)句中調(diào)用unlock()

lock.lock();
try{
  //do critical section code, which may throw exception
} finally {
  lock.unlock();
}

七、java中其他同步方法

信號(hào)量(Semaphore):java.util.concurrent.Semaphore

阻塞隊(duì)列(Blocking Queue):java.util.concurrent.BlockingQueue

public class BlockingQueue {
  private List queue = new LinkedList();
  private int limit = 10;

  public BlockingQueue(int limit) {
    this.limit = limit;
  }

  public synchronized void enqueue(Object item) throws InterruptedException {
    while (this.queue.size() == this.limit) {
      wait();
    }
    if (this.queue.size() == 0) {
      notifyAll();
    }
    this.queue.add(item);
  }

  public synchronized Object dequeue() throws InterruptedException {
    while (this.queue.size() == 0) {
      wait();
    }
    if (this.queue.size() == this.limit) {
      notifyAll();
    }
    return this.queue.remove(0);
  }
}

八、java中的線程池

Java通過(guò)Executors提供四種線程池,分別為:

newCachedThreadPool

創(chuàng)建一個(gè)可緩存的線程池。如果線程池的大小超過(guò)了處理任務(wù)所需要的線程,那么就會(huì)回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當(dāng)任務(wù)數(shù)增加時(shí),此線程池又可以智能的添加新線程來(lái)處理任務(wù)。此線程池不會(huì)對(duì)線程池大小做限制,線程池大小完全依賴(lài)于操作系統(tǒng)(或者說(shuō)JVM)能夠創(chuàng)建的最大線程大小。

newFixedThreadPool

創(chuàng)建固定大小的線程池。每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程,直到線程達(dá)到線程池的最大大小。線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束,那么線程池會(huì)補(bǔ)充一個(gè)新線程。

newScheduledThreadPool

創(chuàng)建一個(gè)大小無(wú)限制的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)。

newSingleThreadExecutor

創(chuàng)建一個(gè)單線程的線程池。此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)。這個(gè)線程池只有一個(gè)線程在工作,也就是相當(dāng)于單線程串行執(zhí)行所有任務(wù)。如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來(lái)替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。

線程池簡(jiǎn)單用法:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
  public static void main(String[] args) {
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 10; i++) {
      final int index = i;
      cachedThreadPool.execute(new Runnable() {
        public void run() {
          System.out.println(index);
        }
      });
    }
  }
}

以上就是淺談java多線程編程的詳細(xì)內(nèi)容,更多關(guān)于java多線程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java設(shè)計(jì)模式之迭代器模式解析

    Java設(shè)計(jì)模式之迭代器模式解析

    這篇文章主要介紹了Java設(shè)計(jì)模式之迭代器模式解析,迭代器模式提供一個(gè)對(duì)象來(lái)順序訪問(wèn)聚合對(duì)象中的一系列數(shù)據(jù),而不暴露聚合對(duì)象的內(nèi)部表示,本文提供了部分代碼,需要的朋友可以參考下
    2023-09-09
  • Java生成的隨機(jī)數(shù)靠譜嗎?多少次會(huì)重復(fù)?

    Java生成的隨機(jī)數(shù)靠譜嗎?多少次會(huì)重復(fù)?

    今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Java生成的隨機(jī)數(shù)靠不靠譜展開(kāi),文中有非常詳細(xì)的介紹,需要的朋友可以參考下
    2021-06-06
  • Java線程阻塞的方法區(qū)別詳解

    Java線程阻塞的方法區(qū)別詳解

    這篇文章主要介紹了Java線程阻塞的方法區(qū)別詳解,線程阻塞是指當(dāng)一個(gè)線程無(wú)法繼續(xù)執(zhí)行時(shí),它會(huì)進(jìn)入阻塞狀態(tài),直到某個(gè)條件滿足后才能繼續(xù)執(zhí)行,線程阻塞可以通過(guò)多種方式實(shí)現(xiàn),如等待鎖、等待IO操作、等待其他線程的完成等,需要的朋友可以參考下
    2023-10-10
  • IDEA中 Getter、Setter 注解不起作用的問(wèn)題如何解決

    IDEA中 Getter、Setter 注解不起作用的問(wèn)題如何解決

    這篇文章主要介紹了IDEA中 Getter、Setter 注解不起作用的問(wèn)題如何解決,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 詳解Java中的hashcode

    詳解Java中的hashcode

    這篇文章主要介紹了詳解Java中的hashcode,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java的小伙伴們有非常好的幫助,需要的朋友可以參考下
    2021-05-05
  • Java實(shí)現(xiàn)文本查重的方法詳解

    Java實(shí)現(xiàn)文本查重的方法詳解

    Ansj 是一個(gè)開(kāi)源的 Java 中文分詞工具,基于中科院的 ictclas 中文分詞算法,采用隱馬爾科夫模型(HMM),比其他常用的開(kāi)源分詞工具(如 MMseg4j)的分詞準(zhǔn)確率更高,下面我們就來(lái)使用它實(shí)現(xiàn)文本查重功能吧
    2024-04-04
  • 詳解Java中如何正確書(shū)寫(xiě)單例模式

    詳解Java中如何正確書(shū)寫(xiě)單例模式

    一般單例都是五種寫(xiě)法:懶漢,餓漢,雙重校驗(yàn)鎖,靜態(tài)內(nèi)部類(lèi)和枚舉。本文整理了幾種常見(jiàn)的單例寫(xiě)法,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • MyBatis使用resultMap如何解決列名和屬性名不一致

    MyBatis使用resultMap如何解決列名和屬性名不一致

    這篇文章主要介紹了MyBatis使用resultMap如何解決列名和屬性名不一致的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • chatgpt java環(huán)境調(diào)用源碼實(shí)現(xiàn)demo

    chatgpt java環(huán)境調(diào)用源碼實(shí)現(xiàn)demo

    這篇文章主要介紹了chatgpt java環(huán)境調(diào)用源碼實(shí)現(xiàn)demo,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-02-02
  • redis redisson 限流器的實(shí)例(RRateLimiter)

    redis redisson 限流器的實(shí)例(RRateLimiter)

    這篇文章主要介紹了redis redisson 限流器的實(shí)例(RRateLimiter),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-07-07

最新評(píng)論