『壹』 java多線程詳細理解
多線程:指的是這個程序(一個進程)運行時產生了不止一個線程
並行與並發:
並行:多個cpu實例或者多台機器同時執行一段處理邏輯,是真正的同時。
並發:通過cpu調度演算法,讓用戶看上去同時執行,實際上從cpu操作層面不是真正的同時。並發往往在場景中有公用的資源,那麼針對這個公用的資源往往產生瓶頸,我們會用TPS或者QPS來反應這個系統的處理能力。
線程安全:經常用來描繪一段代碼。指在並發的情況之下,該代碼經過多線程使用,線程的調度順序不影響任何結果。這個時候使用多線程,我們只需要關注系統的內存,cpu是不是夠用即可。反過來,線程不安全就意味著線程的調度順序會影響最終結果,如不加事務的轉賬代碼:
同步:Java中的同步指的是通過人為的控制和調度,保證共享資源的多線程訪問成為線程安全,來保證結果的准確。如上面的代碼簡單加入@synchronized關鍵字。在保證結果准確的同時,提高性能,才是優秀的程序。線程安全的優先順序高於性能。
『貳』 如何解決java 多線程問題
Java線程同步需要我們不斷的進行相關知識的學習,下面我們就來看看如何才能更好的在學習中掌握相關的知識訊息,來完善我們自身的編寫手段。希望大家有所收獲。 Java線程同步的優先順序代表該線程的重要程度,當有多個線程同時處於可執行狀態並等待獲得 CPU 時間時,線程調度系統根據各個線程的優先順序來決定給誰分配 CPU 時間,優先順序高的線程有更大的機會獲得 CPU 時間,優先順序低的線程也不是沒有機會,只是機會要小一些罷了。 你可以調用 Thread 類的方法 getPriority()和 setPriority()來存取Java線程同步的優先順序,線程的優先順序界於1(MIN_PRIORITY)和10(MAX_PRIORITY)之間,預設是5(NORM_PRIORITY)。 Java線程同步 由於同一進程的多個線程共享同一片存儲空間,在帶來方便的同時,也帶來了訪問沖突這個嚴重的問題。Java語言提供了專門機制以解決這種沖突,有效避免了同一個數據對象被多個線程同時訪問。 由於我們可以通過 private 關鍵字來保證數據對象只能被方法訪問,所以我們只需針對方法提出一套機制,這套機制就是 synchronized 關鍵字,它包括兩種用法:synchronized 方法和 synchronized 塊。 1. synchronized 方法:通過在方法聲明中加入 synchronized關鍵字來聲明 synchronized 方法。如:1. public synchronized void accessVal(int newVal); synchronized 方法控制對類成員變數的訪問:每個類實例對應一把鎖,每個 synchronized 方法都必須獲得調用該方法的類實例的鎖方能執行,否則所屬線程阻塞,方法一旦執行,就獨占該鎖,直到從該方法返回時才將鎖釋放,此後被阻塞的Java線程同步方能獲得該鎖,重新進入可執行狀態。 這種機制確保了同一時刻對於每一個類實例,其所有聲明為 synchronized 的成員函數中至多隻有一個處於可執行狀態(因為至多隻有一個能夠獲得該類實例對應的鎖),從而有效避免了類成員變數的訪問沖突(只要所有可能訪問類成員變數的方法均被聲明為 synchronized)。 在 Java 中,不光是類實例,每一個類也對應一把鎖,這樣我們也可將類的靜態成員函數聲明為 synchronized ,以控制其對類的靜態成員變數的訪問。 synchronized 方法的缺陷:若將一個大的方法聲明為synchronized 將會大大影響效率,典型地,若將線程類的方法 run()聲明為 synchronized ,由於在線程的整個生命期內它一直在運行,因此將導致它對本類任何 synchronized 方法的調用都永遠不會成功。當然我們可以通過將訪問類成員變數的代碼放到專門的方法中,將其聲明為 synchronized ,並在主方法中調用來解決這一問題,但是 Java 為我們提供了更好的解決辦法,那就是 synchronized 塊。 2. synchronized 塊:通過 synchronized關鍵字來聲明synchronized 塊。語法如下:1. synchronized(syncObject)2. {3. //允許訪問控制的代碼4. } synchronized 塊是這樣一個代碼塊,其中的代碼必須獲得對象 syncObject (如前所述,可以是類實例或類)的鎖方能執行,具體機制同前所述。由於可以針對任意代碼塊,且可任意指定上鎖的對象,故靈活性較高。 Java線程同步的阻塞 為了解決對共享存儲區的訪問沖突,Java 引入了同步機制,現在讓我們來考察多個Java線程同步對共享資源的訪問,顯然同步機制已經不夠了,因為在任意時刻所要求的資源不一定已經准備好了被訪問,反過來,同一時刻准備好了的資源也可能不止一個。為了解決這種情況下的訪問控制問題,Java 引入了對阻塞機制的支持。 阻塞指的是暫停一個Java線程同步的執行以等待某個條件發生(如某資源就緒),學過操作系統的同學對它一定已經很熟悉了。Java 提供了大量方法來支持阻塞,下面讓我們逐一分析。
『叄』 java 多線程是什麼
進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所佔用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。
線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。
Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心後台的多任務系統。
Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個Java程序啟動後,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。
『肆』 線程的調度分為幾種模型,在java中是使用哪種調度模型
Java程序屬於搶占式調度,哪個線程的優先順序高,哪個線程搶到的CPU時間片的概率就高;如果兩個線程同一個優先順序,則CPU隨機選擇一個執行。
『伍』 java的多線程是OS調度還是JVM調度的呢
現在java線程和操作系統線程之間的對應關系有三種:
多對一、一對一、多對多
多對一就是所說的「Green thread」,一個java應用程序
被當作一個任務被操作系統調度,而這個java應用程序里
的多個線程則由虛擬機調度執行。也可以說由虛擬機選出
一個多線程java程序里的一個線程作為活動線程,這個線
程再作為操作系統的一個任務被操作系統調度。
一對一就是一個java線程對應一個操作系統線程了,即同
一個多線程java程序里的所有線程都由操作系統統一調度。
多對多還不是很明白。。。
『陸』 Java的多線程和CPU
CPU對於各個線程的調度是隨機的(分時調度),而在Java中,JVM負責線程的調度,可更好地分配CPU的使用權。對於線程的調度一般有兩種模式,分時調度和搶占式調度。分時調度是按照順序平均分配;搶占調度是按照優先順序來進行分配。
『柒』 如何深刻理解Java多線程
線程是系統調度中的最小單位,因為其擁有比進程更小的資源消耗,因此,在進行同類事情,需要進行互相的通訊等等事情的時候,都採用線程來進行處理。
對於只做固定的一件事情(比如:計算1+2+3+...+9999999)來說,其性能上不會比採用單線程的整體效率高,原因是,同時都是要做這么多運算,採用多線程的話,系統在進行線程調度的過程中喙浪費一些資源和時間,從而性能上下降。
那麼,多線程是否就沒有存在的意義了呢?答案當然不是的。多線程還是有存在的價值的,我們在寫輸入流輸出流,寫網路程序等等的時候,都會出現阻塞的情況,如果說,我們不使用多線程的話,從A中讀數據出來的時候,A因為沒有準備好,而整個程序阻塞了,其他的任何事情都沒法進行。如果採用多線程的話,你就不用擔心這個問題了。還舉個例子:游戲中,如果A角色和B角色採用同一個線程來處理的話,那麼,很有可能就會出現只會響應A角色的操作,而B角色就始終被佔用了的情況,這樣,玩起來肯定就沒勁了。
因此,線程是有用的,但也不是隨便亂用,亂用的話,可能造成性能的低下,它是有一點的適用范圍的,一般我認為:需要響應多個人的事情,從設計上需要考慮同時做一些事情(這些事情很多情況下可能一點關系都沒有,也有可能有一些關系的)。
使用多線程的時候,如果某些線程之間涉及到資源共享、互相通訊等等問題的時候,一定得注意線程安全的問題,根據情況看是不是需要使用synchronized關鍵字。
『捌』 在Java 中多線程的實現方法有哪些,如何使用
Java多線程的創建及啟動
Java中線程的創建常見有如三種基本形式
1.繼承Thread類,重寫該類的run()方法。
復制代碼
1 class MyThread extends Thread {
2
3 private int i = 0;
4
5 @Override
6 public void run() {
7 for (i = 0; i < 100; i++) {
8 System.out.println(Thread.currentThread().getName() + " " + i);
9 }
10 }
11 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Thread myThread1 = new MyThread(); // 創建一個新的線程 myThread1 此線程進入新建狀態
8 Thread myThread2 = new MyThread(); // 創建一個新的線程 myThread2 此線程進入新建狀態
9 myThread1.start(); // 調用start()方法使得線程進入就緒狀態
10 myThread2.start(); // 調用start()方法使得線程進入就緒狀態
11 }
12 }
13 }
14 }
復制代碼
如上所示,繼承Thread類,通過重寫run()方法定義了一個新的線程類MyThread,其中run()方法的方法體代表了線程需要完成的任務,稱之為線程執行體。當創建此線程類對象時一個新的線程得以創建,並進入到線程新建狀態。通過調用線程對象引用的start()方法,使得該線程進入到就緒狀態,此時此線程並不一定會馬上得以執行,這取決於CPU調度時機。
2.實現Runnable介面,並重寫該介面的run()方法,該run()方法同樣是線程執行體,創建Runnable實現類的實例,並以此實例作為Thread類的target來創建Thread對象,該Thread對象才是真正的線程對象。
復制代碼
1 class MyRunnable implements Runnable {
2 private int i = 0;
3
4 @Override
5 public void run() {
6 for (i = 0; i < 100; i++) {
7 System.out.println(Thread.currentThread().getName() + " " + i);
8 }
9 }
10 }
復制代碼
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable(); // 創建一個Runnable實現類的對象
8 Thread thread1 = new Thread(myRunnable); // 將myRunnable作為Thread target創建新的線程
9 Thread thread2 = new Thread(myRunnable);
10 thread1.start(); // 調用start()方法使得線程進入就緒狀態
11 thread2.start();
12 }
13 }
14 }
15 }
復制代碼
相信以上兩種創建新線程的方式大家都很熟悉了,那麼Thread和Runnable之間到底是什麼關系呢?我們首先來看一下下面這個例子。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4 for (int i = 0; i < 100; i++) {
5 System.out.println(Thread.currentThread().getName() + " " + i);
6 if (i == 30) {
7 Runnable myRunnable = new MyRunnable();
8 Thread thread = new MyThread(myRunnable);
9 thread.start();
10 }
11 }
12 }
13 }
14
15 class MyRunnable implements Runnable {
16 private int i = 0;
17
18 @Override
19 public void run() {
20 System.out.println("in MyRunnable run");
21 for (i = 0; i < 100; i++) {
22 System.out.println(Thread.currentThread().getName() + " " + i);
23 }
24 }
25 }
26
27 class MyThread extends Thread {
28
29 private int i = 0;
30
31 public MyThread(Runnable runnable){
32 super(runnable);
33 }
34
35 @Override
36 public void run() {
37 System.out.println("in MyThread run");
38 for (i = 0; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 }
41 }
42 }
復制代碼
同樣的,與實現Runnable介面創建線程方式相似,不同的地方在於
1 Thread thread = new MyThread(myRunnable);
那麼這種方式可以順利創建出一個新的線程么?答案是肯定的。至於此時的線程執行體到底是MyRunnable介面中的run()方法還是MyThread類中的run()方法呢?通過輸出我們知道線程執行體是MyThread類中的run()方法。其實原因很簡單,因為Thread類本身也是實現了Runnable介面,而run()方法最先是在Runnable介面中定義的方法。
1 public interface Runnable {
2
3 public abstract void run();
4
5 }
我們看一下Thread類中對Runnable介面中run()方法的實現:
復制代碼
@Override
public void run() {
if (target != null) {
target.run();
}
}
復制代碼
也就是說,當執行到Thread類中的run()方法時,會首先判斷target是否存在,存在則執行target中的run()方法,也就是實現了Runnable介面並重寫了run()方法的類中的run()方法。但是上述給到的列子中,由於多態的存在,根本就沒有執行到Thread類中的run()方法,而是直接先執行了運行時類型即MyThread類中的run()方法。
3.使用Callable和Future介面創建線程。具體是創建Callable介面的實現類,並實現clall()方法。並使用FutureTask類來包裝Callable實現類的對象,且以此FutureTask對象作為Thread對象的target來創建線程。
看著好像有點復雜,直接來看一個例子就清晰了。
復制代碼
1 public class ThreadTest {
2
3 public static void main(String[] args) {
4
5 Callable<Integer> myCallable = new MyCallable(); // 創建MyCallable對象
6 FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); //使用FutureTask來包裝MyCallable對象
7
8 for (int i = 0; i < 100; i++) {
9 System.out.println(Thread.currentThread().getName() + " " + i);
10 if (i == 30) {
11 Thread thread = new Thread(ft); //FutureTask對象作為Thread對象的target創建新的線程
12 thread.start(); //線程進入到就緒狀態
13 }
14 }
15
16 System.out.println("主線程for循環執行完畢..");
17
18 try {
19 int sum = ft.get(); //取得新創建的新線程中的call()方法返回的結果
20 System.out.println("sum = " + sum);
21 } catch (InterruptedException e) {
22 e.printStackTrace();
23 } catch (ExecutionException e) {
24 e.printStackTrace();
25 }
26
27 }
28 }
29
30
31 class MyCallable implements Callable<Integer> {
32 private int i = 0;
33
34 // 與run()方法不同的是,call()方法具有返回值
35 @Override
36 public Integer call() {
37 int sum = 0;
38 for (; i < 100; i++) {
39 System.out.println(Thread.currentThread().getName() + " " + i);
40 sum += i;
41 }
42 return sum;
43 }
44
45 }
復制代碼
首先,我們發現,在實現Callable介面中,此時不再是run()方法了,而是call()方法,此call()方法作為線程執行體,同時還具有返回值!在創建新的線程時,是通過FutureTask來包裝MyCallable對象,同時作為了Thread對象的target。那麼看下FutureTask類的定義:
1 public class FutureTask<V> implements RunnableFuture<V> {
2
3 //....
4
5 }
1 public interface RunnableFuture<V> extends Runnable, Future<V> {
2
3 void run();
4
5 }
於是,我們發現FutureTask類實際上是同時實現了Runnable和Future介面,由此才使得其具有Future和Runnable雙重特性。通過Runnable特性,可以作為Thread對象的target,而Future特性,使得其可以取得新創建線程中的call()方法的返回值。
執行下此程序,我們發現sum = 4950永遠都是最後輸出的。而「主線程for循環執行完畢..」則很可能是在子線程循環中間輸出。由CPU的線程調度機制,我們知道,「主線程for循環執行完畢..」的輸出時機是沒有任何問題的,那麼為什麼sum =4950會永遠最後輸出呢?
原因在於通過ft.get()方法獲取子線程call()方法的返回值時,當子線程此方法還未執行完畢,ft.get()方法會一直阻塞,直到call()方法執行完畢才能取到返回值。
上述主要講解了三種常見的線程創建方式,對於線程的啟動而言,都是調用線程對象的start()方法,需要特別注意的是:不能對同一線程對象兩次調用start()方法。
你好,本題已解答,如果滿意
請點右下角「採納答案」。
『玖』 Java中多線程調度如何實現
多線程有兩種實現方法,分別是繼承Thread類與實現Runnable介面
同步的實現方面有兩種,分別是synchronized,wait與notify