1. 多線程的實現方式,通過繼承Thread類和通過實現Runnable接口的方式以及異同點。
2. 多線程的同步與互斥中synchronized的使用方法。
3. 多線程的通訊中的notify(),notifyAll(),及wait(),的使用方法,以及簡單的生成者和消費者的代碼實現。
下面來具體的講解Java中的多線程:
一:多線程的實現方式
通過繼承Threa類來實現多線程主要分為以下三步:
第一步:繼承 Thread,實現Thread類中的run()方法。
第二步:定義一個Thread子類的實例。
第三步:通過調用Thread類的start()方法來啟動線程。
下面是簡單的代碼實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
class myThread extends Thread{ int n= 100 ; public void run() { while ( true ) { if (n > 0 ) { System.out.println( ":" Thread.currentThread().getName() + "..." + n--); } else { break ; } } } } public class ThreadTest { public static void main(String[] args) { myThread mythread= new myThread(); mythread.setName( "子線程" ); mythread.start(); } } |
上面線程中用到的幾個方法:Thread.currentThraed().getName()方法得到當前線程的名字。mythread.setName(“子線程”);為mythread線程重命名為“子線程”。
通過實現Runnable 接口來實現多線程主要分為以下幾步:
第一步:實現Runnable接口中的run()方法。生成一個Runnable的實例。
第二步:定義一個Thread類,并且將上面的Runnable實例傳給Thread類的構造方法。
第三步:通過調用Thread類的start()方法來啟動線程。
下面是簡單的代碼實現:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
class myRunnable implements Runnable{ int n= 100 ; public void run() { while ( true ) { if (n > 0 ) { System.out.println( ":" Thread.currentThread().getName() + "..." + n--); } else { break ; } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable = new myRunnable(); Thread mythread= new Thread(myrunnable); mythread.setName( "子線程" ); mythread.start(); } } |
既然通過繼承Thread類,和實現Runnable方法都能實現多線程的運行那么兩種方式到底有什么不同呢?下面通過一個買票的類子來看看到底二者有什么不同:
假設售票處總共有100張票,通過三個窗口來進行售票也就是說我們要開辟三個不同的線程來實現買票:首先看看通過Thread類來實現買票的方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
class myThread extends Thread{ int n= 100 ; public void run() { while ( true ) { if (n > 0 ) { System.out.println( ":" Thread.currentThread().getName() + "..." + n--); } else { break ; } } } } public class ThreadTest { public static void main(String[] args) { myThread m1= new myThread(); myThread m2= new myThread(); myThread m3= new myThread(); m1.setName( "窗口1" ); m2.setName( "窗口2" ); m3.setName( "窗口3" ); m1.start(); m2.start(); m3.start(); } } |
結果太長了我不展示了,可以看到原本三個窗口共同買100張票的,但是結果每個窗口都買了100張票,這個很好理解因為每個窗口都是一個獨立的對象,都有自己的n=100。所以通過Thread類來實現買票功能是不可行的,其實用Thread方法來實現多線程,其中每個線程執行的代碼并不是同一段代碼。
下面來看看實現Runnable接口是怎樣實現買票功能的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
class myRunnable implements Runnable{ int n= 100 ; public void run() { while ( true ) { if (n > 0 ) { System.out.println( ":" Thread.currentThread().getName() + "..." + n--); } else { break ; } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable= new myRunnable(); Thread m1= new Thread(myrunnable); Thread m2= new Thread(myrunnable); Thread m3= new Thread(myrunnable); m1.setName( "窗口1" ); m2.setName( "窗口2" ); m3.setName( "窗口3" ); m1.start(); m2.start(); m3.start(); } } |
可以看出上面三個線程公用的是同一個Runnable的子類,所以只是開辟三條線程來執行同一段runnable的代碼。所以不會出現買300張票的情況。但是這個程序還是有問題的當講到了后面的線程的同步與互斥后我們再來完善這段程序。
二:多線程的同步與互斥中synchronized和volatile的使用方法。
現在截取一段上面代碼執行過程中出現的問題并且分析一下問題是如何產生的,再來通過synchronied來解決此問題。
:窗口2…1
:窗口1…1
:窗口3…1
:窗口1…2
:窗口2…2
:窗口1…3
:窗口3…2
:窗口1…4
:窗口2…3
:窗口1…5
:窗口3…3
:窗口1…6
:窗口2…4
上面代碼的結果是通過實現Runnable接口產生的,上面窗口1,窗口2,窗口3,同時產生1是怎么產生的呢?
1
2
3
4
5
6
7
8
9
10
|
int n= 100 ; public void run() { while ( true ) { if (n > 0 ) { System.out.println( ":" Thread.currentThread().getName() + "..." + n--); } else { break ; } } |
這是三個線程共同執行的同一段代碼,上面結果產生的一種原因可能是當窗口2執行完輸出i=1時此時虛擬機執行了窗口2,窗口2執行輸出I,此時的i還未執行++,所以i的值還是1,此時虛擬機又把執行權給了窗口3,這個時候窗口3輸出的i仍然是1,程序產生上面問題的主要原因是存在公共變量i,i的值在程序執行的過程中未保持同步。上面的for循環體應該單獨執行完之后才能讓其他的線程搶占虛擬機。Synchronized關鍵字就是用來實現保證線程在執行一段公共資源是不被其他線程搶占。
被synchronized修飾的代碼塊稱作同步代碼塊,被synchronized修飾的方法稱為同步方法。下面通過加入synchronized關鍵字來實現買票功能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
class myRunnable implements Runnable { int n = 100 ; public void run() { while ( true ) { synchronized ( this ) { if (n > 0 ) { if (n % 10 == 0 ) { try { Thread.currentThread().sleep( 10 ); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println( ":" + Thread.currentThread().getName() + "..." + n--); } else { break ; } } } } } public class ThreadTest { public static void main(String[] args) { myRunnable myrunnable = new myRunnable(); Thread m1 = new Thread(myrunnable); Thread m2 = new Thread(myrunnable); Thread m3 = new Thread(myrunnable); m1.setName( "窗口1" ); m2.setName( "窗口2" ); m3.setName( "窗口3" ); m1.start(); m2.start(); m3.start(); } } |
此時是可以正確的完成售票功能的。
上面代碼中synchronized(this)中的this代表的是當前的對象,因為三個線程執行的都是myRunnable 的對象,所以三個線程公用的是同一個鎖,其實這個this可以用任何的對象來代替,一般我們可以 String str=new String(“”);雖然str的值為空字符串,但是也是一個對象。Synchronized實現互斥的原理是每一個對象都有一個特定的變量值,當任何一個線程調用了synchronized想要進入公共資源區時,先判斷該變量的值,若該變量的值為0則可以進入公共資源區,進程在進入公共資源區之前先把對象的中的該變量值變為1,出同步區后再將該變量的值變為0,從而實現線程互斥訪問公共資源。
三:多線程的通訊中的notify(),notifyAll(),及wait(),的使用方法,以及簡單的生成者和消費者的代碼實現。
在講解notify(),notifyAll(),wait()之前,先看看生產者和消費者問題:生產者生產面包,消費者消費面包,但是存放面包的容器有限,生產者一次最多只能生產20個面包,消費者每次在容器中拿一個面包。通過分析可以知道,當生產者生產了20個面包之后必須停下來,等容器里的面包數目小于20個時再繼續生產,消費者看到容器里面面包個數為0時也必須停下來,等到有面包時才能消費。這時候就涉及到了生產者和消費者的通信。notify()是用于喚醒等待隊列中的線程,wait()用于阻塞當前線程。Notify和wait()都必須用于synchronized修飾的同步代碼塊或同步方法中。
下面直接看生產者消費者代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
|
class Consumer implements Runnable { Clerk clerk; Consumer(Clerk clerk) { this .clerk = clerk; } public void run() { while ( true ) clerk.consumeProduct(); } } class Producter implements Runnable { Clerk clerk; Producter(Clerk clerk) { this .clerk = clerk; } public void run() { while ( true ) clerk.addProduct(); } } class Clerk { int product ; public synchronized void consumeProduct() { while ( true ) { if (product <= 0 ) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { product--; notifyAll(); System.out.println( "消費者消費了:" + product); } } } public synchronized void addProduct() { if (product > 20 ) { try { wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { product++; notifyAll(); System.out.println( "生產者生產了:" + product); } } } public class Test { public static void main(String[] args) { Clerk clerk= new Clerk(); Consumer consumer= new Consumer(clerk); Thread c= new Thread(consumer); Producter producter= new Producter(clerk); Thread p= new Thread(producter); c.start(); p.start(); } } |
以上這篇淺談Java多線程實現及同步互斥通訊就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持服務器之家。