当前位置:编程学习 > JAVA >>

一道多线程题目的思考

题目如下:
  启动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://www.zzzyk.com/kf/201203/123393.html  他的实现我没有时间看,以后再补上,大概是理解的,简单的说就是如果线程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();
补充:软件开发 , Java ,

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,