一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務(wù)器之家 - 編程語言 - Java教程 - 詳談java線程與線程、進(jìn)程與進(jìn)程間通信

詳談java線程與線程、進(jìn)程與進(jìn)程間通信

2020-09-08 13:56Java技術(shù)網(wǎng) Java教程

下面小編就為大家?guī)硪黄斦刯ava線程與線程、進(jìn)程與進(jìn)程間通信。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧

線程與線程間通信

一、基本概念以及線程與進(jìn)程之間的區(qū)別聯(lián)系:

關(guān)于進(jìn)程和線程,首先從定義上理解就有所不同

1、進(jìn)程是什么?

是具有一定獨(dú)立功能的程序、它是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位,重點(diǎn)在系統(tǒng)調(diào)度和單獨(dú)的單位,也就是說進(jìn)程是可以獨(dú) 立運(yùn)行的一段程序。

2、線程又是什么?

線程進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,他是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位,線程自己基本上不擁有系統(tǒng)資源。

在運(yùn)行時(shí),只是暫用一些計(jì)數(shù)器、寄存器和棧 。

他們之間的關(guān)系

1、一個(gè)線程只能屬于一個(gè)進(jìn)程,而一個(gè)進(jìn)程可以有多個(gè)線程,但至少有一個(gè)線程(通常說的主線程)。

2、資源分配給進(jìn)程,同一進(jìn)程的所有線程共享該進(jìn)程的所有資源。

3、線程在執(zhí)行過程中,需要協(xié)作同步。不同進(jìn)程的線程間要利用消息通信的辦法實(shí)現(xiàn)同步。

4、處理機(jī)分給線程,即真正在處理機(jī)上運(yùn)行的是線程。

5、線程是指進(jìn)程內(nèi)的一個(gè)執(zhí)行單元,也是進(jìn)程內(nèi)的可調(diào)度實(shí)體。

從三個(gè)角度來剖析二者之間的區(qū)別

1、調(diào)度:線程作為調(diào)度和分配的基本單位,進(jìn)程作為擁有資源的基本單位。

2、并發(fā)性:不僅進(jìn)程之間可以并發(fā)執(zhí)行,同一個(gè)進(jìn)程的多個(gè)線程之間也可以并發(fā)執(zhí)行。

3、擁有資源:進(jìn)程是擁有資源的一個(gè)獨(dú)立單位,線程不擁有系統(tǒng)資源,但可以訪問隸屬于進(jìn)程的資源。.

二、多線程間通信方式:

1、共享變量

2、wait/notify機(jī)制

3、Lock/Condition機(jī)制

4、管道

三、共享變量

線程間發(fā)送信號(hào)的一個(gè)簡單方式是在共享對(duì)象的變量里設(shè)置信號(hào)值。線程A在一個(gè)同步塊里設(shè)置boolean型成員變量hasDataToProcess為true,線程B也在同步塊里讀取hasDataToProcess這個(gè)成員變量。這個(gè)簡單的例子使用了一個(gè)持有信號(hào)的對(duì)象,并提供了set和check方法:

 

java" id="highlighter_353867">
?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MySignal{
 
 protected boolean hasDataToProcess = false;
 
 public synchronized boolean hasDataToProcess(){
  return this.hasDataToProcess;
 }
 
 public synchronized void setHasDataToProcess(boolean hasData){
  this.hasDataToProcess = hasData;
 }
 
}

線程A和B必須獲得指向一個(gè)MySignal共享實(shí)例的引用,以便進(jìn)行通信。如果它們持有的引用指向不同的MySingal實(shí)例,那么彼此將不能檢測(cè)到對(duì)方的信號(hào)。需要處理的數(shù)據(jù)可以存放在一個(gè)共享緩存區(qū)里,它和MySignal實(shí)例是分開存放的。

四、wait()/notify機(jī)制

