Java多线程探究-Lock对象锁条件变量

Lock锁的条件变量

设想这样的一种情况,现在有一个盘子,一个线程负责往盘子里放一个苹果,一个线程从盘子取一个苹果,如何保证线程A放一个苹果,线程B就把这个苹果取了,不会出现已经放了好几个了,线程B才一个一个的取,现在限定一个条件,盘子里每次只能放一个苹果,由于两个线程随机执行,不能保证线程A刚放了苹果,线程B就刚好取了。如果用通用的思想的话怎么做呢

应该是加条件判断,线程A每次放的时候,判断盘子里是否有苹果,如果有,则不做处理,线程B执行的时候判断是否有一个苹果,有的话,把这个苹果取了。苹果可以用数字表示,0表示盘子没有苹果,1表示有一个苹果,那么线程A做的事情就是加1,线程B做的就是减1,由于是对同一数据同时操作,必须要用锁保证数据安全
下面是基本的实现

public class ThreadDemo4 {
    public static void main(String[] args) {

        int[] apple = new int[]{0};
        Object obj = new Object();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    synchronized (obj){
                        if(apple[0] == 0) {
                            apple[0]++;
                            System.out.println("线程 "+Thread.currentThread().getId()+ "  放了一个苹果 "+apple[0]);
                        }else{
                            System.out.println("线程 "+Thread.currentThread().getId()+ " 放苹果,已经有苹果");
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    synchronized (obj){
                        if(apple[0] == 1) {
                            apple[0]--;
                            System.out.println("线程 "+Thread.currentThread().getId()+  " 取了一个苹果 "+apple[0]);
                        }else{
                            System.out.println("线程 "+Thread.currentThread().getId()+ " 取苹果,没有苹果...");
                        }
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }).start();

    }
}

打印结果
Java多线程探究-Lock对象锁条件变量_第1张图片
从打印结果来看,虽然线程12可以取到苹果,但是并不是都是在线程11刚放入就能取到,从第三行开始,线程12取了好几次都没有取到,这是因为CPU时间片此时被线程12占有,线程11没有执行的时间片,也就不能放苹果了,但是这样的话,会导致资源的消耗,明知没有苹果,那么线程12应该不用再取了,而是等待线程11放了一个苹果后再取,如果线程12发现没苹果了,就不再继续取了,而是等待线程11放苹果,放完之后,你给我发个通知,我再取,由于两个线程都是随机执行的,没法保证按顺序一放一取。怎么才能做到呢?这就是著名的多线程生产者和消费者问题

Java的Object提供了几个方法,用来做线程件的通信

public final native void notify(); 唤醒一个正在等待的线程,如果有多个等待的线程,那么会随机唤醒了一个,这些线程唤醒之后继续尝试获得锁的占有权,进入同步块

public final native void notifyAll(); 唤醒所有等待的线程

public final void wait() throws InterruptedException 释放锁,进入线程等待池,等待被别的线程notify

JDK1.5提供了一个对象锁的条件变量,类似Object的wait,nofity,notifyAll
类Condition的方法
Java多线程探究-Lock对象锁条件变量_第2张图片
下面是一个常见的面试题
如何用两个线程依次打印出100以内的奇数和偶数,一个线程打印奇数,另一个打印偶数,前面说了如果不用wait和notify的话,没法控制线程的顺序执行和条件执行,很可能一个线程打印了几次,另外一个才打印一次
现在使用Condition条件变量来实现

class Obj {
    public int state = 1;
}

class ThreadA implements Runnable {

    private int numA = 0;
    private Obj obj;
    private Lock lock;
    private Condition condition;

    public ThreadA(Obj obj, Lock lock, Condition condition) {
        this.obj = obj;
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        while (numA < 100) {
            lock.lock();
            try {
                if (obj.state != 1) {
                   condition.await();
                } else {
                    System.out.println(Thread.currentThread().getName() + " >>> " + numA);
                    Thread.sleep(100);
                    numA += 2;
                    obj.state = 2;
                    condition.signal();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }

        }
    }
}

class ThreadB implements Runnable {

    private int numB = 1;
    private Obj obj;
    private Lock lock;
    private Condition condition;

    public ThreadB(Obj obj, Lock lock, Condition condition) {
        this.obj = obj;
        this.lock = lock;
        this.condition = condition;
    }

    @Override
    public void run() {
        while (numB < 100) {
            lock.lock();
            try {
                if (obj.state != 2) {
                   condition.await();
                } else {
                    System.out.println(Thread.currentThread().getName() + " >>> " + numB);
                    Thread.sleep(100);
                    numB += 2;
                    obj.state = 1;
                    condition.signal();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }

    }
}

public class ThreadDemo2 {
    public static void main(String[] args) {

        Obj obj = new Obj();
        Lock lock = new ReentrantLock();
        Condition d1 = lock.newCondition();
        Runnable a = new ThreadA(obj, lock, d1);
        Runnable b = new ThreadB(obj, lock, d1);
        Thread t1 = new Thread(a, "Thread-A");
        Thread t2 = new Thread(b, "Thread-B");
        t1.start();
        t2.start();
    }
}

输出结果
Java多线程探究-Lock对象锁条件变量_第3张图片
这样就实现了顺序打印

总结:如果需要让多个线程按条件顺序执行,就需要使用锁对象的wait,notify方法
关于生成者消费者模型,看我的另外一篇博客

你可能感兴趣的:(Java)