Thinking In Java 之多线程 5

来源:互联网 发布:梳理神圣罗马帝国 知乎 编辑:程序博客网 时间:2024/04/29 17:31

DelayQueue

The queue is sorted so that the object at the head has a delay that has expired for(过期) the longest time.
Note that DelayQueue is thus a variation of a priority queue.

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() + TimeUnit.NANOSECONDS.convert(delta, TimeUnit.MILLISECONDS);        sequence.add(this);    }    @Override    public int compareTo(Delayed o) {        DelayedTask that = (DelayedTask) o;        if (trigger < that.trigger)            return -1;        if (trigger > that.trigger)            return 1;        return 0;    }    // 策略模式     @Override    public long getDelay(TimeUnit unit) {        return unit.convert(trigger - System.nanoTime(), TimeUnit.NANOSECONDS);    }    @Override    public void run() {        System.out.print(this + " ");    }    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 exec;        public EndSentinel(int delayInMilliseconds, ExecutorService e) {            super(delayInMilliseconds);            exec = e;        }        public void run() {            System.out.println();            for (DelayedTask pt : sequence) {                System.out.print(pt.summary() + " ");            }            System.out.println();            System.out.println(this + " Calling shutdownNow()");            exec.shutdownNow();        }    }}class DelayedTaskConsumer implements Runnable {    private DelayQueue<DelayedTask> q;    public DelayedTaskConsumer(DelayQueue<DelayedTask> q) {        this.q = q;    }    @Override    public void run() {        try {            while(!Thread.interrupted()) {                q.take().run();            }        } catch (InterruptedException e) {            // Acceptable way to exit        }        System.out.println("Finished DelayedTaskConsumer");    }}public class DelayQueueDemo {    public static void main(String[] args) {        Random rand = new Random(47);        ExecutorService exec = Executors.newCachedThreadPool();        DelayQueue<DelayedTask> queue = new DelayQueue<DelayedTask>();        // Fill with tasks that have random delays:        for(int i=0;i<20;i++) {            queue.put(new DelayedTask(rand.nextInt(5000)));        }        // Set the stopping point        queue.add(new DelayedTask.EndSentinel(5000, exec));        exec.execute(new DelayedTaskConsumer(queue));    }}

In getDelay(), the desired units are passed in as the unit argument, and you use this to convert the time difference form the trigger time to the units requested by the caller, without even knowing what those units are (this is a simple example of the Strategy design pattern, where part of the algorithm is passed in as an argument).

这里写图片描述

PriorityBlockingQueue