為了實(shí)現(xiàn)線程通信,我們可以使用Object類提供的wait()、notify()、notifyAll()三個(gè)方法。調(diào)用wait()方法會(huì)釋放對(duì)該同步監(jiān)視器的鎖定。這三個(gè)方法必須由同步監(jiān)視器對(duì)象來調(diào)用,這可分成兩種情況:

•對(duì)于使用synchronized修飾的同步方法,因?yàn)樵擃惖哪J(rèn)實(shí)例是(this)就是同步監(jiān)視器,所以可以直接調(diào)用這三使用個(gè)方法。

•對(duì)于synchronized修飾的同步代碼塊,同步監(jiān)視器是synchronized括號(hào)里的對(duì)象,所以必須使用該對(duì)象調(diào)用這三個(gè)方法。

假設(shè)系統(tǒng)中有兩條線程,這兩條線程分別代表取錢者和存錢者。現(xiàn)在系統(tǒng)有一種特殊的要求,系統(tǒng)要求存款者和取錢者不斷的實(shí)現(xiàn)存款和取錢動(dòng)作,而且要求每當(dāng)存款者將錢存入指定賬戶后,取錢者立即將錢取走.不允許存款者兩次存錢,也不允許取錢者兩次取錢。

我們通過設(shè)置一個(gè)旗標(biāo)來標(biāo)識(shí)賬戶中是否已有存款,有就為true,沒有就標(biāo)為false。具體代碼如下:

首先我們定義一個(gè)Account類,這個(gè)類中有取錢和存錢的兩個(gè)方法,由于這兩個(gè)方法可能需要并發(fā)的執(zhí)行取錢、存錢操作,所有將這兩個(gè)方法都修改為同步方法.(使用synchronized關(guān)鍵字)。

?
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
public class Account {
  private String accountNo;
  private double balance;
  //標(biāo)識(shí)賬戶中是否有存款的旗標(biāo)
  private boolean flag=false;
   
  public Account() {
    super();
  }
 
  public Account(String accountNo, double balance) {
    super();
    this.accountNo = accountNo;
    this.balance = balance;
  
   
  public synchronized void draw (double drawAmount){
     
    try {
       if(!flag){
       this.wait();
       }else {
         //取錢
         System.out.println(Thread.currentThread().getName()+" 取錢:"+drawAmount);
         balance=balance-drawAmount;
         System.out.println("余額 : "+balance);
         //將標(biāo)識(shí)賬戶是否已有存款的標(biāo)志設(shè)為false
         flag=false;
         //喚醒其它線程
         this.notifyAll();    
       }
      } catch (Exception e) {
        e.printStackTrace();
    }
  }
   
   
  public synchronized void deposit(double depositAmount){
   try {
       if(flag){
        this.wait();
       }
       else{
         System.out.println(Thread.currentThread().getName()+"存錢"+depositAmount);
         balance=balance+depositAmount;
         System.out.println("賬戶余額為:"+balance);
         flag=true;
         //喚醒其它線程
         this.notifyAll();
       }
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
    }
  }
 
}

接下來創(chuàng)建兩個(gè)線程類,分別為取錢和存錢線程!

取錢線程類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DrawThread implements Runnable {
 
  private Account account;
  private double drawAmount;
   
   
  public DrawThread(Account account, double drawAmount) {
    super();
    this.account = account;
    this.drawAmount = drawAmount;
  }
 
  public void run() {
    for(int i=0;i<100;i++){
     account.draw(drawAmount);  
    }
  }
}

存錢線程類:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class depositThread implements Runnable{
  private Account account;
  private double depositAmount;
    
  public depositThread(Account account, double depositAmount) {
    super();
    this.account = account;
    this.depositAmount = depositAmount;
  }
 
 
  public void run() {
  for(int i=0;i<100;i++){
     account.deposit(depositAmount);
   }
  }
 
}

最后我們測(cè)試一下這個(gè)取錢和存錢的操作

?
1
2
3
4
5
6
7
8
9
10
11
12
13
public class TestDraw {
 
