日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java并发编程之美-阅读记录1

發布時間:2024/9/15 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java并发编程之美-阅读记录1 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.1什么是線程?

  在理解線程之前先要明白什么是進程,因為線程是進程中的一個實體。(線程是不會獨立存在的)

  進程:是代碼在數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,線程則是進程中的一個執行路徑,一個進程中至少會有一個線程,進程中的多個線程共享進程的資源。

  線程:是cpu分配的基本單位。

  

  由上圖可看出,一個進程中會有多個線程,多個線程共享堆和方法區,但是每一個線程都會有自己的棧和程序計數器。

為什么要將棧和程序計數器設置為線程私有的呢?前邊說線程是cpu執行的基本單位,而cpu一般是使用時間片輪轉方式輪詢占用的,所以當當前線程cpu時間片使用完畢后,要讓出cpu,等待下一次輪到自己的時候在調用。 那問題就來了,線程是如何知道之前執行到哪了?程序計數器就是為了記錄之前讓出cpu時執行到的地址,等下次再次執行時就可以從程序計數器中獲取之前執行到的位置,繼續向下執行。(程序計數器是不會記錄native方法執行的地址的,它只記錄java代碼執行的地址),而棧則是存儲現場的局部變量,一遍之后再次使用

?

1.2線程的創建和運行

  三種方式:繼承Thread、實現Runnable接口、使用FuthreTask方式(實現Callable接口)。

