新类库中的构件

CountDownLatch

它被用来同步一个或多个任务,强制他们等待由其他任务执行的一组操作完成。
你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用wait的方法都将阻塞,直至这个计数值到达0.其他任务在结束其工作时,可以在该对象上调用countDown来减小这个计数值。CountDownLatch被设计为只触发一次,计数值不能被重载。如果你需要能够重置计数值的版本,则可以使用CyclicBarrier.
调用countDown的任务在产生这个调用时并没有被阻塞,只有对await的调用会被阻塞直至计数值到达0.
CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决任务,并创建者为0的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown。等待问题被解决的任务在这个锁存器上调用await,将他们自己拦住,直至锁存器计数结束。

import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

class TaskPortion implements Runnable {
    private static int counter = 0;

    private final int id = counter++;

    private static Random random = new Random();

    private final CountDownLatch latch;

    public TaskPortion(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        try {
            doWork();
            latch.countDown(); //释放锁,如果计数为零,则释放所有的线程
        } catch (InterruptedException e) {
            //Acceptable way to exit 可接受的退出方式
        }
    }

    public void doWork() throws InterruptedException {
        TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));
        System.out.println(this+ " completed");
    }

    public String toString() {
        return String.format("%1$-3d ",id);
    }
}

class WaitingTask implements Runnable {
    private static int counter = 0;
    private final int id = counter++;

    private final CountDownLatch latch;

    public WaitingTask(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        try {
            //System.out.println("Latch barrier passed for1 "+this);
            latch.await();
            System.out.println("Latch barrier passed for "+this);
        }catch (InterruptedException e) {
            System.out.println(this+" interrupted");
        }
    }

    public String toString() {
        return String.format("WaitingTask %1$-3d",id);
    }
}
public class CountDownLatchDemo {

    static final int SIZE = 100;

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //所有人必须共用一个 CountDownLatch
        CountDownLatch countDownLatch = new CountDownLatch(SIZE);

**加粗样式**        for (int i = 0; i < 10; i++) {
            executorService.execute(new WaitingTask(countDownLatch));
        }

        for (int i = 0; i < SIZE; i++) {
            executorService.execute(new TaskPortion(countDownLatch));
        }

        System.out.println("Launched all tasks");

        executorService.shutdown();
    }
}

TaskPortion 将随机地休眠一段时间,以模拟这部分工作的完成,而WaitingTask表示系统中必须等待地部分,它要等待到问题的初始部分完成为止。所有任务都使用了在main中定义的同一个单一的CountDownLatch.

类库的线程安全

注意,TaskPorition包含一个静态的Random对象,这意味着多个任务可能会同时调用Random.nextInt。这是否安全呢?
如果存在问题,在这种情况下,可以通过想TaskPortion提供其自己的Random对象来解决。也就是说,通过移除static限定符的方式解决。但是这个问题对于Java标准类库中的方法来说,也大都存在,那些是线程安全,那些不是。
遗憾的是,JDK文档并没有指出这一点,Random.nextInt碰巧是安全的,但是你必须通过使用Web引擎,或者审视Java类库代码,去逐个地揭示这一点,这对于被设计为支持,至少理论上支持并发的程序设计语言来说,并非是一件好事。

CyclicBarrier

CyclicBarrier适用于这样的情况:你希望创建一组任务,它们并行地执行工作,然后在进行下一个步骤之前等待,直至所有任务都完成(看起来有些像join).它使得所有的并行任务都将在侧栏列队,因此可以一致地向前移动。这非常像CountDownLatch,只是CountDownLatch是只触发一次的事件,而CyclicBarrier可以多次重用。
从刚开始接触计算机时开始,我就对仿真着了迷,而并发是使仿真成为可能的一个关键因素。记得我最开始编写的一个程序就是一个防止:一个BASIC编写的(由于文件名的卸载而命名为HOSRAC.BAS的赛马游戏)。下面是那个程序的面向对象的多线程版本,其中使用了CyclicBarrier:

import jdk.nashorn.internal.ir.CallNode;

import java.sql.Time;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

class Horse implements Runnable {
    private static  int counter = 0;

