91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java 中怎么利用Thread實現讀寫同步

發布時間:2021-08-07 15:51:26 來源:億速云 閱讀:149 作者:Leah 欄目:編程語言

Java 中怎么利用Thread實現讀寫同步,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

1.讀寫者同步問題

多個讀者可以同時讀取同一個緩沖區,但當有寫者對緩沖區進行寫操作時,具有排他性質,其他的讀者都不能讀取這個緩沖區,其他的寫者也不能寫這個緩沖區。

2.代碼構成和思路

這個例子中包括四個類。

(1)讀寫資源類RWResource,包含了讀寫者的計數,共享資源,還有所有的同步代碼。

(2)讀者類RunnableReader。實現Runnable接口;進行讀操作。

(3)寫者類RunnableWriter。實現Runnable接口;進行寫操作。

(4)測試類TestMain。生成并運行多個寫線程和讀線程,顯示結果。

這個例子對共享資源進行“盒式封裝”,把共享資源包含在一個“盒”內。并把所有的同步代碼都集中在“盒”里面。讀者類和寫者類并不進行同步處理,只是申請資源,然后進行讀寫,讀寫完成之后,釋放資源。

這種方法的優點是共享資源“盒”部分的代碼直觀易讀,緊湊可控,讀者類和寫者類不用關心同步問題。缺點是共享資源“盒”規定了嚴格的調用順序和調用規范,讀者類和寫者類必須嚴格遵守共享資源“盒”的調用規范,否則會造成線程死鎖,或者資源操作沖突。

不過,即使由讀者類和寫者類來實現線程同步,如果不注意,也會造成線程死鎖,或者資源操作沖突。這是線程的固有問題。:-)

下面給出這四個類的源代碼和說明。

3.讀寫資源類RWResource

package thread;

import java.util.List;

import java.util.ArrayList;

/**

 * resource for reading and writing

 */

public class RWResource {

  /**

  * When readerNumber == 0, there is no one reading or writing.

  * When readerNumber > 0, readerNumber means number of readers.

  * When readerNumber < 0, it means that some writer is writing.

  */

  private int readerNumber = 0;

  /**

  * the shared resource for writing or reading

  */

  private List buffer = null;

  public RWResource() {

  buffer = new ArrayList(512);

  readerNumber = 0;

  }

  /**

  * get buffer for reading.

  * should be called before reading

  * @return the buffer

  */

  public synchronized List getBufferForReading(){

  // if some writer is writing, wait until no writer is writing

  while(readerNumber < 0){

  try{

  this.wait();

  }catch(InterruptedException e){

  e.printStackTrace();

  }

  }

  // when readerNumber >= 0

  readerNumber++;

  return buffer;

  }

  /**

  * should be called after reading

  */

  public synchronized void finishReading(){

  readerNumber--;

  if(readerNumber == 0){

  this.notifyAll(); // notify possible waiting writers

  }

  }

  /**

  * get buffer for writing.

  * should be called before writing.

  * @return the buffer

  */

  public synchronized List getBufferForWriting(){

  // if some writer is writing or some reader is reading, wait until no one is writing or reading

   while(readerNumber != 0){

  try{

  this.wait();

  }catch(InterruptedException e){

  e.printStackTrace();

  }

   }

  // when readerNumber == 0

  readerNumber--; // now readderNumber == -1.

  return buffer;

  }

  /**

  * should be called after writing

  */

  public synchronized void finishWriting(){

  readerNumber++; // readerNumber = -1 + 1 = 0;

  // readerNumber must be 0 at this point

  this.notifyAll(); // notify possible waiting writers or waiting readers

  }

}

讀寫資源類RWResource提供了4個Synchronized方法,分成兩組,供讀者和寫者調用。

