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

深入講解java線程與synchronized關(guān)鍵字

 更新時(shí)間:2017年03月08日 09:26:58   作者:Walker_YAM  
Java 中多線程的同步依靠的是對(duì)象鎖機(jī)制,synchronized關(guān)鍵字就是利用了封裝對(duì)象鎖來實(shí)現(xiàn)對(duì)共享資源的互斥訪問。下面這篇文章主要介紹了java線程與synchronized關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下。

我們將會(huì)從以下的幾點(diǎn)理解java線程的一些概念:

  1. 線程的基本概念和優(yōu)劣之處
  2. 創(chuàng)建一個(gè)線程的兩種方式
  3. 線程的屬性
  4. 線程的狀態(tài)
  5. synchronized可修飾的方法
  6. synchronized的重要特性

一、線程的基本概念

在計(jì)算機(jī)中有進(jìn)程和線程這么兩個(gè)概念,進(jìn)程中可以有多個(gè)線程,它們是從屬關(guān)系,進(jìn)程往往更像是資源的占有者,線程才是程序的執(zhí)行者,多個(gè)線程之間共享著進(jìn)程中的資源。一個(gè)cpu同時(shí)只能運(yùn)行一個(gè)線程,每個(gè)線程都有一個(gè)時(shí)間片,時(shí)間片用完了就會(huì)被阻塞并讓出CPU的控制權(quán),交給下一個(gè)線程使用。這樣在計(jì)算機(jī)中就可以實(shí)現(xiàn)多任務(wù)的假象,其實(shí)CPU在不斷的切換線程,好像多個(gè)任務(wù)在同時(shí)運(yùn)行。

使用線程的優(yōu)勢(shì)毋庸置疑,可以增加CPU的執(zhí)行效率,一旦某個(gè)線程需要等待某種資源(例如:等待打印機(jī)),就可以將它阻塞釋放CPU讓CPU執(zhí)行別的線程,而不需要讓CPU和此線程一起等待某種資源從而提高系統(tǒng)效率,另外一點(diǎn)就是可以用這種假象增加用戶體驗(yàn)度。但是,CPU在切換不同線程之間所要花費(fèi)的代價(jià)也是不可忽視的,在較為復(fù)雜的程序中這種劣勢(shì)可能九流一毛,但是如果在簡(jiǎn)單的程序中就會(huì)顯得尤為突出。

二、創(chuàng)建一個(gè)線程

接下來我們看看如何在java中創(chuàng)建一個(gè)線程來實(shí)現(xiàn)多個(gè)線程同時(shí)運(yùn)行。第一種方式,java 中有一個(gè)類Thread,我們只要繼承這個(gè)類并重寫他的run方法,調(diào)用start方法就可以啟動(dòng)一個(gè)新的線程了。(沒見過的同學(xué)可能不能理解以下代碼,下面我會(huì)解釋)

 /*聲明自己的一個(gè)線程類*/
public class Test_thread extends Thread {
 //重寫Thread類中的run方法
 public void run(){
 System.out.println("i am the thread");
 }
}

public class Test_Class {
 public static void main(String[] args){
 Test_thread thread = new Test_thread();
 thread.start();
 }
}

輸出結(jié)果:i am the thread

首先我們先了解一下,一個(gè)線程被創(chuàng)建之后,怎么才能啟動(dòng)運(yùn)行,我們調(diào)用thread.start();方法啟動(dòng)一個(gè)線程,首先就會(huì)執(zhí)行我們重寫的run方法(如果沒有重寫就會(huì)調(diào)用Thread類的run方法,什么也不做,這也是我們重寫run方法的原因),也就是說run方法是一個(gè)線程的開始。有個(gè)疑問,為什么調(diào)用start方法,卻執(zhí)行了run方法了?其實(shí)調(diào)用start方法就是為線程的啟動(dòng)做準(zhǔn)備操作,分配線程私有的堆棧資源,然后執(zhí)行run方法。

下面我們看創(chuàng)建一個(gè)線程的第二種方式,實(shí)現(xiàn)接口Runnable,并重寫其中的run方法。

 public class Test_thread implements Runnable {
 public void run(){
  System.out.println("i am the thread");
 }
}