    private final int id = counter++;

    private int strides = 0;

    private static Random random = new Random(47);

    private static CyclicBarrier barrier;

    public Horse(CyclicBarrier b) {
        barrier = b;
    }

    public synchronized int getStrides() {
        return strides;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                synchronized (this) {
                    strides+=random.nextInt(3);
                }
                barrier.await();
            }
        }catch (InterruptedException e) {
            //合法的退出方式
        } catch (BrokenBarrierException e) {
            //我们想知道的是这个
            throw new RuntimeException(e);
        }
    }

    public String toString() {
        return "Horse "+id+" ";
    }

    public String tracks() {
        StringBuilder stringBuilder = new StringBuilder();

        for (int i = 0; i < getStrides(); i++) {
            stringBuilder.append("*");
        }
        stringBuilder.append(id);
        return stringBuilder.toString();
    }
}

public class HorseRace {
    static final int FINISH_LINE = 75;

    private List<Horse> horses =  new ArrayList<Horse>();

    private ExecutorService executorService = Executors.newCachedThreadPool();

    private CyclicBarrier barrier;

    public HorseRace(int nHorses, final int pause){
        barrier = new CyclicBarrier(nHorses, new Runnable() {
            @Override
            public void run() {
                StringBuilder s = new StringBuilder();

                for (int i = 0; i < FINISH_LINE; i++) {
                    s.append("=");//跑道上的栅栏
                }
                System.out.println("chengpeng: "+s);
                for (Horse hors : horses) {
                    if (hors.getStrides() >= FINISH_LINE) {
                        System.out.println(hors+" won!");
                        executorService.shutdownNow();
                        return;
                    }
                    try {
                        TimeUnit.MILLISECONDS.sleep(pause);
                    } catch (InterruptedException e) {
                        System.out.println("barrier-action sleep interruoted");
                    }
                }
            }
        });

        for (int i = 0; i < nHorses; i++) {
            Horse horse = new Horse(barrier);
            horses.add(horse);
            executorService.execute(horse);
        }
    }

    public static void main(String[] args) {
        int nHorses = 7;
        int pause = 200;

        if (args.length > 0) {
            int n = new Integer(args[0]);
            nHorses = n>0?n:nHorses;
        }

        if (args.length >1) {
            int p = new Integer(args[1]);
            pause = p>-1?p:pause;
        }

        new HorseRace(nHorses,pause);
    }
}

可以向CyclicBarrier提供一个侧栏动作,他是一个Runnable,当计数值到达0时自动执行–这是CyclicBarrier和CountDownLatch之间的另一个区别。这里,侧栏动作时作为匿名内部类创建的,它被提交了CyclicBarrier的构造器。

DelayQueue

这是一个无界的BlockingQueue,用于放置实现了Delayed接口的对象,其中的对象只能在其到期时才能从队列中取走。这种队列是有序的,即队头对象的延迟到期的时间最长。如果没有任何延迟到期,那么就不会有任何头元素,并且poll将返回null(正因为这样,你不能将null放置到这种队列中)。
下面是一个示例,其中的Delayed对象自身就是任务,而DelayedTaskConsumer将最紧急的任务(到期时间最长的任务)从队列中取出,然后运行它。注意,这样DelayQueue就成为了优先级队列的一种变体:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;

import static java.util.concurrent.TimeUnit.*;

class DelayedTask implements Runnable, Delayed {

    private static int counter = 0;

    private final int id = counter++;

    private final int delta;

    private final long trigger;

    protected static List<DelayedTask> sequence = new ArrayList<DelayedTask>();

    public DelayedTask(int delayInMilliseconds) {
        delta = delayInMilliseconds;
        trigger = System.nanoTime()+NANOSECONDS.convert(delta,MILLISECONDS);
        //System.out.println("chengpeng: "+ this);
        sequence.add(this);
    }

    @Override
    public int compareTo(Delayed arg) {

        DelayedTask that = (DelayedTask)arg;
        if (trigger < that.trigger)
            return -1;
        if (trigger > that.trigger)
            return 1;
        return 0;
    }