閱讀上面的代碼,可以看到,讀寫資源類RWResource通過readerNumber計數控制對共享資源的讀寫訪問。當readerNumber等于0時,說明資源空閑,可以讀寫;當readerNumber大于0時,說明資源正在被一些讀者讀取,其他線程可以讀,不可以寫;當readerNumber小于0時(-1),說明資源被某個寫者占用,正在寫入,其他線程不可以讀,也不可以寫。

讀者首先調用getBufferForReading()獲取共享資源,如果readerNumber大于等于0,表示沒有寫者占用資源,讀者能夠獲取共享資源,此時,readerNumber加1,表示讀者的個數增加;讀取之后,必須調用finishReading()釋放資源,此時,readerNumber減1,表示讀者的個數減少。

寫者首先調用getBufferForWriting()獲取共享資源,如果readerNumber等于0,表示資源空閑,寫者能夠獲取到共享資源,此時,readerNumber減1,readerNumber的值變為-1,表示資源正在被寫入;寫者寫完資源之后,必須調用,必須調用finishWriting()釋放資源,此時,readerNumber加1,readerNumber的值變為0,回到空閑狀態。

另外,還請留意讀寫資源類RWResource代碼里面的wait()和notifyAll()調用。

讀者在readerNumber小于0的情況下等待,調用Wait();寫者在readerNumber大于0的情況下等待,調用Wait()。

在釋放資源時( finishReading()或finishWriting() ),如果readerNumber的值變為0,回到空閑狀態,調用notifyAll(),通知潛在的等待者——讀者或寫者。

4.讀者類RunnableReader

package thread;

import java.util.List;

import java.util.Iterator;

public class RunnableReader implements Runnable{

  private RWResource resource = null;

  public RunnableReader() {

  }

  /**

  * must be called before start running

  * @param theResource

  */

  public void setRWResource(RWResource theResource){

  resource = theResource;

  }

  public void run(){

  while(true){

  // get the reader's name

  String readerName = "[" + Thread.currentThread().getName() + "] ";

  // first, get buffer for reading

  List buffer = resource.getBufferForReading();

  // reading

  for(Iterator iterator = buffer.iterator(); iterator.hasNext(); ){

  System.out.println(readerName + iterator.next());

  }

  int articleNumber = buffer.size();

  int thinkingTime = articleNumber * 1000;

  for(int i = 0; i < thinkingTime; i++){

  // thingking hard when reading

  }

  // finish reading

  resource.finishReading();

  // rest

  try{

  Thread.sleep(articleNumber * 50);

  }catch(InterruptedException e){

  e.printStackTrace();

  }

  }

  }

}

上述代碼中的setRWResource()方法傳入共享資源——讀寫資源類。本例采用參數傳遞的方法,在讀者和寫者之間共享讀寫資源類。

Run()方法實現Runnable接口的Run()方法。首先,獲取當前讀者(線程)的名稱,然后,試圖獲取讀資源——resource.getBufferForReading(),獲取資源之后,讀取buffer里面的所有文章,邊讀邊思考(注意代碼里面的for(int i = 0; i < thinkingTime; i++)行,占用cpu時間,表示思考過程),最后,釋放資源——resource.finishReading()。讀完文章,讀者休息一段時間——Thread.sleep(articleNumber * 50)。

注意,在以上的過程中,一定要嚴格遵守這樣的規定,在resource.getBufferForReading()和resource.finishReading()之間,進行讀取操作。

5.寫者類RunnableWriter

package thread;

import java.util.List;

import java.util.Iterator;

public class RunnableWriter implements Runnable{

  private RWResource resource = null;

  private int articleNumber = 0;

  public RunnableWriter() {

  articleNumber = 0;

  }

  /**

  * must be called before start running

  * @param theResource

  */

  public void setRWResource(RWResource theResource){

  resource = theResource;

  }

