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

来源:互联网 发布:linux交叉编译环境 编辑:程序博客网 时间:2024/06/05 13:32

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();    }}

打印结果
这里写图片描述
从打印结果来看,虽然线程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的方法
这里写图片描述
下面是一个常见的面试题
如何用两个线程依次打印出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();    }}

输出结果
这里写图片描述
这样就实现了顺序打印

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

0 0
原创粉丝点击