    @Override
    public void run() {
        System.out.println(this+" ");
    }


    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(trigger-System.nanoTime(),NANOSECONDS);
    }

    public String toString() {
        return String.format("[%1$-4d]",delta)+" Task "+id;
    }

    public String summary() {
        return "("+id+": "+delta+")";
    }

    public static class EndSentinel extends DelayedTask {
        private ExecutorService executorService;

        public EndSentinel(int delay, ExecutorService executorService) {
            super(delay);
            this.executorService =executorService;
        }

        public void run(){
            for (DelayedTask delayedTask : sequence) {
                System.out.println(delayedTask.summary()+" ");
            }

            System.out.println();
            System.out.println(this+" Calling shutdownNow()");
            executorService.shutdownNow();
        }
    }
}

class DelayedTaskConsumer implements Runnable {
    private DelayQueue<DelayedTask> q;

    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {
        this.q = q;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                //检索并删除此队列的头部,必要时等待 直到延迟过期的元素在此队列上可用。
                q.take().run();
            }
        }catch (InterruptedException e) {

        }
        System.out.println("Finished DelayedTaskConsumer");
    }
}

public class DelayQueueDemo {
    public static void main(String[] args) {
        Random random = new Random(47);
        ExecutorService executorService = Executors.newCachedThreadPool();
        DelayQueue<DelayedTask> delayedTasks = new DelayQueue<DelayedTask>();

        for (int i = 0; i < 20; i++) {
            delayedTasks.put(new DelayedTask(random.nextInt(5000)));//插入元素
        }
        //设置停止点
        delayedTasks.add(new DelayedTask.EndSentinel(5000,executorService));

        executorService.execute(new DelayedTaskConsumer(delayedTasks));
    }
}

DelayedTask包含一个称为sequence的List,它保存了任务被创建的顺序,因此我可以看到排序是按照实际实际发生的顺序执行的。
Delayed接口有一个方法名为getDelay,它可以用来告知延迟到期有多长时间,或者延迟在多长时间之前已经到期。这个方法将强制我们去使用tImeUnit类,因为这就是参数类型。这会产生一个非常方便的类,因为你可以很容易地转换单位而无需作任何声明。例如delta的值是以毫秒为单位存储的,但是在Java SE5的方法System.nanoTime产生的时间则是以纳秒为单位的。你可以转换delta的值,方法是声明它的单位以及你希望以什么单位来表示,就像下面这样:NANOSECONDS.convert(delta,MILLISECONDS);
在getDelay中,希望使用的单位是作为unit参数传递进来的,你使用它将当前时间与触发时间之间的差转换为调用者要求的单位,而无需知道这些单位是什么(这是策略设计模式的一个简单示例,在这种模式中,算法的一部分是作为参数传递进来的)。
为了排序,Delayed接口还继承了Comparable接口,因此必须实现compareTo,使其可以产生合理的比较,toString和summary提供了输出格式化,而嵌套的EndSentinel类提供了一种关闭所有事物的途径,具体做法是将其放置为队列的最后一个元素。
注意,因为DelayedTaskConsumer自身是一个任务,所以它有自己的Thread,它可以使用这个线程来运行从队列中获取的所有的任务。由于任务时按照队列优先级的顺序执行的,因此在本例中不需要启动任何单独的线程来运行DelayedTask。
从输出中可以看到,任务创建的顺序对执行顺序没有任何影响,任务是按照所期望的延迟顺序执行的。

PriorityBlockingQueue

这是一个很基础的优先级队列,它具有可阻塞的读取操作。下面是一个示例,其中在优先级队列中的对象是按照优先级顺序从队列中出现的任务。PrioritizedTask被赋予了一个优先级数字,以此来提供这种顺序:

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;

class PrioritizedTask implements Runnable,Comparable<PrioritizedTask> {

    private Random random = new Random(47);

    private static int counter = 0;

    private final int id = counter++;

    private final int priority;

    protected static List<PrioritizedTask> sequence = new ArrayList<PrioritizedTask>();

    public PrioritizedTask(int priority) {
        this.priority = priority;
        sequence.add(this);
    }