  public static void main(String[] args) {
    //創(chuàng)建一個(gè)賬戶
    Account account=new Account();
    new Thread(new DrawThread(account, 800),"取錢者").start();
    new Thread(new depositThread(account, 800),"存款者甲").start();
    new Thread(new depositThread(account, 800),"存款者乙").start();
    new Thread(new depositThread(account, 800),"存款者丙").start();
 
  }
 
}

大致的輸出結(jié)果:

?
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
存款者甲存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者丙存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者甲存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者丙存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者甲存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者丙存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者甲存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者丙存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0
存款者甲存錢800.0
賬戶余額為:800.0
取錢者 取錢:800.0
余額 : 0.0

五、Lock/Condition機(jī)制

如何程序不使用synchronized關(guān)鍵字來保持同步,而是直接適用Lock對(duì)像來保持同步,則系統(tǒng)中不存在隱式的同步監(jiān)視器對(duì)象,也就不能使用wait()、notify()、notifyAll()來協(xié)調(diào)線程的運(yùn)行.

當(dāng)使用LOCK對(duì)象保持同步時(shí),JAVA為我們提供了Condition類來協(xié)調(diào)線程的運(yùn)行。關(guān)于Condition類,JDK文檔里進(jìn)行了詳細(xì)的解釋.,再次就不啰嗦了。

我們就拿Account類進(jìn)行稍微的修改 一下吧!

?
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
 
public class Account {
   
  //顯示定義Lock對(duì)象
  private final Lock lock=new ReentrantLock();
  //獲得指定Lock對(duì)象對(duì)應(yīng)的條件變量
  private final Condition con=lock.newCondition();  
 
  private String accountNo;
  private double balance;
  //標(biāo)識(shí)賬戶中是否有存款的旗標(biāo)
  private boolean flag=false;
   
  public Account() {
    super();
  }
 
  public Account(String accountNo, double balance) {
    super();
    this.accountNo = accountNo;
    this.balance = balance;
  
   
  public void draw (double drawAmount){
     
    //加鎖
    lock.lock();
    try {
       if(!flag){
//      this.wait();
       con.await();
       }else {
         //取錢
         System.out.println(Thread.currentThread().getName()+" 取錢:"+drawAmount);
         balance=balance-drawAmount;
         System.out.println("余額 : "+balance);
         //將標(biāo)識(shí)賬戶是否已有存款的標(biāo)志設(shè)為false
         flag=false;
         //喚醒其它線程
//        this.notifyAll(); 
         con.signalAll();
       }
      } catch (Exception e) {
        e.printStackTrace();
    }
      finally{
        lock.unlock();
      }
  }
   
   
  public void deposit(double depositAmount){
    //加鎖
    lock.lock();
    try {
       if(flag){
//       this.wait();
         con.await();
       }
       else{
         System.out.println(Thread.currentThread().getName()+"存錢"+depositAmount);
         balance=balance+depositAmount;
         System.out.println("賬戶余額為:"+balance);
         flag=true;
         //喚醒其它線程
//        this.notifyAll();
         con.signalAll();
       }
    } catch (Exception e) {
      // TODO: handle exception
      e.printStackTrace();
    }finally{
      lock.unlock();
    }
  }
 
}

輸出結(jié)果和上面是一樣的! 只不過這里 顯示的使用Lock對(duì)像來充當(dāng)同步監(jiān)視器,使用Condition對(duì)象來暫停指定線程,喚醒指定線程!

六、管道

管道流是JAVA中線程通訊的常用方式之一,基本流程如下:

1)創(chuàng)建管道輸出流PipedOutputStream pos和管道輸入流PipedInputStream pis

2)將pos和pis匹配,pos.connect(pis);

3)將pos賦給信息輸入線程,pis賦給信息獲取線程,就可以實(shí)現(xiàn)線程間的通訊了

?
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
 
