四个线程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;
}
}