    @Override
    public int compareTo(PrioritizedTask arg) {
        return priority < arg.priority ? 1:(priority > arg.priority ? -1: 0);
    }

    @Override
    public void run() {
        try {
            System.out.println("check");
            TimeUnit.MILLISECONDS.sleep(random.nextInt(250));
        } catch (InterruptedException e) {

        }
        System.out.println(this +" chengpeng");
    }

    public String toString() {
        return String.format("[%1$-3d]",priority)+" Task "+id;
    }

    public String summary() {
        return "("+id+":"+priority+")";
    }

    public static class EndSentinel extends PrioritizedTask {
        private ExecutorService executorService;

        public EndSentinel(ExecutorService executorService) {
            super(-1); //这个程序的最低优先级
            this.executorService = executorService;
        }

        public void run() {
            int count = 0;

            for (PrioritizedTask prioritizedTask : sequence) {
                System.out.println(prioritizedTask.summary());
                if (++count % 5 == 0) {
                    System.out.println();
                }
            }
            System.out.println();
            System.out.println(this +" Calling shutdownNow()");
            executorService.shutdownNow();
        }
    }
}

class PrioritizedTaskProducer implements Runnable {
    private Random random = new Random(47);
    private Queue<Runnable> queue;

    private ExecutorService executorService;

    public PrioritizedTaskProducer(Queue<Runnable> q, ExecutorService executorService) {
        queue =q;
        this.executorService =executorService;
    }

    public void run() {
        for (int i = 0; i < 20; i++) {
            queue.add(new PrioritizedTask(random.nextInt(10)));
            Thread.yield();
        }

        try {
            for (int i = 0; i < 10; i++) {
                TimeUnit.MILLISECONDS.sleep(250);
                queue.add(new PrioritizedTask(10));
            }

            for (int i = 0; i < 10; i++) {
                queue.add(new PrioritizedTask(i));
            }

            queue.add(new PrioritizedTask.EndSentinel(executorService));
        } catch (InterruptedException e){

        }
        System.out.println("Finished PrioritizedTaskProducer");
    }
}

class PrioritizedTaskConsumer implements Runnable {
    private PriorityBlockingQueue<Runnable> q;

    public PrioritizedTaskConsumer(PriorityBlockingQueue<Runnable> q) {
        this.q = q;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                System.out.println("checking");
                q.take().run();
                System.out.println("checking1");
            }
        } catch (InterruptedException e) {

        }
        System.out.println("Finished PrioritizedTaskConsumer");
    }
}

public class PriorityBlockingQueueDemo {
    public static void main(String[] args) {
        Random random = new Random(47);

        ExecutorService executorService = Executors.newCachedThreadPool();

        PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<>();

        executorService.execute(new PrioritizedTaskProducer(queue,executorService));
        executorService.execute(new PrioritizedTaskConsumer(queue));

    }
}

与前一个示例相同,PrioritizedTask对象的创建序列被记录在sequence List中,用于和实际的执行顺序比较,run方法将休眠一小段随机的时间,然后打印对象信息,而EndSentinel提供了和前面相同的功能,要确保它是队列中最后一个对象。
PrioritizedTaskProducer和PrioritizedTaskComsumer通过PrioriryBlockingQueue彼此连接,因为这种队列的阻塞特性提供了所有必须得同步,所以你应该注意到了,这里不需要任何显示的同步–不必考虑当你从这种队列中读取时,其中是否有元素,因为这个队列在没有元素时,将直接阻塞读取者。

Semaphore

