java并发编程基础(四)-线程之间的协作

java并发编程基础(四)-线程之间的协作

本文为学习《thinking in java》第21章的相关笔记

Object.wait()和Object.notifyAll()

  1. 忙等待:占用CPU时间并且不断进行空循环

  2. wait()会在等待外部世界产生变化的时候将任务挂起,并且只有notify()或notifyAll()发生变化时,这个任务才会被唤醒

  3. 调用sleep()的时候锁并没有释放,调用yield()也是同样的情况

  4. 而一个任务调用wait的时候,线程的执行被挂起,对象上的锁被释放

  5. 只能在同步控制块中调用wait(),notify(),notifyAll()方法,即任务在调用这些方法前必须获得对象的锁

  6. 必须使用使用一个检查感兴趣的条件的while循环来包围wait(),因为有可能在A线程被唤醒前,B线程先被唤醒并且改变了条件,即本质上就是一直检查感兴趣的特定条件,并在条件不满足的情况下返回到wait()中

    while(expression)
    	wait();
    
  7. notify()和notifyAll()

    • 前者只会唤醒众多等待同一个锁任务中的一个任务,后者会唤醒等待的所有任务

使用显式的Lock和Condition对象

  1. 同上面提及到的方法作用类似,使用互斥并允许任务挂起的基本类为Condition,可以通过Condition来调用await()来挂起一个任务;当外部条件发生变化,意味着某一个任务应该继续执行,可以通过调用signal()来通知这一个任务,或者通过调用signalAll()来通知所有的任务

  2. 构造Condition的方法

    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    

    单个的Lock将产生一个Condition对象,这个对象被用来管理任务之间的通讯,但是Condition对象不包括任何有关处理状态的信息,因此我们需要管理额外的表示处理状态的信息,例如boolean hungry

生产者-消费者队列

  1. 使用阻塞队列BlockingQueue,该接口有大量的标准实现
    • LinkedBlockingQueue:无界队列
    • ArrayBlockingQueue:有界队列

死锁

  1. 简化版的哲学家进餐的死锁和避免死锁的方法(Jerry类中注释的代码)

    public class PhilosopherDeadLock {
    
        /**
         * 筷子类,代表临界资源
         * 筷子只有两个状态: 空闲和占用
         * 这两个状态之间的装换需要两个方法
         */
        static class Chopstick {
    
            private boolean taken;
    
            private Lock lock = new ReentrantLock();
    
            private Condition condition = lock.newCondition();
    
            public void take() {
                lock.lock();
                try {
                    while (taken) {
                        condition.await(); // 筷子已经被占用,只好等着,等别人通知
                    }
                    taken = true; // 表示自己拿了筷子
                } catch (InterruptedException ex) {
    
                } finally {
                    lock.unlock();
                }
            }
    
            public void drop() {
                lock.lock();
                try {
                    taken = false; // 放下筷子
                    condition.signalAll(); // 通知别人
                } finally {
                    lock.unlock();
                }
            }
        }
    
        static class Philosopher {
    
            protected Chopstick left;
    
            protected Chopstick right;
    
            public Philosopher(Chopstick left, Chopstick right) {
                this.left = left;
                this.right = right;
            }
    
        }
    
        static class Tom extends Philosopher implements Runnable {
    
            public Tom(Chopstick left, Chopstick right) {
                super(left, right);
            }
    
            @Override
            public void run() {
                try {
                    left.take();
                    TimeUnit.SECONDS.sleep(2); // 占用左筷子,睡眠足够的时间让对方拿
                    right.take();
                    System.out.println("tom is eating!");
                    right.drop();
                    left.drop();
                    System.out.println("tom finished eating!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        static class Jerry extends Philosopher implements Runnable {
    
            public Jerry(Chopstick left, Chopstick right) {
                super(left, right);
            }
    
            @Override
            public void run() {
                try {
                    right.take();
                    TimeUnit.SECONDS.sleep(2); 
                    left.take();
                    // 破坏死锁第四个条件循环等待
    //                left.take();
    //                TimeUnit.SECONDS.sleep(2); // 占用左筷子,睡眠足够的时间让对方拿
    //                right.take();
                    System.out.println("Jerry is eating!");
                    left.drop();
                    right.drop();
                    System.out.println("Jerry finished eating!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            Chopstick left = new Chopstick();
            Chopstick right = new Chopstick();
            ExecutorService service = Executors.newFixedThreadPool(2);
            service.execute(new Tom(left, right));
            service.execute(new Jerry(left, right));
            service.shutdown();
        }
    }
    
  2. 死锁发生的四个条件,缺一不可

    • 互斥:即资源不能同时共享,例如一根筷子不可以同时两人使用吧
    • 占有和等待:至少一个任务拿了一部分资源,并且等待着被其他任务占有的资源,比如Tom拿着左边的一根筷子,等待着Jerry拿着的右边的一根筷子
    • 资源不可抢占:例如Tom不能抢了Jerry手上的一根筷子
    • 环路(循环)等待:死锁发生时,系统一定有两个或者以上的进程形成一条环路,该环路中每一个进程(或线程)都在等待着下一个进程(或线程)占有的资源。例如Tom等着Jerry手上的右边一根,Jerry等着Tom手上的左边一根,形成环路。

你可能感兴趣的:(java)