Thinking in Java 之 多线程 3

来源:互联网 发布:sql注入例题分析 编辑:程序博客网 时间:2024/06/03 17:43

Cooperation between tasks

wait() and notifyAll()

It’s important to understand that sleep() does not release the object lock when it is called, and neither does yield(). On the other hang, when a task enters a call to wait() inside a method, that thread’s execution is suspended, and the lock on that object is released.

One fairly unique aspect of await(), notify(), and notifyAll() is that these methods are part of the base class Object and not part of Thread. Although this seems a bit strange at first – to have something that’s exclusively for threading as part of the universal base class – it’s essential because these methods manipulate the lock that’s also part of every object.

You can ask another object to perform an operation that manipulates its own lock. To do this, you must first capture that object’s lock.

synchronized(x){    x.notifyAll();}
class Car {    private boolean waxOn = false;    public synchronized void waxed() {        waxOn = true;        notifyAll();    }    public synchronized void buffed() {        waxOn = false;        notifyAll();    }    public synchronized void waitForWaxing() throws InterruptedException {        while (waxOn == false) {            wait();        }    }    public synchronized void waitForBuffing() throws InterruptedException {        while (waxOn == true) {            wait();        }    }}class WaxOn implements Runnable {    private Car car;    public WaxOn(Car c) {        car = c;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (car) {                    car.waxed();                    System.out.println("Wax On!");                    TimeUnit.MILLISECONDS.sleep(200);                }                car.waitForBuffing();            }        } catch (InterruptedException e) {            System.out.println("Exiting via interrupt");        }        System.out.println("Ending Wax On task");    }}class WaxOff implements Runnable {    private Car car;    public WaxOff(Car c) {        car = c;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (car) {                    car.buffed();                    System.out.println("Wax Off!");                    TimeUnit.MILLISECONDS.sleep(200);                }                car.waitForWaxing();            }        } catch (InterruptedException e) {            System.out.println("Exiting via interrupt");        }        System.out.println("Ending Wax Off task");    }}public class WaxOMatic {    public static void main(String[] args) throws InterruptedException {        Car car = new Car();        ExecutorService exec = Executors.newCachedThreadPool();        exec.execute(new WaxOn(car));        exec.execute(new WaxOff(car));        TimeUnit.SECONDS.sleep(1);        exec.shutdownNow();//注意不是shutdown();    }}

这里写图片描述
You must surround a wait() with a while loop that checks the condition(s) of interest.

notify() vs notifyAll()

class Blocker {    synchronized void waitingCall() {        try {            while(!Thread.interrupted()) {                wait();// 这里有释放锁,所以其他线程进得来。                System.out.print(Thread.currentThread() + " ");            }        } catch (InterruptedException e) {            // Ok to exit this way        }    }    synchronized void prod() {        notify();    }    synchronized void prodAll() {        notifyAll();    }}class Task implements Runnable {    static Blocker blocker = new Blocker();    @Override    public void run() {        blocker.waitingCall();    }}class Task2 implements Runnable{    // A separate Blocker object:    static Blocker blocker = new Blocker();    @Override    public void run() {        blocker.waitingCall();    }}public class NotifyVsNotifyAll {    public static void main(String[] args) throws InterruptedException {        ExecutorService exec = Executors.newCachedThreadPool();        for(int i=0;i<5;i++) {            exec.execute(new Task());        }        exec.execute(new Task2());        Timer timer = new Timer();        timer.scheduleAtFixedRate(new TimerTask() {            boolean prod = true;            @Override            public void run() {                if(prod) {                    System.out.print("\nnotify() ");                    Task.blocker.prod();                    prod = false;                }else {                    System.out.print("\nnotifyAll() ");                    Task.blocker.prodAll();                    prod = true;                }            }        }, 400, 400);        TimeUnit.SECONDS.sleep(3);        timer.cancel();        System.out.println("\nTimer canceled");        TimeUnit.MILLISECONDS.sleep(500);        System.out.println("Task2.blocker.prodAll() ");        Task2.blocker.prodAll();        TimeUnit.MILLISECONDS.sleep(500);        exec.shutdownNow();    }}

这里写图片描述
Even though the timer is canceled, the first five tasks are still running and still blocked in their calls to Task.blocker.waitingCall().

Producers and consumers

class Meal {    private int orderNum;    public Meal(int orderNum) {        this.orderNum = orderNum;    }    public String toString() {        return "Meal " + orderNum;    }}class WaitPerson implements Runnable {    private Restaurant restaurant;    public WaitPerson(Restaurant restaurant) {        this.restaurant = restaurant;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (this) {                    while (restaurant.meal == null) {                        wait();// for the chef to produce a meal                    }                }                System.out.println("Waitperson got " + restaurant.meal);                synchronized (restaurant.chef) {                    restaurant.meal = null;                    // 持有chef的锁                    restaurant.chef.notifyAll();// Ready for another                }            }        } catch (InterruptedException e) {            System.out.println("WaitPerson interrupted");        }    }}class Chef implements Runnable {    private Restaurant restaurant;    private int count = 0;    public Chef(Restaurant restaurant) {        this.restaurant = restaurant;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                synchronized (this) {                    while (restaurant.meal != null) {                        wait(); // for the meal to be taken                    }                }                if (++count == 10) {                    System.out.println("Out of food, closing");                    restaurant.exec.shutdownNow();                }                synchronized (restaurant.waitPerson) {                    restaurant.meal = new Meal(count);                    restaurant.waitPerson.notifyAll();                }                TimeUnit.MILLISECONDS.sleep(100);//如果注释掉结果将不会显示最后一行。            }        } catch (InterruptedException e) {            System.out.println("Chef interrupted");        }    }}public class Restaurant {    Meal meal;    ExecutorService exec = Executors.newCachedThreadPool();    WaitPerson waitPerson = new WaitPerson(this);    Chef chef = new Chef(this);    public Restaurant() {        exec.execute(chef);        exec.execute(waitPerson);    }    public static void main(String[] args) {        new Restaurant();    }}

If you remove the call to sleep(), the task will get to the top of the run() loop and exit because of the Thread.interrupted() test, without throwing an exception.
这里写图片描述

The only safe approach is to always use the following idiom for a wait()(within proper synchronization, of course, and programming against the possibility of missed signals):

while(conditionIsNotMet){    wait();}

Using explicit Lock and Condition objects

class Car2 {    private ReentrantLock lock = new ReentrantLock();    private Condition condition = lock.newCondition();    private boolean waxOn = true;    public void waxed() {        lock.lock();        try {            waxOn = true;            System.out.println("waxing...");            condition.signal();        } finally {            lock.unlock();        }    }    public void buffed() {        lock.lock();        try {            waxOn = false;            System.out.println("buffing...");            condition.signal();        } finally {            lock.unlock();        }    }    public void waitForWaxed() throws InterruptedException {        lock.lock();        try {            while (waxOn == false) {                condition.await();            }         } finally {            lock.unlock();        }    }    public void waitForBuffed() throws InterruptedException {        lock.lock();        try {            while (waxOn == true) {                condition.await();            }         } finally {            lock.unlock();        }    }}class WaxOn2 implements Runnable {    private Car2 car2;    public WaxOn2(Car2 car2) {        this.car2 = car2;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                car2.waitForBuffed();                car2.waxed();            }        } catch (InterruptedException e) {            System.out.println("Ending WaxOn via interrupt");        }        System.out.println("Ending WaxOn task");    }}class WaxOff2 implements Runnable {    private Car2 car2;    public WaxOff2(Car2 car2) {        this.car2 = car2;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                car2.waitForWaxed();                car2.buffed();            }        } catch (InterruptedException e) {            System.out.println("Ending WaxOff via interrupt");        }        System.out.println("Ending Wax Off task");    }}public class WaxOMatic2 {    public static void main(String[] args) throws InterruptedException {        Car2 car = new Car2();        ExecutorService exec = Executors.newCachedThreadPool();        exec.execute(new WaxOn2(car));        exec.execute(new WaxOff2(car));        TimeUnit.MILLISECONDS.sleep(10);        exec.shutdownNow();    }}

Each call to lock() must immediately be followed by a try-finally clause to guarantee that unlocking happens in all cases. As with the built-in versions, a task must own the lock before it can call await(), signal() or signalAll().

如果catch后没有再抛异常,那么会执行后面的语句。

Producer-consumers and queues

The queues suspend a consumer task if that task tries to get an object from the queue and the queue is empty, and resume when more elements become available.

LinkedBlockingQueue<>());// Unlimited sizeArrayBlockingQueue<>(3));// Fixed sizeSynchronousQueue<>());// Size of 1

BlockingQueues of toast

class Toast {    public enum Status {        DRY, BUTTERED, JAMMED    }    private Status status = Status.DRY;    private final int id;    public Toast(int id) {        this.id = id;    }    public void butter() {        status = Status.BUTTERED;    }    public void Jam() {        status = Status.JAMMED;    }    public Status getStatus() {        return status;    }    public int getId() {        return id;    }    public String toString() {        return "Toast " + id + ": " + status;    }}class ToastQueue extends LinkedBlockingQueue<Toast> {}class Toaster implements Runnable {    private ToastQueue toastQueue;    private int count = 0;    private Random rand = new Random(47);    public Toaster(ToastQueue tq) {        toastQueue = tq;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                TimeUnit.MILLISECONDS.sleep(100 + rand.nextInt(500));                // Make toast                Toast t = new Toast(count++);                System.out.println(t);                // Insert into queue                toastQueue.put(t);            }        } catch (InterruptedException e) {            System.out.println("Toaster interrupted");        }        System.out.println("Toaster off");    }}class Butterer implements Runnable {    private ToastQueue dryQueue, butteredQueue;    public Butterer(ToastQueue dry, ToastQueue buttered) {        dryQueue = dry;        butteredQueue = buttered;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                // Blocks until next piece of toast is available:                Toast t = dryQueue.take();                t.butter();                System.out.println(t);                butteredQueue.put(t);            }        } catch (InterruptedException e) {            System.out.println("Butterer interrupted");        }        System.out.println("Butterer off");    }}class Jammer implements Runnable {    private ToastQueue butteredQueue, finishedQueue;    public Jammer(ToastQueue buttered, ToastQueue finished) {        butteredQueue = buttered;        finishedQueue = finished;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                // Block until next piece of toast is available:                Toast t = butteredQueue.take();                t.Jam();                System.out.println(t);                finishedQueue.put(t);            }        } catch (InterruptedException e) {            System.out.println("Jammer interrupted");        }        System.out.println("Jammer off");    }}class Eater implements Runnable {    private ToastQueue finishedQueue;    private int counter = 0;    public Eater(ToastQueue finished) {        finishedQueue = finished;    }    @Override    public void run() {        try {            while (!Thread.interrupted()) {                // Block until next piece of toast is available:                Toast t = finishedQueue.take();                // Verify that all pieces are getting jammed:                if (t.getId() != counter++ || t.getStatus() != Toast.Status.JAMMED) {                    System.out.println(">>>> Error: " + t);                    System.exit(1);                } else {                    System.out.println("Chomp! " + t);                }            }        } catch (InterruptedException e) {            System.out.println("Eater interrupted");        }        System.out.println("Eater off");    }}public class ToastOMatic {    public static void main(String[] args) throws InterruptedException {        ToastQueue dryQueue = new ToastQueue(),                butteredQueue = new ToastQueue(),                finishedQueue = new ToastQueue();        ExecutorService exec = Executors.newCachedThreadPool();        exec.execute(new Toaster(dryQueue));        exec.execute(new Butterer(dryQueue, butteredQueue));        exec.execute(new Jammer(butteredQueue, finishedQueue));        exec.execute(new Eater(finishedQueue));        TimeUnit.MILLISECONDS.sleep(2000);        exec.shutdownNow();    }}

Using pipes for I/O between tasks

class Sender implements Runnable {    private Random rand = new Random();    private PipedWriter out = new PipedWriter();    public PipedWriter getPipedWriter() {        return out;    }    @Override    public void run() {        try {            while (true) {                for (char c = 'A'; c < 'z'; c++) {                    out.write(c);                }                TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));            }        } catch (IOException e) {            System.out.println(e + " Sender writer exeception");        } catch (InterruptedException e) {            System.out.println(e + " Sender sleep interrupted");        }    }}class Receiver implements Runnable{    private PipedReader in;    public Receiver(Sender sender) throws IOException {        in = new PipedReader(sender.getPipedWriter());    }    @Override    public void run() {        try {            while(true) {                // Blocks until characters are there:                System.out.println("Read: " + (char)in.read());            }        }catch(IOException e) {            System.out.println(e + " Receiver read exceprion");        }    }}public class PipedIO {    public static void main(String[] args) throws Exception {        Sender sender = new Sender();        Receiver receiver = new Receiver(sender);        ExecutorService exec = Executors.newCachedThreadPool();        exec.execute(sender);        exec.execute(receiver);        TimeUnit.SECONDS.sleep(4);        exec.shutdownNow();    }}

When it does a read(), the pipe automatically blocks when there is no more data.

原创粉丝点击