欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java線程間共享實(shí)現(xiàn)方法詳解

 更新時(shí)間:2019年10月25日 11:18:13   作者:ねぇ  
這篇文章主要介紹了Java線程間共享實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

一、synchronize對(duì)象鎖和類鎖

synchronize為多線程關(guān)鍵字是一種同步鎖,它可以修飾以下幾種對(duì)象:

代碼塊:被修飾的代碼塊被稱為同步代碼塊,作用的范圍是{}里面的代碼,作用的對(duì)象是調(diào)用這個(gè)代碼塊的對(duì)象

方法:被修飾的方法稱為同步方法,作用的范圍是整個(gè)方法,作用的對(duì)象是調(diào)用這個(gè)方法的對(duì)象

類:作用的范圍是synchronize后面括號(hào)里的部分,作用的對(duì)象是當(dāng)前這個(gè)類

1、對(duì)象鎖

下面由一個(gè)栗子引入:

public class TestSynchronize {
  //加了對(duì)象鎖的方法
  private synchronized void syn(){
    //自定義sleep工具類
    SleepTools.second(2);
    System.out.println("syn is going..."+this.toString());
    SleepTools.second(2);
    System.out.println("syn ended..."+this.toString());
  }

  //調(diào)用了對(duì)象鎖方法的線程1
  private static class thread implements Runnable{
    private TestSynchronize testSynchronize;
    public thread(TestSynchronize testSynchronize){
      this.testSynchronize = testSynchronize;
    }
    @Override
    public void run() {
      System.out.println("thread is running...");
      testSynchronize.syn();
    }
  }
  //調(diào)用了對(duì)象鎖方法的線程2
  private static class thread2 implements Runnable{
    private TestSynchronize testSynchronize;
    public thread2(TestSynchronize testSynchronize){
      this.testSynchronize = testSynchronize;
    }
    @Override
    public void run() {
      System.out.println("thread2 is running...");
      testSynchronize.syn();
    }
  }
  public static void main(String[] args) {
    TestSynchronize testSynchronize = new TestSynchronize();
    thread thread = new thread(testSynchronize);

    TestSynchronize testSynchronize2 = new TestSynchronize();
    thread2 thread2 = new thread2(testSynchronize);
    //thread2 thread2 = new thread2(testSynchronize2);
    new Thread(thread).start();
    new Thread(thread2).start();
  }
}

/**
當(dāng)兩個(gè)線程都將testSynchronize傳入時(shí)(即使用同一個(gè)對(duì)象調(diào)用加了對(duì)象鎖的方法)運(yùn)行結(jié)果如下:
thread is running...
thread2 is running...
syn is going...com.zl.synchronize.TestSynchronize@6b52350c
syn ended...com.zl.synchronize.TestSynchronize@6b52350c
syn is going...com.zl.synchronize.TestSynchronize@6b52350c
syn ended...com.zl.synchronize.TestSynchronize@6b52350c
*/

/**
當(dāng)一個(gè)傳入testSynchronize,另一個(gè)傳入testSynchronize2時(shí) 運(yùn)行結(jié)果如下:
thread is running...
thread2 is running...
syn is going...com.zl.synchronize.TestSynchronize@28835f5f
syn is going...com.zl.synchronize.TestSynchronize@47c48106
syn ended...com.zl.synchronize.TestSynchronize@28835f5f
syn ended...com.zl.synchronize.TestSynchronize@47c48106
*/

結(jié)論:多個(gè)線程調(diào)用同一個(gè)對(duì)象的同步方法會(huì)阻塞,調(diào)用不同對(duì)象的同步方法不會(huì)阻塞

2、類鎖

1) synchronized修飾的靜態(tài)方法

public static synchronized void obj3() {
  int i = 5;
  while (i-- > 0) {
    System.out.println(Thread.currentThread().getName() + " : " + i);
    try {
      Thread.sleep(500);
    } catch (InterruptedException ie) {
    }
  }
}

2) synchronized (test.class) ,鎖的對(duì)象是test.class,即test類的鎖。

public void obj1() {
  synchronized (test.class) {
    int i = 5;
    while (i-- > 0) {
      System.out.println(Thread.currentThread().getName() + " : " + i);
      try {
        Thread.sleep(500);
      } catch (InterruptedException ie) {
      }
    }
  }
}

那么問題來(lái)了:在一個(gè)類中有兩方法,分別用synchronized 修飾的靜態(tài)方法(類鎖)和非靜態(tài)方法(對(duì)象鎖)。多線程訪問兩個(gè)方法的時(shí)候,線程會(huì)不會(huì)阻塞?

答案是當(dāng)類鎖和對(duì)象鎖同時(shí)存在時(shí),多線程訪問時(shí)不會(huì)阻塞,因?yàn)樗麄儾皇且粋€(gè)鎖。