正常的锁(来自concurrent.locks或内建的synchronized锁)在任何时刻都只允许一个任务访问一项资源,而计数信号量允许n个任务同时访问这个资源。你还可以将信号量看作是在向外分发使用资源的许可证,尽管实际上没有使用任何许可证对象。
作为一个示例,请考虑对象池的概念,它管理着数量有限的对象,当使用对象时可以签出他们,而在用户使用完毕时,可以将他们签回。这种功能可以被封装到一个泛型类中:

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class Pool<T> {

    private int size;

    private List<T> items = new ArrayList<T>();

    private volatile boolean[] checkedOut;

    private Semaphore available;

    public Pool(Class<T> classObject, int size) {
        this.size = size;
        checkedOut = new boolean[size];
        available = new Semaphore(size,true);

        for (int i = 0; i < size; i++) {
            try {
            	//假设一个默认构造函数
                items.add(classObject.newInstance());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    public T checkOut() throws InterruptedException {
        available.acquire(); // 线程占用一个许可
        return getItem();
    }

    public void checkIn(T x) {
        if (releaseItem(x)) {
            available.release(); // 释放一个锁
        }
    }

    public synchronized T getItem() throws InterruptedException {
        for (int i = 0; i < size; i++) {
            if (!checkedOut[i]) {
                checkedOut[i] = true;
                return items.get(i);
            }
        }
        return null; //信号阻止到达这里
    }

    public synchronized boolean releaseItem(T item) {
        int index = items.indexOf(item);
        if (index == -1) {
            return false;
        }

        if (checkedOut[index]) {
            checkedOut[index] = false;
            return true;
        }

        return false;
    }
}

在这个简化的形式中,构造器使用newInstance来把对象加载到池中。如果你需要一个新的对象,那么可以调用checkOut,并且使用完之后,将其递交给checkIn.
boolean类型的数组checkedOut可以跟踪被签出的对象,并且可以通过getItem和releaseItem方法来管理。而这些都将由Semaphore类型的available来加以确保,因此,在checkOut中,如果没有任何信号量许可证可用(这意味着在池中没有更多的对象了),available将阻塞调用过程。在checkIn中,如果被签入的对象有效,则会向信号量返回一个许可证。
为了创建一个示例,我们可以使用Fat,这是一种创建代价高昂的对象类型,因为它的构造器运行起来很耗时:

public class Fat {

    private volatile  double d; //防止优化
    
    private static int counter = 0;
    
    private final int id = counter++;
    
    public Fat() {
        for (int i = 0; i < 10000; i++) {
            d+= (Math.PI + Math.E)/(double)i;
        }
    }
    
    public void operation() {
        System.out.println(this);
    }
    
    public String toString() {
        return "Fat id: " + id;
    }
}

我们在池中管理这些对象,以限制这个构造器所造成的影响,我们可以创建一个任务,它将签出Fat对象,持有一段时间之后再将时间之后再将他们签入,以此来测试Pool这个类:

import org.omg.CORBA.MARSHAL;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

class CheckoutTask<T> implements Runnable {
    private static int counter = 0;

    private final int id = counter++;

    private Pool<T> pool;

    public CheckoutTask(Pool<T> pool) {
        this.pool = pool;
    }

    public void run() {
        try {
            T item = pool.checkOut();
            System.out.println(this + "checked out " +item);
            TimeUnit.MILLISECONDS.sleep(1);
            System.out.println(this+"checking in "+ item);
            pool.checkIn(item);
        } catch (InterruptedException e) {

        }
    }

    public String toString() {
        return "CheckoutTask " +id +" ";
    }

}

public class SemaphoreDemo {
    final static int SIZE = 25;

    public static void main(String[] args) throws InterruptedException {
        final Pool<Fat> fatPool = new Pool<>(Fat.class, SIZE);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < SIZE; i++) {
            executorService.execute(new CheckoutTask<Fat>(fatPool));
        }
        System.out.println("All CheckoutTasks created");

        List<Fat> fats = new ArrayList<>();

        for (int i = 0; i < SIZE; i++) {
            Fat fat = fatPool.checkOut();

            System.out.println(i+": main() thread checked out");

            fat.operation();

            fats.add(fat);
        }

        Future<?> submit = executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    //信号量防止额外的检出
                    //所以调用被阻塞了
                    fatPool.checkOut();
                } catch (Exception e) {
                    System.out.println("checkOut() Interrupted");
                }
            }
        });

        TimeUnit.SECONDS.sleep(2);
        submit.cancel(true); //尝试取消任务
        System.out.println("Checking in objects in "+fats);

        for (Fat fat : fats) {
            fatPool.checkIn(fat);
        }

        for (Fat fat : fats) {
            fatPool.checkIn(fat); //第二次签入忽略
        }

        executorService.shutdown();

    }
}