public class Test_Class {
 public static void main(String[] args){
  Test_thread thread = new Test_thread();
  thread.start();//編譯錯(cuò)誤
 }
}

我們會(huì)發(fā)現(xiàn)雖然重寫了run方法,但是在調(diào)用start方法的時(shí)候卻編譯錯(cuò)誤,我們進(jìn)入到Runnable接口的源代碼中可以看到,只有一個(gè)抽象方法run,所以沒有啟動(dòng)線程的start方法,所以我們還是要借助Tread類。

 public class Test_Class {
 public static void main(String[] args){
  Thread thread = new Thread(new Test_thread());
  thread.start();
 }
}

因?yàn)門hread類中有start方法,所以可以使用Thread的一個(gè)構(gòu)造函數(shù)傳入一個(gè)實(shí)現(xiàn)了Runnable接口的類型,構(gòu)建一個(gè)Thread類。

三、線程的屬性和狀態(tài)

在一個(gè)多線程的程序中我們使用線程的一些屬性來區(qū)別和辨認(rèn)它們:

private long tid;
private volatile char name[];
public static native Thread currentThread()
private boolean  daemon = false;
private volatile int threadStatus = 0;
public final static int NORM_PRIORITY = 5;

每個(gè)線程會(huì)有一個(gè)tid指定此線程的在所有線程中的排序,這個(gè)值是遞增的,還有一個(gè)name指定該線程的名字,也可以使用setName設(shè)置一個(gè)優(yōu)雅的線程名字,Thread類還提供了一個(gè)方法返回當(dāng)前正在占用CPU的線程對(duì)象。每個(gè)線程在某個(gè)時(shí)刻都是有狀態(tài)的,屬性threadStatus記錄了當(dāng)前對(duì)象線程的狀態(tài)是什么,默認(rèn)情況下,所有的線程的優(yōu)先級(jí)都被置為5,如果有特殊需要可以修改線程的優(yōu)先級(jí),使得某個(gè)線程可以優(yōu)先得到運(yùn)行。下面介紹線程的幾種不同的狀態(tài)。

  1. New:線程剛剛被創(chuàng)建,還沒有被啟動(dòng)。
  2. Runnable:線程處于可運(yùn)行的狀態(tài),但是不一定處于正在運(yùn)行的狀態(tài)(后面解釋區(qū)別)
  3. Blocked:線程處于被阻塞的狀態(tài),一般是請(qǐng)求某資源不成功于是讓出CPU阻塞自己(具體的區(qū)別后面說)
  4. Waiting:線程處于等待狀態(tài),一般是等待服務(wù)
  5. Terminated:線程終止,也就是線程被kill,釋放其占用的資源

下面具體的說說不同狀態(tài)下的線程的一些操作,首先看看new,線程從此被創(chuàng)建,只是離運(yùn)行狀態(tài)還需要一些準(zhǔn)備。Runnable表示線程是可運(yùn)行,至于是否已經(jīng)處于運(yùn)行狀態(tài)還要看系統(tǒng)給的時(shí)間片是否用完,如果用完了就會(huì)將此線程放置在可運(yùn)行線程隊(duì)列的尾部,等待下次分配時(shí)間片,如果時(shí)間片沒有用完,就是處于運(yùn)行狀態(tài)的(這也是為什么叫做Runnable而不是Running的原因)。接下來的兩種狀態(tài)Blocked和waiting都表示線程阻塞,需要讓出CPU。只是導(dǎo)致它們處于這種狀態(tài)的原因不一樣,具體的在我們介紹完synchronized關(guān)鍵字之后就會(huì)不言而喻了。

四、關(guān)鍵字synchronized

先看一段代碼:

 public class Test_thread extends Thread{
 public static int count = 0;
 public void run(){
  try {
   Thread.sleep((int) (Math.random() * 100));
  }catch(InterruptedException e){

  }
  count++;
 }
}

public class Test_Class {
 public static void main(String[] args){
  Test_thread[] thread = new Test_thread[1000];
  for(int a=0;a<1000;a++){
   thread[a] = new Test_thread();
   thread[a].start();
  }

  for(int a=0;a<1000;a++){
   try {
    thread[a].join();
   }catch (InterruptedException e){

   }
  }
  System.out.println(Test_thread.count);
 }
}