public class testPipeConnection {
 
  public static void main(String[] args) {
    /**
     * 創(chuàng)建管道輸出流
     */
    PipedOutputStream pos = new PipedOutputStream();
    /**
     * 創(chuàng)建管道輸入流
     */
    PipedInputStream pis = new PipedInputStream();
    try {
      /**
       * 將管道輸入流與輸出流連接 此過程也可通過重載的構(gòu)造函數(shù)來實(shí)現(xiàn)
       */
      pos.connect(pis);
    } catch (IOException e) {
      e.printStackTrace();
    }
    /**
     * 創(chuàng)建生產(chǎn)者線程
     */
    Producer p = new Producer(pos);
    /**
     * 創(chuàng)建消費(fèi)者線程
     */
    Consumer1 c1 = new Consumer1(pis);
    /**
     * 啟動(dòng)線程
     */
    p.start();
    c1.start();
  }
}
 
/**
 * 生產(chǎn)者線程(與一個(gè)管道輸入流相關(guān)聯(lián))
 *
 */
class Producer extends Thread {
  private PipedOutputStream pos;
 
  public Producer(PipedOutputStream pos) {
    this.pos = pos;
  }
 
  public void run() {
    int i = 0;
    try {
      while(true)
      {
      this.sleep(3000);
      pos.write(i);
      i++;
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
 
/**
 * 消費(fèi)者線程(與一個(gè)管道輸入流相關(guān)聯(lián))
 *
 */
class Consumer1 extends Thread {
  private PipedInputStream pis;
 
  public Consumer1(PipedInputStream pis) {
    this.pis = pis;
  }
 
  public void run() {
    try {
      while(true)
      {
      System.out.println("consumer1:"+pis.read());
      }
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

程序啟動(dòng)后,就可以看到producer線程往consumer1線程發(fā)送數(shù)據(jù)

?
1
2
3
4
5
consumer1:0
consumer1:1
consumer1:2
consumer1:3
......

管道流雖然使用起來方便,但是也有一些缺點(diǎn)

1)管道流只能在兩個(gè)線程之間傳遞數(shù)據(jù)

線程consumer1和consumer2同時(shí)從pis中read數(shù)據(jù),當(dāng)線程producer往管道流中寫入一段數(shù)據(jù)后,每一個(gè)時(shí)刻只有一個(gè)線程能獲取到數(shù)據(jù),并不是兩個(gè)線程都能獲取到producer發(fā)送來的數(shù)據(jù),因此一個(gè)管道流只能用于兩個(gè)線程間的通訊。不僅僅是管道流,其他IO方式都是一對(duì)一傳輸。

2)管道流只能實(shí)現(xiàn)單向發(fā)送,如果要兩個(gè)線程之間互通訊,則需要兩個(gè)管道流

可以看到上面的例子中,線程producer通過管道流向線程consumer發(fā)送數(shù)據(jù),如果線程consumer想給線程producer發(fā)送數(shù)據(jù),則需要新建另一個(gè)管道流pos1和pis1,將pos1賦給consumer1,將pis1賦給producer,具體例子本文不再多說。

II.進(jìn)程與進(jìn)程間通信

一、進(jìn)程間通信方式

(1)管道(Pipe):管道可用于具有親緣關(guān)系進(jìn)程間的通信,允許一個(gè)進(jìn)程和另一個(gè)與它有共同祖先的進(jìn)程之間進(jìn)行通信。

(2)命名管道(named pipe):命名管道克服了管道沒有名字的限制,因此,除具有管道所具有的功能外,它還允許無親緣關(guān) 系 進(jìn)程間的通信。命名管道在文件系統(tǒng)中有對(duì)應(yīng)的文件名。命名管道通過命令mkfifo或系統(tǒng)調(diào)用mkfifo來創(chuàng)建。

(3)信號(hào)(Signal):信號(hào)是比較復(fù)雜的通信方式,用于通知接受進(jìn)程有某種事件發(fā)生,除了用于進(jìn)程間通信外,進(jìn)程還可以發(fā)送 信號(hào)給進(jìn)程本身;linux除了支持Unix早期信號(hào)語義函數(shù)sigal外,還支持語義符合Posix.1標(biāo)準(zhǔn)的信號(hào)函數(shù)sigaction(實(shí)際上,該函數(shù)是基于BSD的,BSD為了實(shí)現(xiàn)可靠信號(hào)機(jī)制,又能夠統(tǒng)一對(duì)外接口,用sigaction函數(shù)重新實(shí)現(xiàn)了signal函數(shù))。

(4)消息(Message)隊(duì)列:消息隊(duì)列是消息的鏈接表,包括Posix消息隊(duì)列system V消息隊(duì)列。有足夠權(quán)限的進(jìn)程可以向隊(duì)列中添加消息,被賦予讀權(quán)限的進(jìn)程則可以讀走隊(duì)列中的消息。消息隊(duì)列克服了信號(hào)承載信息量少,管道只能承載無格式字節(jié)流以及緩沖區(qū)大小受限等缺

(5)共享內(nèi)存:使得多個(gè)進(jìn)程可以訪問同一塊內(nèi)存空間,是最快的可用IPC形式。是針對(duì)其他通信機(jī)制運(yùn)行效率較低而設(shè)計(jì)的。往往與其它通信機(jī)制,如信號(hào)量結(jié)合使用,來達(dá)到進(jìn)程間的同步及互斥。

(6)內(nèi)存映射(mapped memory):內(nèi)存映射允許任何多個(gè)進(jìn)程間通信,每一個(gè)使用該機(jī)制的進(jìn)程通過把一個(gè)共享的文件映射到自己的進(jìn)程地址空間來實(shí)現(xiàn)它。

(7)信號(hào)量(semaphore):主要作為進(jìn)程間以及同一進(jìn)程不同線程之間的同步手段。

(8)套接口(Socket):更為一般的進(jìn)程間通信機(jī)制,可用于不同機(jī)器之間的進(jìn)程間通信。起初是由Unix系統(tǒng)的BSD分支開發(fā)出來的,但現(xiàn)在一般可以移植到其它類Unix系統(tǒng)上:Linux和System V的變種都支持套接字。

以上這篇詳談java線程與線程、進(jìn)程與進(jìn)程間通信就是小編分享給大家的全部內(nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持服務(wù)器之家。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产成人精品一区二三区2022 | 女仆色网址 | 亚洲经典激情春色另类 | 我们中文在线观看免费完整版 | v视界影院.vsjla | 香蕉精品国产高清自在自线 | 国产日产国无高清码2020 | 九哥草逼网 | 亚洲嫩模吧粉嫩粉嫩冒白浆 | 果冻传媒在线视频观看免费 | a性片| 无人区乱码1区2区3区网站 | 咪咪爱小说 | 欧美国产在线 | 经典千人斩一区二区视频 | 无遮18禁在线永久免费观看挡 | 青青草成人在线 | 日本三级欧美三级人妇英文 | 精品一区二区三区中文 | 91精品国产高清久久久久久91 | 美女的让男人桶爽网站 | 亚洲天堂精品在线 | caoporn草棚在线视频 | 亚洲国产在线视频中文字 | 亚洲va天堂va国产va久久 | 日韩精品欧美高清区 | ady成人映画网站官网 | 蜜臀91| 日产2021免费一二三四区 | 五月婷婷丁香在线视频 | 久久久影院亚洲精品 | 精品国产一区二区三区久久久蜜臀 | 果冻传媒天美传媒在线小视频播放 | 欧美综合另类 | 欧美日韩高清完整版在线观看免费 | 国内久久精品视频 | 欧美最猛性xxxxx69交 | 近亲乱中文字幕 | 好大好深好舒服 | 欧美日韩精 | www.国产一区二区三区 |