class PriorityzedTask implements Runnable, Comparable<PriorityzedTask> {    private Random rand = new Random(47);    private static int counter = 0;    private final int id = counter++;    private final int priority;    protected static List<PriorityzedTask> sequence = new ArrayList<PriorityzedTask>();    public PriorityzedTask(int priority) {        this.priority = priority;        sequence.add(this);    }    @Override    public int compareTo(PriorityzedTask o) {        return priority < o.priority ? 1 : (priority > o.priority ? -1 : 0);    }    @Override    public void run() {        try {            TimeUnit.MILLISECONDS.sleep(rand.nextInt(250));        } catch (InterruptedException e) {            // Acceptable way to exit        }        System.out.print(this);    }    public String toString() {        return String.format("[%1$d]", priority) + "Task" + id + " ";    }    public String summary() {        return "(" + id  + ":" + priority + ")";    }    public static class EndSentinel extends PriorityzedTask {        private ExecutorService exec;        public EndSentinel(ExecutorService e) {            super(-1); // Lower priority in this program            exec = e;        }        public void run() {            for (PriorityzedTask pt : sequence) {                System.out.print(pt.summary());            }            System.out.println();            System.out.println(this + " Calling shutdownNow()");            exec.shutdownNow();        }    }}class PriorityzedTaskProducer implements Runnable {    private Random rand = new Random(47);    private Queue<Runnable> queue;    private ExecutorService exec;    public PriorityzedTaskProducer(Queue<Runnable> queue, ExecutorService e) {        this.queue = queue;        exec = e; // Used for EndSentinel    }    @Override    public void run() {        // Unbounded queue; never blocks.        // Fill it up fast with random priorities:        for (int i = 0; i < 20; i++) {            queue.add(new PriorityzedTask(rand.nextInt(10)));            Thread.yield();// 如果注释掉,第一条结果显示优先等级为9的线程,而不是线程0了        }        // Trickle in highest-priority jobs:        try {            for (int i = 0; i < 10; i++) {                TimeUnit.MILLISECONDS.sleep(200);                queue.add(new PriorityzedTask(10));            }            // Add jobs, lowest priority first:            for (int i = 0; i < 10; i++) {                queue.add(new PriorityzedTask(i));            }            // A Sentinel to stop all the tasks:            queue.add(new PriorityzedTask.EndSentinel(exec));        } catch (InterruptedException e) {        }        System.out.println("Finished PriorityzedTaskProducer");    }}class PriorityzedTaskConsumer implements Runnable {    private PriorityBlockingQueue<Runnable> q;    public PriorityzedTaskConsumer(PriorityBlockingQueue<Runnable> q) {        this.q = q;    }    @Override    public void run() {        try {            while(!Thread.interrupted()) {                // Use current thread to run the task:                q.take().run();            }        } catch (InterruptedException e) {        }        System.out.println("Finished PriorityzedTaskConsumer");    }}public class PriorityBlockingQueueDemo {    public static void main(String[] args) {        Random rand = new Random(47);        ExecutorService exec = Executors.newCachedThreadPool();        PriorityBlockingQueue<Runnable> queue = new PriorityBlockingQueue<Runnable>();        exec.execute(new PriorityzedTaskProducer(queue, exec));        exec.execute(new PriorityzedTaskConsumer(queue));    }}

You don’t have to think about whether the queue has any elements in it when you’re reading from it, because the queue will simply block the reader when it is out of elements.

这里写图片描述

The greenhouse controller with ScheduledExecutor

Using either schedule() (to run a task once) or scheduleAtFixedRate() (to repeat a task at a regular interval), you set up Runnable objects to be executed at some time in the future.

public class GreenhouseScheduler {    private volatile boolean light = false;    private volatile boolean water = false;    private String thermostat = "Day";    public synchronized String getThermostat() {        return thermostat;    }    public synchronized void setThermostat(String value) {        thermostat = value;    }    ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(10);    public void schedule(Runnable event, long delay) {        scheduler.schedule(event, delay, TimeUnit.MILLISECONDS);    }    public void repeat(Runnable event, long initialDelay, long period) {        scheduler.scheduleAtFixedRate(event, initialDelay, period, TimeUnit.MILLISECONDS);    }    class LightOn implements Runnable {        @Override        public void run() {            System.out.println("Turning on lights");            light = true;        }    }    class LightOff implements Runnable {        @Override        public void run() {            System.out.println("Turning off lights");            light = false;        }    }    class WaterOn implements Runnable {        @Override        public void run() {            System.out.println("Turning greenhouse water on");            water = true;        }    }    class WaterOff implements Runnable {        @Override        public void run() {            System.out.println("Turning greenhouse water off");            water = false;        }    }    class ThermostatNight implements Runnable {        @Override        public void run() {            System.out.println("Thermostat to night setting");            setThermostat("Night");        }    }    class ThermostatDay implements Runnable {        @Override        public void run() {            System.out.println("Thermostat to day setting");            setThermostat("Day");        }    }    class Bell implements Runnable {        @Override        public void run() {            System.out.println("Belling");        }    }    class Terminate implements Runnable {        @Override        public void run() {            System.out.println("Terminating");            scheduler.shutdownNow();            new Thread() {                public void run() {                    for(DataPoint d : data) {                        System.out.println(d);                    }                }            }.start();        }    }    static class DataPoint {        final Calendar time;        final float temperature;        final float humidity;        public DataPoint(Calendar d, float temp, float hum) {            time = d;            temperature = temp;            humidity = hum;        }        public String toString() {            return time.getTime() + String.format(" temperature: %1$.1f humidity: %2$.2f", temperature, humidity);        }    }    private Calendar lastTime = Calendar.getInstance();    {        // Adjust date to the half hour        lastTime.set(Calendar.MINUTE, 30);        lastTime.set(Calendar.SECOND, 00);    }    private float lastTemp = 65.0f;    private int tempDirection = +1;    private float lastHumidity = 50.0f;    private int humidityDirection = +1;    private Random rand = new Random(47);      List<DataPoint> data = Collections.synchronizedList(new ArrayList<DataPoint>());    class CollectData implements Runnable {        @Override        public void run() {            System.out.println("Collecting data");            synchronized (GreenhouseScheduler.this) {                // Pretend the interval is longer than it is:                lastTime.set(Calendar.MINUTE,lastTime.get(Calendar.MINUTE) + 30);                // One in 5 chances of reversing the direction:                if(rand.nextInt(5) == 4) {                    tempDirection = -tempDirection;                }                // Store previous value:                lastTemp = lastTemp + tempDirection * (1.0f + rand.nextFloat());                if(rand.nextInt(5) == 4) {                    humidityDirection = -humidityDirection;                }                lastHumidity = lastHumidity + humidityDirection * rand.nextFloat();                // Calendar must be cloned, otherwise all DataPoints hold refrences to                // the same lastTime. For a basic object like Calendar, clone() is OK.                data.add(new DataPoint((Calendar)lastTime.clone(), lastTemp, lastHumidity));            }        }    }    public static void main(String[] args) {        GreenhouseScheduler gh = new GreenhouseScheduler();        gh.schedule(gh.new Terminate(), 3000);        gh.repeat(gh.new Bell(), 0, 1000);        gh.repeat(gh.new ThermostatNight(), 0, 2000);        gh.repeat(gh.new LightOn(), 0, 200);        gh.repeat(gh.new LightOff(), 0, 400);        gh.repeat(gh.new WaterOn(), 0, 600);        gh.repeat(gh.new WaterOff(), 0, 800);        gh.repeat(gh.new ThermostatDay(), 0, 1400);        gh.repeat(gh.new CollectData(), 500, 500);    }}

最后部分的结果

这里写图片描述

Semaphore

A counting semaphore allows n tasks to access the resource at the same time.

资源池
As an example, consider the concept of the object pool, which manages a limited number of objects by allowing them to be checked out for use, and then checked back in again when the user is finished. This functionality can be encapsulated in a generic class:

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);        // Load pool with objects that can be checked out:            for(int i=0;i<size;i++) {                try {                    items.add(classObject.newInstance());                } catch (Exception e) {                    throw new RuntimeException(e);                }            }    }    public T checkedOut() throws InterruptedException {        available.acquire(); // 在这里没有会阻塞        return getItem();    }    public void checkedIn(T x) {        if(releaseItem(x)) {//          System.out.println(releaseItem(x));            available.release();        }    }    private synchronized T getItem() {        for(int i=0;i<size;i++) {            if(!checkedOut[i]) {                checkedOut[i] = true;                return items.get(i);            }        }        return null; // Semaphore prevent reaching here    }    private synchronized boolean releaseItem(T item) {        int index = items.indexOf(item);        if(index == -1) return false; // Not in the list        if(checkedOut[index]) {            checkedOut[index] = false;            return true;        }        return false; // Wasn't checked out    }}

使用例子

public class Fat {    private volatile double d;    private static int counter = 0;    private final int id = counter++;    public Fat() {        // Expensive, interruptible operation:        for(int i=1;i<10000;i++) {            d += (Math.PI + Math.E) / (double) i;        }    }    public void operation() {        System.out.println(this);    }    public String toString() {        return "Fat id: " + id;    }}
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;    }    @Override    public void run() {        try {            T item = pool.checkedOut();            System.out.println(this + "checked out " + item);            TimeUnit.SECONDS.sleep(1);            System.out.println(this + "checking in " + item);            pool.checkedIn(item);            System.out.println(this + " end");        } catch (InterruptedException e) {            // Acceptable way to terminate        }    }    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> pool = new Pool<Fat>(Fat.class, SIZE);        ExecutorService exec = Executors.newCachedThreadPool();        for (int i = 0; i < SIZE; i++) {            exec.execute(new CheckoutTask<Fat>(pool));        }        System.out.println("All CheckoutTasks created");        List<Fat> list = new ArrayList<Fat>();        for (int i = 0; i < SIZE; i++) {            Fat f = pool.checkedOut();            System.out.print(i + ": main() thread checked out ");            f.operation();            list.add(f);        }        Future<?> blocked = exec.submit(new Runnable() {            @Override            public void run() {                // Semaphore prevents additional checkout,                try {                    // 前面的for循环中资源已被拿光,会在acquire()方法里阻塞                    pool.checkedOut();                    System.out.println("blocked!!!!"); // never reach here                } catch (InterruptedException e) {                    System.out.println("checkedOut() Interrupted");                }            }        });        TimeUnit.SECONDS.sleep(2);        // 如果注释掉,那么在下面的for循环释放资源后,blocked会获取到资源并接着输出“Blocked!!!”        blocked.cancel(true); // Break out of blocked call        System.out.println("Checking in objects in " + list);        for(Fat f : list) {            pool.checkedIn(f);;        }        for(Fat f : list) {            pool.checkedIn(f);        }        System.out.println("hello");        exec.shutdown(); // 如果注释掉,即使里面的线程已经执行完,程序也会在一段时间后才退出。因为查源码得出,Executors.newCachedThreadPool()会默认给它60秒的存活时间。    }}

Exchanger

Exchangers are typically used when one task is creating objects that are expensive to produce and another task is consuming those objects; this way, more object can be create at the same time as they are being consumed.

When you call the Exchanger.exchanger() method, it blocks until the partnwe tasks calls its exchanger() method, and when both exchanger() methods have completed, the List< T> has been swapped:

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> gen, List<T> holder) {        exchanger = exchg;        generator = gen;        this.holder = holder;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                for (int i = 0; i < ExchangerDemo.size; i++) {                    holder.add(generator.next());                }                System.out.println(this +" "+holder);                // Exchange full for empty:                holder = exchanger.exchange(holder);            }        } catch (InterruptedException e) {            // OK to terminate this way        }    }    public String toString() {        return "ExchangerProducer";    }}class ExchangerConsumer<T> implements Runnable {    private Exchanger<List<T>> exchanger;    private List<T> holder;    private volatile T value;    ExchangerConsumer(Exchanger<List<T>> ex, List<T> holder){        exchanger = ex;        this.holder = holder;    }    @Override    public void run() {        try {            while(!Thread.interrupted()) {                holder = exchanger.exchange(holder);                System.out.println(this +" "+holder);                for(T x : holder) {                    value = x;                    holder.remove(x);                }            }        } catch (InterruptedException e) {        }        System.out.println("Final value : " + value);    }    public String toString() {        return "ExchangerConsumer";    }}public class ExchangerDemo {    public static int size = 10;    static int delay = 2;    public static void main(String[] args) throws InterruptedException {        ExecutorService exec = Executors.newCachedThreadPool();        Exchanger<List<Fat>> xc = new Exchanger<List<Fat>>();        List<Fat>             producerList = new CopyOnWriteArrayList<Fat>(),            consumerList = new CopyOnWriteArrayList<Fat>();        exec.execute(new ExchangerProducer<Fat>(xc, BasicGenerator.create(Fat.class), producerList));        exec.execute(new ExchangerConsumer<Fat>(xc, consumerList));        TimeUnit.SECONDS.sleep(delay);        exec.shutdownNow();    }}

这里写图片描述

原创粉丝点击