线程顺序控制:四个线程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写的,写完后释放……

扫描二维码关注公众号,回复: 4742086 查看本文章

总结一下:应该是看文本的空闲情况写数据,而不是看线程的顺序情况,基本上四个线程轮到后都能向文本中写数据。


上代码:

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;
	}
}


猜你喜欢

转载自blog.csdn.net/dobuy/article/details/7618150
今日推荐