按照直覺,創(chuàng)建1000個(gè)線程,每個(gè)線程隨機(jī)睡覺并將公共變量增一,main線程等待所有線程執(zhí)行結(jié)束之后,輸出公共變量的值。按照直覺答案應(yīng)該是1000,但是我們運(yùn)行的結(jié)果每次都是不一樣的,接近1000但每次都不一樣。這是為什么呢?這其實(shí)就是簡(jiǎn)單的模擬了高并發(fā),可能有幾個(gè)線程睡了相同的時(shí)間,同時(shí)醒來獲取的count值是相同的,這就導(dǎo)致這幾個(gè)線程對(duì)count的操作被覆蓋了。

 public class Test_thread extends Thread{
 public static int count = 0;
 public void run(){
  try {
   Thread.sleep((int) (Math.random() * 100));
  }catch(InterruptedException e){

  }
  /*使用關(guān)鍵字*/
  synchronized (Test_thread.class){
   count++;
  }
 }
}

一個(gè)簡(jiǎn)單的關(guān)鍵字就可以輕松解決這樣的高并發(fā)的問題。至于為什么這么寫?在介紹完synchronized關(guān)鍵字之后,想必你就會(huì)知道了。

首先我們需要知道,每個(gè)對(duì)象都有一把內(nèi)部鎖。所以被synchronized關(guān)鍵字修飾的方法,其實(shí)是被加了內(nèi)部對(duì)象鎖。我們看代碼:

  public class Counter{
  private int count;

  /*為實(shí)例方法加此關(guān)鍵字*/
  public synchronized int getCount(){
   return count;
  }
 }

為實(shí)例方法添加關(guān)鍵字,實(shí)際上就是給當(dāng)前的對(duì)象加鎖;括號(hào)中就是要加鎖的對(duì)象。

  public class Counter{
  private int count;

  /*為實(shí)例方法加此關(guān)鍵字*/
  synchronized(this){
   return count;
  }
 }

對(duì)于靜態(tài)方法,實(shí)際上就是為這個(gè)類加上鎖;

  public class Counter{
  private static int count;

  public static synchronized int getCount(){
   return count;
  }
 }

/*實(shí)際上給這個(gè)類加上鎖*/
 public class Counter{
  private int count;

  synchronized(Counter.class){
   return count;
  }
 }

五、深入理解synchronized的一些特性

第一個(gè)性質(zhì)是:可重入性。被synchronized修飾的方法中的所有操作都是原子操作,但是當(dāng)我們需要在其中訪問另外的一些需要鎖的代碼時(shí)候,可以直接獲取別的鎖。也就是當(dāng)前的對(duì)象是可以獲得多個(gè)鎖的。

第二個(gè)性質(zhì)是:內(nèi)存的可見性。在我們的計(jì)算機(jī)中,其實(shí)有很多的操作并不是很"干脆"的,比如你向數(shù)據(jù)庫中存數(shù)據(jù)時(shí),其實(shí)很多時(shí)候一些待存入的數(shù)據(jù)積累在緩存中等到一定數(shù)據(jù)量的時(shí)候才會(huì)統(tǒng)一的存入數(shù)據(jù)庫,但是在我們看來這些數(shù)據(jù)其實(shí)應(yīng)該早就存入數(shù)據(jù)庫了。這就導(dǎo)致了一個(gè)內(nèi)存的不可見性問題。當(dāng)然我們也是可以使用關(guān)鍵字synchronized來保證每次取出的數(shù)據(jù)都是最新的。在釋放鎖的時(shí)候會(huì)立即將數(shù)據(jù)協(xié)會(huì)內(nèi)存,獲得鎖的時(shí)候會(huì)去讀取最新的內(nèi)容。

  public class Counter{
  private int count;

  public synchronized int getCount(){
   return count;
  }
 }
 /*每次獲得該對(duì)象的鎖之后,去獲取最新的count數(shù)值*/