二、volatile

volatile 是一個(gè)類型修飾符。volatile 的作用是作為指令關(guān)鍵字,確保本條指令不會(huì)因編譯器的優(yōu)化而省略。

​ volatile的特性

  • 保證了不同線程對(duì)這個(gè)變量進(jìn)行操作時(shí)的可見性,即一個(gè)線程修改了某個(gè)變量的值,這新值對(duì)其他線程來(lái)說是立即可見的。(實(shí)現(xiàn)可見性)
  • 禁止進(jìn)行指令重排序。(實(shí)現(xiàn)有序性)
  • volatile 只能保證對(duì)單次讀/寫的原子性。i++ 這種操作不能保證原子性。

三、ThreadLocal

  • ThreadLocal從字面意思來(lái)理解,是一個(gè)線程本地變量,也可以叫線程本地變量存儲(chǔ)。有時(shí)候一個(gè)對(duì)象的變量會(huì)被多個(gè)線程所訪問,這個(gè)時(shí)候就會(huì)有線程安全問題,當(dāng)然可以使用synchronized關(guān)鍵字來(lái)為該變量加鎖,進(jìn)行同步處理來(lái)限制只能有一個(gè)線程來(lái)使用該變量,但是這樣會(huì)影響程序執(zhí)行的效率,這時(shí)ThreadLocal就派上了用場(chǎng);
  • 使用ThreadLocal維護(hù)變量的時(shí)候,會(huì)為每一個(gè)使用該變量的線程提供一個(gè)獨(dú)立的變量副本,即每個(gè)線程內(nèi)部都會(huì)有一個(gè)當(dāng)前變量。這樣同時(shí)有多個(gè)線程訪問該變量并不會(huì)相互影響,因?yàn)樗麄兌际鞘褂酶髯跃€程存儲(chǔ)的變量,所以不會(huì)存在線程安全的問題。
  • 同步機(jī)制采用了“以時(shí)間換空間”的方式,而ThreadLocal采用了“以空間換時(shí)間”的方式,前者僅提供一份變量,讓不同的線程排隊(duì)訪問,而后者為每一個(gè)線程都提供了一份變量,因此可以同時(shí)訪問且互不影響。

下面給出測(cè)試程序:

public class ThreadLocalDemo {
  private static ThreadLocal<Integer> number = new ThreadLocal<Integer>(){
    @Override
    protected Integer initialValue() {
      return 1;
    }
  };
  private static class thread extends Thread{
    @Override
    public void run() {
      Integer number = ThreadLocalDemo.number.get();
      for (int i = 0; i < this.getId(); i++) {
        number++;
      }
      System.out.println(this.getName()+"---"+this.getId()+"===="+number);
    }
  }
  private static class thread2 extends Thread{
    @Override
    public void run() {
      Integer number = ThreadLocalDemo.number.get();
      for (int i = 0; i < this.getId(); i++) {
        number++;
      }
      System.out.println(this.getName()+"---"+this.getId()+"===="+number);
    }
  }
  public static void main(String[] args) {
    new Thread(new thread()).start();
    new Thread(new thread2()).start();
  }
}
/**
Thread-0---12====13
Thread-2---14====15
*/

四、等待(Wait)和通知(notify)

為了支撐多線程之間的協(xié)作,JDK提供了兩個(gè)非常重要的線程接口:等待wait()方法和通知notify()方法。 這兩個(gè)方法并不是在Thread類中的,而是輸出在Object類。這意味著任何對(duì)象都可以調(diào)用這兩個(gè)方法。

等待/通知的經(jīng)典范式

wait()方法和notify()方法究竟是如何工作的呢?

  如果一個(gè)線程調(diào)用了object.wait()方法,那么它就會(huì)進(jìn)入object對(duì)象的等待隊(duì)列,這個(gè)隊(duì)列中,可能會(huì)有多個(gè)線程,因?yàn)橄到y(tǒng)運(yùn)行多個(gè)線程同時(shí)等待某一個(gè)對(duì)象,

  當(dāng)object.notify()方法被調(diào)用的時(shí)候,它就會(huì)從這個(gè)等待隊(duì)列中隨機(jī)選擇一個(gè)線程,并進(jìn)行喚醒。

  除notity()方法外,Object對(duì)象還有一個(gè)類似的notifyAll()方法,它和notity方法的功能基本一致,不同的是,它會(huì)喚醒在這個(gè)等待隊(duì)列中所有等待的線程,而不是隨機(jī)一個(gè)。

  object.wait()方法并不能隨便調(diào)用。它必須包含在對(duì)象的synchronzied語(yǔ)句中,無(wú)論是wait()方法或者notity()方法都需要首先獲得目標(biāo)對(duì)象的一個(gè)監(jiān)視器。

