1. 程式人生 > >執行緒順序控制:四個執行緒A、B、C、D向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B……

執行緒順序控制:四個執行緒A、B、C、D向四個檔案寫入資料。要求A執行緒只寫入A,B執行緒只寫入B……

四個執行緒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;
	}
}