  public void run(){

  while(true){

  // get the writer's name

  String writerName = "[" + Thread.currentThread().getName() + "] ";

  // first, get buffer for reading

  List buffer = resource.getBufferForWriting();

  int nWritten = 3; // write 4 articles one time

  for(int n = 0; n< nWritten; n++){

  // writing

  articleNumber++;

  String articleName = "article" + articleNumber;

  buffer.add(articleName);

  System.out.println(writerName + articleName);

  int thinkingTime = 10000;

  for (int i = 0; i < thinkingTime; i++) {

  // thingking hard when writing

  }

  }  // finish writing

  resource.finishWriting();

  // rest

  try{

  Thread.sleep(500);

  }catch(InterruptedException e){

  e.printStackTrace();

  }

  }

  }

}

上述代碼中的setRWResource()方法傳入共享資源——讀寫資源類。本例采用參數傳遞的方法,在讀者和寫者之間共享讀寫資源類。

Run()方法實現Runnable接口的Run()方法。首先,獲取當前寫者(線程)的名稱,然后,試圖獲取寫資源——resource.getBufferForWriting(),獲取資源之后,開始向buffer里面寫3篇文章,邊寫邊思考(注意代碼里面的for(int i = 0; i < thinkingTime; i++)行,占用cpu時間,表示思考過程),最后,釋放資源——resource.finishWriting()。讀完文章,寫者休息一段時間——Thread.sleep(500)。

注意,在以上的過程中,一定要嚴格遵守這樣的規定,在resource.getBufferForWriting()和resource.finishWriting()之間,進行寫操作。

6.測試類TestMain

package thread;

public class TestMain{

  public static void main(String[] args) {

  // init 生成共享資源

  RWResource resource = new RWResource();

  // 生成讀者,設置共享資源

  RunnableReader reader = new RunnableReader();

  reader.setRWResource(resource);

  // 生成寫者,設置共享資源。

  RunnableWriter writer = new RunnableWriter();

  writer.setRWResource(resource);

  int writerNumber = 5;  // 5個寫者

  int readerNumber = 10; // 10個讀者

  // start writers 生成5個寫線程,并給每個線程起個名字,writer1, writer2…

  for(int i = 0; i < writerNumber; i++){

  Thread thread = new Thread(writer, "writer" + (i+1));

  thread.start();

  }

  // give writers enough time to think and write articles

  try{

  Thread.sleep(1000);

  }catch(InterruptedException e){

  e.printStackTrace();

  }

  // start readers生成10個讀線程,并給每個線程起個名字,reader1, reader2…

  for(int i = 0; i < readerNumber; i++){

  Thread thread = new Thread(reader, "reader" + (i+1));

  thread.start();

  }

  }

}

以上的測試類TestMain代碼,生成并運行多個寫線程和讀線程,產生的結果可能如下:

[writer1] article1

[writer1] article2

[writer1] article3

[reader2] article1

[reader3] article1

[reader4] article1

[reader5] article1

[reader6] article1…

[reader1] article1

[writer3] article67

[writer3] article68

[writer3] article69

我們可以看到,Writer寫的文章的號碼從不相同,而且,每個Writer每次寫3篇文章,寫的過程從來不會被打斷。每個讀者每次通讀所有的文章,經常有幾個讀者同時讀同一篇文章的情況。

6.兩種改進思路

這個例子采用“盒”包裝的方法,把“鎖”(readerNumber)和資源(buffer)放在同一個“盒子”(RWResource)里,但是,“盒子”對資源的包裝是不完全的,只是簡單地把資源(buffer)返回給讀者和寫者,并沒有對資源(buffer)的訪問操作進行封裝。

其實,可以對資源(buffer)的訪問進行進一步封裝,比如,為“盒子”提供String[] readerBuffer()和writeBuffer(String)兩個方法,在這兩個方法里面,根據鎖(readerNumber)的狀態,判斷讀寫操作是否合法,這樣,代碼的整體性會更好。帶來的結果是,RWResource類的調用規范和順序更加嚴格,必須在resource.getBufferForReading()和resource.finishReading()調用readerBuffer()方法,必須在resource.getBufferForWriting()和resource.finishWriting()之間調用writeBuffer()方法。否則,這兩個方法會報錯。