​ 假設(shè)有T1和T2表示兩個(gè)線程,T1在正確執(zhí)行wait()方法前,必須獲得object對(duì)象的監(jiān)視器,而wait()方法執(zhí)行之后會(huì)釋放這個(gè)監(jiān)視器。

這樣做的目的是使其他等待在object對(duì)象上的線程不至于因?yàn)門1的休眠而全部無(wú)法正常執(zhí)行。

  線程T2在notity()方法調(diào)用前,也必須獲得object對(duì)象的監(jiān)視器。此時(shí)T1已經(jīng)釋放了這個(gè)監(jiān)視器。所以T2可以順利獲得object對(duì)象的監(jiān)視器。

接著,T2執(zhí)行了notify()方法嘗試喚醒一個(gè)等待線程,這里假設(shè)喚醒了T1,T1被喚醒后,要做的第一件事并不是執(zhí)行后續(xù)代碼,而是要嘗試重新

獲得object對(duì)象的監(jiān)視器,而這個(gè)監(jiān)視器也正是T1在wait()方法執(zhí)行前所持有的那個(gè)。

如果暫時(shí)無(wú)法獲得,則T1還必須等待這個(gè)監(jiān)視器。當(dāng)監(jiān)視器順利獲得后,T1才可以在真正意義上繼續(xù)執(zhí)行。

這里要注意,只有當(dāng)wait()和notify()被包含的synchronized語(yǔ)句執(zhí)行完,才會(huì)釋放監(jiān)視器。

為了方便理解,簡(jiǎn)單的案例:

public class testWaitAndNotify {
  final static Object object = new Object();

  public static class T1 extends Thread {
    public void run() {
      synchronized (object) {
        try {
          System.out.println(System.currentTimeMillis() + ":T1 start! ");
          System.out.println(System.currentTimeMillis() + ":T1 wait for object");
          object.wait();
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(System.currentTimeMillis() + ":T1 end! ");
      }
    }
  }

