執行緒順序控制:四個執行緒A、B、C、D向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B……
阿新 • • 發佈:2019-01-02
四個執行緒A、B、C、D向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B……
最終達到的效果:
A.txt內容為: A B C D A B C D……
B.txt內容為: B C D A B C D A……
C.txt內容為: C D A B C D A B……
D:txt內容為: D A B C D A B C……
分析如下:
4個執行緒必須對所有的txt檔案加鎖,要想效率高,必須保證只要有txt檔案等待被寫時就喚醒所有執行緒去競爭寫入。
如A執行緒獲取到寫許可權時,只要4個文字中有一個輪到A執行緒寫,A就去寫;
A寫完後,喚醒所有執行緒,如C獲取資源,C也在4個文字中找一個輪到C寫的,寫完後釋放……
總結一下:應該是看文字的空閒情況寫資料,而不是看執行緒的順序情況,基本上四個執行緒輪到後都能向文字中寫資料。
上程式碼:
1、記事本類,代表一個txt檔案
Note.java
import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * 記事本 * * @author dobuy * @time 2012-5-30 */ public class Note { /** * 日誌物件 */ private static Log logger = LogFactory.getLog(Note.class); /** * 當前待記事的人 */ private Person person; /** * 記事本的路徑:Windows專案的src目錄下 */ private final String PATH; /** * 寫入緩衝流 */ private BufferedWriter writer; /** * 構造記事本物件 * * @param fileName 記事本名稱 * @param person 第一個記事人 */ public Note(String fileName, Person person) { // 指定src目錄 PATH = System.getProperty("user.dir") + File.separator + "src" + File.separator + fileName; this.person = person; } /** * 記事本上新增內容,同時本記事本的待記事人後移一位 */ public void addNote() { try { // 定義寫入緩衝流,追加方式 writer = new BufferedWriter(new FileWriter(PATH, true)); writer.write(person.getContent() + "\t"); // 清空緩衝流 writer.flush(); } catch (IOException e) { logger.error("寫入流異常", e); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { logger.error("寫入流關閉異常", e); } } } // 待記事人後移一位 person = person.getNextPerson(); } /** * 獲取本記事本的待記事人 * * @return */ public Person getCurrentPerson() { return person; } }
2、Notes.java 所有的記事本集合類,應對它加同步鎖
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 記事本集
*
* @author dobuy
* @time 2012-5-30
*/
public class Notes
{
/**
* 列印日誌物件
*/
private static Log logger = LogFactory.getLog(Notes.class);
/**
* 記事本集中的所有記事本集合
*/
private List<Note> notes = new ArrayList<Note>();
/**
* 向記事本集中新增記事本
*
* @param note
*/
public void addNote(Note note)
{
if (null == note || null == note.getCurrentPerson())
{
logger.error("記事本未指定/記事本的首選人未指定");
return;
}
notes.add(note);
}
/**
* 獲取當前人可以寫的記事本,沒有返回null
* 為了保證所有記事本被寫的公平性,遍歷記事本集採取從隨機位置開始
*
* @param person 當前人
* @return
*/
public Note getCurrentNote(Person person)
{
int size = notes.size();
int index = new Random().nextInt(size);
boolean start = true;
for (int i = index;; i++)
{
i = i % size;
if (i == index)
{
if (start)
{
start = false;
}
else
{
return null;
}
}
if (notes.get(i).getCurrentPerson() == person)
{
return notes.get(i);
}
}
}
}
3、Person.java 對應A、B、C、D執行緒,負責寫資料
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* 記事人類,負責記事
* @author dobuy
* @time 2012-5-30
*/
public class Person implements Runnable
{
/**
* 日誌類
*/
private static Log logger = LogFactory.getLog(Person.class);
/**
* 記事本集
*/
private Notes notes;
/**
* 記事內容
*/
private String content;
/**
* 下一個記事人
*/
private Person nextPerson;
/**
* 構建記事人物件,指定記事本集和內容
* @param content 記事內容
* @param notes 記事本集
*/
public Person(String content,Notes notes)
{
this.notes = notes;
this.content = content;
}
/**
* 向記事本中寫入內容
*/
public void write()
{
synchronized(notes)
{
//當記事本集中存在記事本輪到本人寫,本人就開始寫,寫完後立馬釋放
while(null==notes.getCurrentNote(this))
{
logger.debug(content+"進入等待狀態.");
try
{
notes.wait();
}
catch (InterruptedException e)
{
logger.error("執行緒中斷異常",e);
}
}
logger.debug(content+"開始寫資料");
//待被寫的記事本上增加記事人內容,寫完後,該記事本的待記事人後移一位
notes.getCurrentNote(this).addNote();
//喚醒所有記事人
notes.notifyAll();
}
}
@Override
public void run()
{
if(null==notes)
{
logger.error("記事本集物件未指定.");
return;
}
while(true)
{
//準備寫入內容
write();
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
logger.error("執行緒中斷異常",e);
}
}
}
public String getContent()
{
return content;
}
public Person getNextPerson()
{
return nextPerson;
}
public void setNextPerson(Person nextPerson)
{
this.nextPerson = nextPerson;
}
}
4、Junit測試類
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* 測試類
*
* @author dobuy
* @time 2012-5-30
*/
public class PersonTest
{
// 定義物件
private Person personA;
private Person personB;
private Person personC;
private Person personD;
private Note noteA;
private Note noteB;
private Note noteC;
private Note noteD;
private Notes notes;
@Before
public void before()
{
notes = new Notes();
personA = new Person("A", notes);
personB = new Person("B", notes);
personC = new Person("C", notes);
personD = new Person("D", notes);
noteA = new Note("A.txt", personA);
noteB = new Note("B.txt", personB);
noteC = new Note("C.txt", personC);
noteD = new Note("D.txt", personD);
}
@Test
public void test()
{
// 設定記事人的先後順序
personA.setNextPerson(personB);
personB.setNextPerson(personC);
personC.setNextPerson(personD);
personD.setNextPerson(personA);
// 記事本集中加入記事本
notes.addNote(noteA);
notes.addNote(noteB);
notes.addNote(noteC);
notes.addNote(noteD);
// 記事人開始記事
new Thread(personA).start();
new Thread(personB).start();
new Thread(personC).start();
new Thread(personD).start();
try
{
// 執行20秒
Thread.sleep(1000 * 20);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
@After
public void after()
{
notes =null;
noteA =null;
noteB =null;
noteC =null;
noteD =null;
personA =null;
personB =null;
personC =null;
personD =null;
}
}