這樣做會增加RWResource類的復雜度。還有一些設計上的因素需要考慮——readerBuffer和writeBuffer方法是否應該被synchronized修飾?

從上例看到,在resource.getBufferForReading()和resource.finishReading()之間,進行讀操作;在resource.getBufferForWriting()和resource.finishWriting()之間,進行寫操作。讀操作和寫操作部分的代碼,是不用同步的。所以,在getBufferForReading()和finishReading()這樣的成對操作之間,用synchronized修飾readerBuffer和writeBuffer方法,是多此一舉。

但是,從代碼的完整性角度來看,因為readerBuffer和writeBuffer方法需要讀 “鎖”的狀態,所以,readerBuffer和writeBuffer方法還是加上synchronized修飾符為好。

考慮到這些因素,本例采取了一種折衷的方法。從形式上看,“鎖”和“資源”是聚合在一起的,實際上,兩者的操作是分開的,并不相關。“盒子”根據“鎖”的狀態,調整資源的分配,讀者和寫者得到資源之后,享有對資源的完全訪問權限。

另一個方向是把“資源”和“鎖”完全分開。把getBufferForReading方法改成startReading,把getBufferForWriting方法改成startWriting,RWResource不再分配資源,只進行“鎖”操作。把RWResource該作RWLock類。

RunnableReader和RunnableWriter類各自增加一個setBuffer()方法,共享buffer資源。這樣,RunnableReader和RunnableWriter類就有了兩個分開的方法:setBuffer()設置共享資源,setRWLock()設置讀寫鎖。

對本例稍加修改,就可以實現上述的兩種思路。限于篇幅,這里不能給出完整的修改代碼。

7.補充

本例中對讀寫資源類RWResource強加了調用順序。

在resource.getBufferForReading()和resource.finishReading()之間,進行讀操作。

在resource.getBufferForWriting()和resource.finishWriting()之間,進行寫操作。

要求在執行一些處理之前,一定要執行某項特殊操作,處理之后一定也要執行某項特殊操作。這種人為的順序性,無疑增加了代碼的耦合度,降低了代碼的獨立性。很有可能會成為線程死鎖和資源操作沖突的根源。

這點一直讓我不安,可是沒有找到方法避免。畢竟,死鎖或者資源操作沖突,是線程的固有問題。

很巧的是,正在我惴惴不安的時候,我的一個朋友提供了一個信息。Sun公司根據JCR,決定在jdk1.5中引入關于concurrency(并發)的部分。

以下這個網址是concurrency部分的util.concurrent一個實現。非常好的信息。對于處理多線程并發問題,很有幫助。

http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html

里面提供了一個ReadWriteLock類,標準用法如下。

Standard usage of ReadWriteLock:

 class X {

  ReadWriteLock rw;

  // ...

  public void read() throws InterruptedException {

  rw.readLock().acquire();

  try {

  // ... do the read

  }

  finally {

  rw.readlock().release()

  }

  }

  public void write() throws InterruptedException {

   rw.writeLock().acquire();

  try {

  // ... do the write

  }

  finally {

    rw.writelock().release()

  }

  }

 }

看完上述內容,你們掌握Java 中怎么利用Thread實現讀寫同步的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

兰溪市| 常州市| 张家界市| 濮阳市| 同仁县| 阿拉尔市| 上虞市| 鸡西市| 华安县| 滦南县| 昔阳县| 喀什市| 崇义县| 马鞍山市| 衡水市| 申扎县| 方城县| 平塘县| 镶黄旗| 镇巴县| 克什克腾旗| 三穗县| 申扎县| 芷江| 耿马| 巨鹿县| 屏南县| 绩溪县| 铜山县| 太原市| 大兴区| 阿拉尔市| 定日县| 南岸区| 寻乌县| 永定县| 洪洞县| 乃东县| 兰溪市| 万盛区| 新绛县|