在main中,创建了一个持有Fat对象的Pool,而一组CheckoutTask则开始操练这个Pool.然后,main线程签出池中的Fat对象,但是并不签入他们。一旦池中所有的对象都签出,Semaphore将不再允许执行任何签出操作。Blocked的run方法因此会被阻塞,2秒钟之后,cancel方法被调用,以此来挣脱Future的束缚。注意,冗余的签入将被Pool忽略。
这个示例依赖于Pool的客户端严格地并原因签入所持有的对象,当其工作时,这是最简单的解决方案,如果你无法总是可以依赖于此。

Exchanger

Exchanger是在两个任务之间交换对象的侧栏,当这些任务进入侧栏时,他们各种拥有一个对象,当他们离开时,他们都拥有之前由对象持有的对象。Exchanger的典型应用场景是:一个任务在创建对象,这些对象的生产代表很高昂,而另一个任务在消费这些对象。通过这种方式,可以有更多的对象在被创建的同时被消费。
为了演绎Exchanger类,我们将创建生产者和消费者任务,他们经由泛型和Generator,可以工作于任何类型的对象,然后我们将他们应用于Fater.ExchangerProducer和ExchangerConsumer使用一个List作为要交换的对象,它们都包含一个用于这个List的Exchanger,当你调用Exchanger.exchange方法时,它将阻塞直至对方任务调用它自己的exchange方法,那时,这两个exchange方法将全部完成,而List则被互换:

import net.mindview.util.BasicGenerator;
import net.mindview.util.Generator;

import java.util.List;
import java.util.concurrent.*;

class ExchangerProducer<T> implements Runnable {

    private Generator<T> generator;

    private Exchanger<List<T>> exchanger;

    private List<T> holder;

    ExchangerProducer(Exchanger<List<T>> exchg,Generator<T> generator, List<T> holder) {
        this.exchanger = exchg;
        this.generator = generator;
        this.holder = holder;
    }

    public void run() {
        try {
            while (!Thread.interrupted()) {
                for (int i = 0; i < ExchangerDemo.size; i++) {
                    holder.add(generator.next());
                }
                holder = exchanger.exchange(holder);
            }
        } catch (InterruptedException e) {

        }
    }
}


class ExchangerConsumer<T> implements Runnable {

    private Exchanger<List<T>> exchanger;

    private List<T> holder;

    private volatile T value;

    ExchangerConsumer(Exchanger<List<T>> exchanger,List<T> holder) {
        this.exchanger = exchanger;
        this.holder = holder;
    }


    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                holder = exchanger.exchange(holder);
                for (T t : holder) {
                    value = t;
                    holder.remove(t);
                }
            }
        } catch (InterruptedException e) {

        }
        System.out.println("Final value: " + value);
    }
}

public class ExchangerDemo {

    static int size = 10;

    static int delay  = 5;

    public static void main(String[] args) throws InterruptedException {
        if (args.length > 0) {
            size = new Integer(args[0]);
        }

        if (args.length >1) {
            delay = new Integer(args[1]);
        }

        ExecutorService executorService = Executors.newCachedThreadPool();
        Exchanger<List<Fat>> listExchanger = new Exchanger<>();

        List<Fat> fats = new CopyOnWriteArrayList<>();
        List<Fat> fats1 = new CopyOnWriteArrayList<>();

        executorService.execute(new ExchangerProducer<Fat>(listExchanger, BasicGenerator.create(Fat.class),fats));
        executorService.execute(new ExchangerConsumer<Fat>(listExchanger,fats1));
        TimeUnit.SECONDS.sleep(delay);
        executorService.shutdownNow();

    }
}

在main中,创建了用于两个任务的单一的Exchanger,以及两个用于互换的CopyOnWriterArrayList。这个特定的List变体允许在列表被遍历时调用remover方法,而不会抛出异常。ExchangeProducer将填充这个List,然后将这个满列表交换ExchangerConsumer传递给它的空列表。因为有了Exchanger,填充一个列表和消费另一个列表便可以同时发生了。
(不理解)

你可能感兴趣的:(java,开发语言)