一道多线程题目的思考

题目如下:
  启动4个线程,向4个文件A,B,C,D里写入数据,每个线程只能写一个值。
  线程1:只写1
  线程2:只写2
  线程3:只写3
  线程4:只写4
  4个文件A,B,C,D。
  程序运行起来,4个文件的写入结果如下:
  A:12341234...
  B:23412341...
  C:34123412...
  D:41234123...

  这是前些天看的一个blog上的题目 http://yanxuxin.iteye.com/blog/602326 他的实现我没有时间看,以后再补上,大概是理解的,简单的说就是如果线程2来了,就想办法找结尾是1的文件提供给2写入.我自己亦因为一些问题的困扰想了好久
  最初比较容易发现的一个规律:
    线程:1234 1234 1234 1234....
    资源:ABCD DABC CDAB BCDA....
  线程对应资源的调度是呈以上的规律重复出现的。打个很烂的比喻就是:1234手上分别拿着ABCD4种不同的水果 而他们除了想吃自己的还想吃别人的而且生怕别人多吃,于是约定了一个协议:每个人只能对自己拿到的水果咬一口,咬完之后就要按一个方向(顺时针)给下一个(4给1),而自己咬完了上一个人没咬完就只能等。所以很直观的想到按固定顺序调度线程组操作有固定流向的资源。而控制线程按着一个固定顺序调度是可以做到的,如按1,2,3,4的循环;而固定流向的资源ABCDDABCCDABBCDA亦有规律的:以4位为一组分为[ABCD][DABC][CDAB][BCDA]而每组的最后一位放在最前面就是下一组的结果,而后面的都是重复的,所以考虑循环。而如何协调1->A,2->B,3->C,4->D 可以将资源放到阻塞队列中 然后按固定顺序调度控制对应的线程去取,这个固定顺序需要同步机制实现

  而之后有个问题我想了好久:有这样的一种情况就是: 2吃完了自己的水果,3把自己的和2的都吃完了,就是2,3都等着要吃1的 当1吃完了 如何区分是给2还是3呢?后面想到的方法是给他们定义带顺序的号卡,2,3就按着号排队。1通过号卡区分(就是给1,2,3,4定义各自的对象监视器,而且是有序的,他们通过自身的序号可以取到下一线程等待的监视器,而唤醒下一线程)

  实现如下:<如有不对欢迎指出>

package com.java5.learning.concurrent;

import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* 测试类
*
@author zengjf
*
*/
public class WritingTest {

private static int order = 0;

public static void main(String[] args) {
final String[] Contents = new String[]{"1","2","3","4"};
int srcNums = 4, threadNums = 4;
FileFlow flow = new FileFlow(srcNums);
final FileScheduler fileScheduler = new FileScheduler(flow,threadNums);

ExecutorService es = Executors.newFixedThreadPool(threadNums);
for (int i = 0; i < threadNums; i++) {
es.execute(new Runnable() {
public void run() {
int o = order++;
try {
while (true) {
fileScheduler.write(o, fileScheduler.schedule(o),
Contents[o]);
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
es.shutdown();
}
}
/**
* 文件对象的获取及写入
*
@author zengjf
*
*/
class FileScheduler{

FileFlow flow;
Lock lock;
Condition[] condition;
boolean[] turn;//顺序
ConcurrentMap isVisiting;//文件是否被占据
ConcurrentMap lockObjMap;//号卡集合
ConcurrentMap outputMap;

public FileScheduler(FileFlow flow,int threadNums){
this.flow = flow;
this.lock = new ReentrantLock();
this.turn = new boolean[threadNums];
this.condition = new Condition[threadNums];
this.isVisiting = new ConcurrentHashMap();
this.lockObjMap = new ConcurrentHashMap();
this.outputMap = new ConcurrentHashMap<String,StringBuffer>();

for (int i = 0; i < threadNums; i++) {
turn[i] = i == 0 ? true: false;
condition[i] = this.lock.newCondition();
lockObjMap.put(i,new Integer(i));
}
}
/**
* 按流向获取文件
*
@param order
*
@return
*
@throws Exception
*/
public String schedule(int order) throws Exception{
String file;
lock.lock();
try {
//如果初次进入的非1线程 则等待 又1线程开始获取 并唤醒下一线程
while (!turn[order]) {
condition[order].await();
}

file = flow.getFileInFlow();
Thread.sleep(100);

turn[order] = false;//自身等待
order = order == 3 ? 0 : order + 1;
turn[order] = true;//唤醒下一位

condition[order].signal();
} finally {
lock.unlock();
}
return file;
}
/**
* 写入文件
*
@param order
*
@param file
*
@param Content
*
@throws Exception
*/
public void write(int order,String file,String Content)throws Exception{

synchronized (FileScheduler.class) {
if (!isVisiting.containsKey(file)) {
isVisiting.put(file, false);
outputMap.put(file,new StringBuffer(file+":"));
}
}

synchronized (FileScheduler.class) {
while ((Boolean) isVisiting.get(file)) {
Integer lockObj = (Integer)lockObjMap.get(order);//号卡
synchronized (lockObj) {
System.out.println("线程" + (order+1) + "等待" + file);
lockObj.wait();
}
}
}
//并发写入
isVisiting.put(file, true);
System.out.println("线程" + (order+1) + "开始写文件" + file + "..");
Thread.sleep(new Random().nextInt(4)*1000);
System.out.println("线程" + (order+1) + "写文件" + file + "结束..");
StringBuffer sb = (StringBuffer)outputMap.get(file);
System.out.println(sb.append(Content).toString());
isVisiting.put(file, false);

//如果存在等待线程 则通过号卡唤醒
Integer nextLockObj = (Integer)lockObjMap.get(order == 3 ? 0 : order + 1);
synchronized (nextLockObj) {
nextLockObj.notify();
}
}
}
/**
* 文件类 按照某个流向供应文件对象
*
@author zengjf
*
*/
class FileFlow{
private ArrayList<String> list = new ArrayList<String>();
private BlockingQueue<String> queue = new ArrayBlockingQueue<String>(4);
private String[] filenames = {"A","B","C","D"};
public FileFlow(int nums){
for (int i = 0; i < nums && i< filenames.length; i++) {
list.add(filenames[i]);
}

ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new Runnable(){
public void run() {
while(true){
int i ;
for ( i = 0; i < list.size(); i++) {
try {
queue.put(list.get(i));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

list.add(0,list.remove(i-1));
}
}
});
}

public String getFileInFlow(){
try {
return queue.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}

public int length(){
return list.size();
}
}



 

 

posted @ 2012-03-16 06:23  Goodspeed85  阅读(641)  评论(6编辑  收藏  举报