  public static class T2 extends Thread {
    public void run() {
      synchronized (object) {
        try {
          System.out.println(System.currentTimeMillis() + ":T2 start! notify one thread");
          object.notify();
          sleep(5000);
          System.out.println(System.currentTimeMillis() + ":T2 end! ");
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  public static void main(String[] args) {
    Thread t1 = new T1();
    Thread t2 = new T2();
    t1.start();
    t2.start();
  }
}
/**
1571039516250:T1 start! 
1571039516250:T1 wait for object
1571039516251:T2 start! notify one thread
1571039521251:T2 end! 
1571039521251:T1 end! 
*/

五、等待超時(shí)模式

由于經(jīng)典的等待/通知范式無(wú)法做到超時(shí)等待,也就是說,當(dāng)消費(fèi)者在獲得鎖后,如果條件不滿足,等待生產(chǎn)者改變條件之前會(huì)一直處于等待狀態(tài),在一些實(shí)際應(yīng)用中,會(huì)浪費(fèi)資源,降低運(yùn)行效率。

偽代碼如下所示:

//假設(shè)超時(shí)時(shí)間是mills,則等待持續(xù)時(shí)間是remaining,超時(shí)時(shí)間是future
long future = System.currentTimeMillis() + mills;
long remaining = mills;
synchronized (lock) {
  while (!condition && remaining > 0) {
    wait(remaining);
    remaining = future - System.currentTimeMillis();
  }
  //處理代碼
}

六、join()

join在線程里面意味著“插隊(duì)”,哪個(gè)線程調(diào)用join代表哪個(gè)線程插隊(duì)先執(zhí)行——但是插誰(shuí)的隊(duì)是有講究了,不是說你可以插到隊(duì)頭去做第一個(gè)吃螃蟹的人,而是插到在當(dāng)前運(yùn)行線程的前面,比如系統(tǒng)目前運(yùn)行線程A,在線程A里面調(diào)用了線程B.join方法,則接下來(lái)線程B會(huì)搶先在線程A面前執(zhí)行,等到線程B全部執(zhí)行完后才繼續(xù)執(zhí)行線程A。

話不多說上代碼

public class TestJoin {
  private static class thread extends Thread{
    private Thread t;
    //接收一個(gè)插隊(duì)線程
    public thread(Thread t){
      this.t = t;
    }
    @Override
    public void run() {
      try {
        //調(diào)用插隊(duì)線程的join方法
        t.join();
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(getName()+"---執(zhí)行完畢!");
    }
  }
  public static void main(String[] args) throws InterruptedException {
    //獲取當(dāng)前線程作為前一個(gè)線程
    Thread pre = Thread.currentThread();
    //創(chuàng)建五個(gè)線程
    for (int i = 0; i < 5; i++) {
      Thread thread = new Thread(new thread(pre),String.valueOf(i));
      //啟動(dòng)線程
      thread.start();
      //重置前一個(gè)線程
      pre = thread;
    }
    System.out.println(System.currentTimeMillis());
    //讓主線程睡眠2s
    Thread.currentThread().sleep(2000);
    System.out.println(System.currentTimeMillis());
    System.out.println(Thread.currentThread().getName()+"---執(zhí)行完畢");
  }
}

/**
1571061168064
1571061170065
main---執(zhí)行完畢
Thread-0---執(zhí)行完畢!
Thread-1---執(zhí)行完畢!
Thread-2---執(zhí)行完畢!
Thread-3---執(zhí)行完畢!
Thread-4---執(zhí)行完畢!
*/

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java中TCP通信的實(shí)現(xiàn)方法詳解

    Java中TCP通信的實(shí)現(xiàn)方法詳解

    這篇文章主要給大家介紹了關(guān)于Java中TCP通信的實(shí)現(xiàn)方法,TCP通信能實(shí)現(xiàn)兩臺(tái)計(jì)算機(jī)之間的數(shù)據(jù)交互,通信的兩端,要嚴(yán)格區(qū)分為客戶端(Client)與服務(wù)端(Server),需要的朋友可以參考下
    2023-09-09
  • SpringBoot打包發(fā)布到linux上(centos 7)的步驟

    SpringBoot打包發(fā)布到linux上(centos 7)的步驟

    這篇文章主要介紹了SpringBoot打包發(fā)布到linux上(centos 7)的步驟,幫助大家更好的理解和使用springboot框架,感興趣的朋友可以了解下
    2020-12-12
  • Java使用自定義注解實(shí)現(xiàn)為事件源綁定事件監(jiān)聽器操作示例

    Java使用自定義注解實(shí)現(xiàn)為事件源綁定事件監(jiān)聽器操作示例

    這篇文章主要介紹了Java使用自定義注解實(shí)現(xiàn)為事件源綁定事件監(jiān)聽器操作,結(jié)合實(shí)例形式分析了java自定義注解、注解處理、事件監(jiān)聽與響應(yīng)等相關(guān)操作技巧,需要的朋友可以參考下
    2019-10-10
  • Java實(shí)現(xiàn)AC自動(dòng)機(jī)全文檢索示例

    Java實(shí)現(xiàn)AC自動(dòng)機(jī)全文檢索示例

    本篇文章主要介紹了Java實(shí)現(xiàn)AC自動(dòng)機(jī)全文檢索示例,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來(lái)看看吧
    2017-02-02
  • Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理

    Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理

    這篇文章主要介紹Java 負(fù)載均衡的 5 種算法實(shí)現(xiàn)原理,負(fù)載均衡能夠平均分配客戶請(qǐng)求到服 務(wù)器陣列,借此提供快速獲取重要數(shù)據(jù),解決大量并發(fā)訪問服務(wù)問題,這種集群技術(shù)可以用最少的投資獲得接近于大型主機(jī)的性能。下面就來(lái)看看文章的具體內(nèi)容吧
    2021-10-10
  • SpringBoot項(xiàng)目中遇到的BUG問題及解決方法

    SpringBoot項(xiàng)目中遇到的BUG問題及解決方法

    這篇文章主要介紹了SpringBoot項(xiàng)目中遇到的BUG問題及解決方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • springboot的四種啟動(dòng)方式

    springboot的四種啟動(dòng)方式

    本文主要介紹了springboot的四種啟動(dòng)方式,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-02-02
  • SpringBoot整合Docker實(shí)現(xiàn)一次構(gòu)建到處運(yùn)行的操作方法

    SpringBoot整合Docker實(shí)現(xiàn)一次構(gòu)建到處運(yùn)行的操作方法

    本文講解的是 SpringBoot 引入容器化技術(shù) Docker 實(shí)現(xiàn)一次構(gòu)建到處運(yùn)行,包括鏡像構(gòu)建、Docker倉(cāng)庫(kù)搭建使用、Docker倉(cāng)庫(kù)可視化UI等內(nèi)容,需要的朋友可以參考下
    2022-10-10
  • mybatis中數(shù)據(jù)加密與解密的實(shí)現(xiàn)

    mybatis中數(shù)據(jù)加密與解密的實(shí)現(xiàn)

    數(shù)據(jù)加解密的實(shí)現(xiàn)方式多種多樣,在mybatis環(huán)境中數(shù)據(jù)加解密變得非常簡(jiǎn)單易用,本文主要介紹了mybatis中數(shù)據(jù)加密與解密的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • 深入解析Java中的內(nèi)部類

    深入解析Java中的內(nèi)部類

    這篇文章主要介紹了Java中的內(nèi)部類,是Java入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2015-07-07

最新評(píng)論