package com.nxz.blog.otherTest;import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask;public class TestThread {static class MyThread1 extends Thread {@Overridepublic void run() {System.out.println("extend-Thread:"+this.getName());}}static class MyThread2 implements Runnable {@Overridepublic void run() {System.out.println("implements-Runnable:"+Thread.currentThread());}}static class MyThread3 implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("implements-Callable:"+Thread.currentThread());return "Callable接口";}}public static void main(String[] args) {MyThread1 t1 = new MyThread1();t1.start();Thread t2 = new Thread(new MyThread2());t2.start();FutureTask<String> futureTask = new FutureTask<>(new MyThread3());new Thread(futureTask).start();try {// 阻塞,等待執行完畢,并返回結果String res = futureTask.get();System.out.println(res);} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}System.out.println("main-Thread");} }

  上述代碼執行結果:

extend-Thread:Thread-0 implements-Runnable:Thread[Thread-1,5,main] implements-Callable:Thread[Thread-2,5,main] Callable接口 main-Thread

  創建線程后會調用start方法,代表當前線程已經處于就緒狀態(也就是說該線程已經獲取了除cpu資源外的其他資源,等待cpu執行時間片),當獲取了cpu資源后,該線程才處于運行狀態,一旦run方法(對于extent Thread來說)執行完畢,該線程就處于終止狀態。

  使用繼承的好處:在run方法內可以直接使用this來獲取當前線程(而另兩個的this則沒有getName等一些方法),而不需要Thread.currentThread()。

1.3線程通知和等待

  以下三個方法都是Object類的方法,為所有對象公用

  ①wait()

    當一個線程調用wait()方法后,該線程就會被阻塞掛起,知道被notify/notifyAll調用或者其他線程調用了intercept方法中斷了該線程

  ②wait(long timeout)

    表上邊一個多了一個超時時間,在等待期間如果沒有其他線程喚醒(notify/notifyAll)該線程,該線程仍然會在超過timeout時間后自動返回

  ③wait(long timeout, int nanos)? :兩個參數,毫秒timeout,納秒nanos,其內部還是調用的wait(long timeout)方法,只不多對于第二個參數納秒,如果其范圍是大于0,小于一百萬的話(1毫秒= 100 0000納秒),則將timeout加一秒

public final void wait(long timeout, int nanos) throws InterruptedException {if (timeout < 0) {throw new IllegalArgumentException("timeout value is negative");}if (nanos < 0 || nanos > 999999) {throw new IllegalArgumentException("nanosecond timeout value out of range");}if (nanos > 0) {timeout++;}wait(timeout);}

  ④notify()/notifyAll()

    喚醒在同一個共享變量上等待的線程,區別:一個喚醒一個,一個喚醒所有

  需要注意的:

  *虛假喚醒:

    一個線程可以從掛起狀態變為可以運行狀態(也就是被喚醒),即使該線程沒有被其他線程調用notify()、notifyAll()方法進行通知,或者被中斷,或者等待超時,這就是虛假喚醒

  (在java并發編程之美中這樣描述的,感覺不太清楚,找了其他資料有的是這樣描述的:在多核處理器下,其他線程的信號可能會喚醒多個線程(阻塞在同一個條件變量上的線程),結果就是,多個被喚醒的線程,前一個執行代碼邏輯后,另外的線程再次執行同樣的代碼后會出現問題)

    例子:在下邊代碼中分別為生產者和消費者,假如在一個生產者和一個消費者的情況下(也就是單線程),是沒有問題的,不存在虛假喚醒,假如在多線程環境下,當一個生產者完成之后,調用notifyAll方法后,會喚醒其他生產者和所有的消費者,當第一個消費者消費完成之后,在有消費者消費,此時就會出現問題(也就是出現了虛假喚醒),解決該問題的方法,就是將消費者和生產者中的條件判斷if(queue.size() == 1) 和if(queue.isEmpty()) 改為? ?while( queue.size() == 1) 和while( queue.isEmpty() ) ,即改為循環判斷,當被喚醒是,每一個線程都重新判斷是否符合條件

static Queue queue;static class ProductThread extends Thread {@Overridepublic void run() {synchronized (queue) {if (queue.size() == 1) {// 假設隊列最多存一個對象try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.add(new Object());queue.notifyAll();}}}static class ConsumerThread extends Thread {@Overridepublic void run() {synchronized (queue) {if (queue.isEmpty()) {try {queue.wait();} catch (InterruptedException e) {e.printStackTrace();}}queue.poll();queue.notifyAll();}}}

?

1.4等待線程執行完畢? ?的join方法

  當調用join方法(該方法屬于Thread類)時,就會等待線程執行完畢后,在繼續向下運行

  例子:

package com.nxz.blog.otherTest;public class TestThread002 {static class MyThread extends Thread{@Overridepublic void run() {System.out.println("myThread");}}public static void main(String[] args) throws InterruptedException {MyThread t1 = new MyThread();t1.setName("myThread");t1.start();// 如果注釋掉 下邊代碼的話,main方法的輸出應該會是 先main-thread 然后再試mythread,// 放開的話 main主線程就會等待t1線程執行完畢后,在向下運行t1.join();System.out.println("main-thread");} }

?

1.5sleep方法休眠

  Thread中的靜態方法sleep(),當一個線程調用了sleep方法后,會短暫的讓出cpu執行權(也就是不參與cpu的調度),但是線程擁有的鎖等資源是不釋放的,這一點和wait方法不同

1.6讓出cpu執行權yield方法

  Thread中的靜態方法yield(),當一個線程調用了Thread.yield()時,表名當前線程放棄cpu的使用權,yield之后,當前線程處于就緒狀態。線程調度器會從包括該線程的所有線程中隨機選出一個線程分配cpu執行時間片。

1.7中斷線程

  java中的線程中斷是一種線程間的協作模式,通過設置線程的中斷標志并不能直接中斷線程,而是被中斷的線程根據中斷標志自行處理。

  void interruupt() :中斷線程

  boolean isInterrupted() :檢測當前現車鞥是否中斷(調用isInterrupted()方法的實例的中斷標志)

  boolean interrupted(): 檢測當前線程是否被中斷(內部是獲取當前線程的中斷標志而不是調用interruupted()方法的實例的中斷標志)

?1.8線程的上下文切換

  在多線程編程中線程的個數一般都是大于cpu的個數的,而每個cpu在同一時刻只能被一個線程使用。為了讓用戶感覺實在同時執行,cpu資源的分配采用時間片輪詢的側率。

  上下文切換:就是一個線程使用完時間片之后,將cpu資源讓給其他線程使用,并且線程狀態轉變為就緒狀態。

1.9線程死鎖

  多個線程在執行過程中,因爭奪資源而造成相互等待的狀態,如沒有其他因素,線程會一直等待,而無法繼續運行。

  死鎖產生的條件:1、互斥 2、請求并持有資源 3、不可剝奪 4、環路等待

1.10守護線程和用戶線程

  守護線程:類似垃圾回收線程

  用戶線程:main函數啟動,就是一個簡單的用戶線程

  jvm停止的時機:在用戶線程全都執行完畢后,jvm就會停止(無論有沒有守護線程)

1.11ThreadLocal

  當創建了一個ThreadLocal變量之后,多個線程操作這個變量時,實際上操作的是自己本地內存里的變量,也就是說每個線程都會復制一份變量到自己的本地內存。

  THreadLocal是怎么和線程關聯起來的呢?

    ThreadLocal其實就是一個空殼,當調用ThreadLocal的set或get方法時,其內部是通過set方法把value放到調用線程的threadlocals變量中(ThreadLocal.threadLocalMap),也就是說本地變量不是存在ThreadLocal實例中,而是存入Thread中的threadlocals中。

public void set(T value) {Thread t = Thread.currentThread();// 當調用ThreadLocal的set方法時,其實是獲取的是當前線程Thread中的ThreadLocalMap變量,如果變量為null,則新建一個ThreadLocalMap,如果變量存在,則之間將當前線程和value存入定制的map中(ThreadLocalMap)ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}// 從源碼getMap中可以看出,其實獲取的是當前線程中的threadLocals變量ThreadLocalMap getMap(Thread t) {return t.threadLocals;}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);} public T get() {// ThreadLocal的get方法,同樣是獲取的是當前線程Thread中的threadLocals變量(也就是ThreadLocalMap類型的數據)Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}

?

  

?

    

總結

以上是生活随笔為你收集整理的java并发编程之美-阅读记录1的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。