其實(shí),如果僅僅是要保證內(nèi)存的不可見性,使用synchronized關(guān)鍵字可能代價(jià)有點(diǎn)高,在這種情況下,我們可以使用關(guān)鍵字volatile。

  public class Counter{
  /*使用關(guān)鍵字volatile*/
  private volatile int count;

  public int getCount(){
   return count;
  }
 }
 /*此關(guān)鍵字保證每次使用count的時(shí)候數(shù)據(jù)都是最新的*/

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,還是希望大家發(fā)現(xiàn)其中錯(cuò)誤直接指出,方便我糾正錯(cuò)誤,更新知識(shí)。java并發(fā)系列文章,希望大家多多關(guān)注。

相關(guān)文章

  • SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul

    SpringCloud超詳細(xì)講解微服務(wù)網(wǎng)關(guān)Zuul

    這篇文章主要介紹了SpringCloud Zuul微服務(wù)網(wǎng)關(guān),負(fù)載均衡,熔斷和限流,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • 一文搞懂Java設(shè)計(jì)模式之責(zé)任鏈模式

    一文搞懂Java設(shè)計(jì)模式之責(zé)任鏈模式

    這篇文章主要給大家介紹了關(guān)于Java設(shè)計(jì)模式之責(zé)任鏈模式的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Java實(shí)現(xiàn)并查集示例詳解

    Java實(shí)現(xiàn)并查集示例詳解

    這篇文章主要通過一個(gè)題目示例為大家詳細(xì)介紹Java如何實(shí)現(xiàn)并查集,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-12-12
  • Mybatis-plus批量去重插入ON DUPLICATE key update使用方式

    Mybatis-plus批量去重插入ON DUPLICATE key update使用方式

    這篇文章主要介紹了Mybatis-plus批量去重插入ON DUPLICATE key update使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • MyBatis-plus中的模糊查詢解讀

    MyBatis-plus中的模糊查詢解讀

    這篇文章主要介紹了MyBatis-plus中的模糊查詢解讀,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • Java?如何通過注解實(shí)現(xiàn)接口輸出時(shí)數(shù)據(jù)脫敏

    Java?如何通過注解實(shí)現(xiàn)接口輸出時(shí)數(shù)據(jù)脫敏

    這篇文章主要介紹了Java?如何通過注解實(shí)現(xiàn)接口輸出時(shí)數(shù)據(jù)脫敏,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java 壓縮和解壓縮Zip、Jar、Gzip文件實(shí)例代碼

    java 壓縮和解壓縮Zip、Jar、Gzip文件實(shí)例代碼

    本文主要介紹java壓縮和解壓縮Zip、Jar、Gzip文件的知識(shí),這里整理了相關(guān)資料,并附示例代碼有興趣的小伙伴可以參考下
    2016-09-09
  • Java簡(jiǎn)單實(shí)現(xiàn)約瑟夫環(huán)算法示例

    Java簡(jiǎn)單實(shí)現(xiàn)約瑟夫環(huán)算法示例

    這篇文章主要介紹了Java簡(jiǎn)單實(shí)現(xiàn)約瑟夫環(huán)算法,簡(jiǎn)單描述了約瑟夫環(huán)問題,并結(jié)合實(shí)例形式分析了Java實(shí)現(xiàn)約瑟夫環(huán)的具體操作技巧,需要的朋友可以參考下
    2017-09-09
  • Java多線程中的Phaser使用解析

    Java多線程中的Phaser使用解析

    這篇文章主要介紹了Java多線程中的Phaser使用解析,java多線程技術(shù)提供了Phaser工具類,Phaser表示“階段器”,用來解決控制多個(gè)線程分階段共同完成任務(wù)的情景問題,其作用相比CountDownLatch和CyclicBarrier更加靈活,需要的朋友可以參考下
    2023-11-11
  • Java Stopwatch類,性能與時(shí)間計(jì)時(shí)器案例詳解

    Java Stopwatch類,性能與時(shí)間計(jì)時(shí)器案例詳解

    這篇文章主要介紹了Java Stopwatch類,性能與時(shí)間計(jì)時(shí)器案例詳解,本篇文章通過簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09

最新評(píng)論