`
nowing
  • 浏览: 84756 次
  • 来自: ...
社区版块
存档分类
最新评论

Java线程:条件变量

阅读更多
http://lavasoft.blog.51cto.com/62575/222536

条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量。但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往通过代码来赋予其含义。

这里的条件和普通意义上的条件表达式有着天壤之别。

条件变量都实现了java.util.concurrent.locks.Condition接口,条件变量的实例化是通过一个Lock对象上调用newCondition()方法来获取的,这样,条件就和一个锁对象绑定起来了。因此,Java中的条件变量只能和锁配合使用,来控制并发程序访问竞争资源的安全。

条件变量的出现是为了更精细控制线程等待与唤醒,在Java5之前,线程的等待与唤醒依靠的是Object对象的wait()和notify()/notifyAll()方法,这样的处理不够精细。

而在Java5中,一个锁可以有多个条件,每个条件上可以有多个线程等待,通过调用await()方法,可以让线程在该条件下等待。当调用signalAll()方法,又可以唤醒该条件下的等待的线程。有关Condition接口的API可以具体参考JavaAPI文档。

条件变量比较抽象,原因是他不是自然语言中的条件概念,而是程序控制的一种手段。

下面以一个银行存取款的模拟程序为例来揭盖Java多线程条件变量的神秘面纱:

有一个账户,多个用户(线程)在同时操作这个账户,有的存款有的取款,存款随便存,取款有限制,不能透支,任何试图透支的操作都将等待里面有足够存款才执行操作。

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
* Java线程:条件变量
*
* @author leizhimin 2009-11-5 10:57:29
*/
public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/
class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/
class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}


/**
* 普通银行账户,不可透支
*/
class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额
        private Lock lock = new ReentrantLock();                //账户锁
        private Condition _save = lock.newCondition();    //存款条件
        private Condition _draw = lock.newCondition();    //取款条件

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public void saving(int x, String name) {
                lock.lock();                        //获取锁
                if (x > 0) {
                        cash += x;                    //存款
                        System.out.println(name + "存款" + x + ",当前余额为" + cash);
                }
                _draw.signalAll();            //唤醒所有等待线程。
                lock.unlock();                    //释放锁
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public void drawing(int x, String name) {
                lock.lock();                                 //获取锁
                try {
                        if (cash - x < 0) {
                                _draw.await();             //阻塞取款操作
                        } else {
                                cash -= x;                     //取款
                                System.out.println(name + "取款" + x + ",当前余额为" + cash);
                        }
                        _save.signalAll();             //唤醒所有存款操作
                } catch (InterruptedException e) {
                        e.printStackTrace();
                } finally {
                        lock.unlock();                     //释放锁
                }
        }
}


李四存款3600,当前余额为13600
张三存款2000,当前余额为15600
老张存款600,当前余额为16200
老牛取款1300,当前余额为14900
胖子取款800,当前余额为14100
王五取款2700,当前余额为11400

Process finished with exit code 0

假如我们不用锁和条件变量,如何实现此功能呢?下面是实现代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Java线程:不用条件变量
*
* @author leizhimin 2009-11-5 10:57:29
*/
public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/
class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/
class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}


/**
* 普通银行账户,不可透支
*/
class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public synchronized void saving(int x, String name) {
                if (x > 0) {
                        cash += x;                    //存款
                        System.out.println(name + "存款" + x + ",当前余额为" + cash);
                }
                notifyAll();            //唤醒所有等待线程。
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public synchronized void drawing(int x, String name) {
                if (cash - x < 0) {
                        try {
                                wait();
                        } catch (InterruptedException e1) {
                                e1.printStackTrace();
                        }
                } else {
                        cash -= x;                     //取款
                        System.out.println(name + "取款" + x + ",当前余额为" + cash);
                }
                notifyAll();             //唤醒所有存款操作
        }
}

输出结果为:
李四存款3600,当前余额为13600
王五取款2700,当前余额为10900
老张存款600,当前余额为11500
老牛取款1300,当前余额为10200
胖子取款800,当前余额为9400
张三存款2000,当前余额为11400

Process finished with exit code 0

结合先前同步代码知识,举一反三,将此例改为同步代码块来实现,代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Java线程:改为同步代码块
*
* @author leizhimin 2009-11-5 10:57:29
*/
public class Test {
        public static void main(String[] args) {
                //创建并发访问的账户
                MyCount myCount = new MyCount("95599200901215522", 10000);
                //创建一个线程池
                ExecutorService pool = Executors.newFixedThreadPool(2);
                Thread t1 = new SaveThread("张三", myCount, 2000);
                Thread t2 = new SaveThread("李四", myCount, 3600);
                Thread t3 = new DrawThread("王五", myCount, 2700);
                Thread t4 = new SaveThread("老张", myCount, 600);
                Thread t5 = new DrawThread("老牛", myCount, 1300);
                Thread t6 = new DrawThread("胖子", myCount, 800);
                //执行各个线程
                pool.execute(t1);
                pool.execute(t2);
                pool.execute(t3);
                pool.execute(t4);
                pool.execute(t5);
                pool.execute(t6);
                //关闭线程池
                pool.shutdown();
        }
}

/**
* 存款线程类
*/
class SaveThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        SaveThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.saving(x, name);
        }
}

/**
* 取款线程类
*/
class DrawThread extends Thread {
        private String name;                //操作人
        private MyCount myCount;        //账户
        private int x;                            //存款金额

        DrawThread(String name, MyCount myCount, int x) {
                this.name = name;
                this.myCount = myCount;
                this.x = x;
        }

        public void run() {
                myCount.drawing(x, name);
        }
}


/**
* 普通银行账户,不可透支
*/
class MyCount {
        private String oid;                         //账号
        private int cash;                             //账户余额

        MyCount(String oid, int cash) {
                this.oid = oid;
                this.cash = cash;
        }

        /**
         * 存款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public void saving(int x, String name) {
                if (x > 0) {
                        synchronized (this) {
                                cash += x;                    //存款
                                System.out.println(name + "存款" + x + ",当前余额为" + cash);
                                notifyAll();            //唤醒所有等待线程。
                        }
                }
        }

        /**
         * 取款
         *
         * @param x        操作金额
         * @param name 操作人
         */
        public synchronized void drawing(int x, String name) {
                synchronized (this) {
                        if (cash - x < 0) {
                                try {
                                        wait();
                                } catch (InterruptedException e1) {
                                        e1.printStackTrace();
                                }
                        } else {
                                cash -= x;                     //取款
                                System.out.println(name + "取款" + x + ",当前余额为" + cash);
                        }
                }
                notifyAll();             //唤醒所有存款操作
        }
}

李四存款3600,当前余额为13600
王五取款2700,当前余额为10900
老张存款600,当前余额为11500
老牛取款1300,当前余额为10200
胖子取款800,当前余额为9400
张三存款2000,当前余额为11400

Process finished with exit code 0

对比以上三种方式,从控制角度上讲,第一种最灵活,第二种代码最简单,第三种容易犯错。
分享到:
评论

相关推荐

    Java多线程编程总结

    Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 ...Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:大总结

    java线程详解

    Java线程:新特征-条件变量 Java线程:新特征-原子量 Java线程:新特征-障碍器 Java线程:深入ThreadLocal 一、标准例子 二、不用ThreadLocal 三、自己实现个ThreadLocal 四、透过现象看本质 Java线程:大...

    Java线程:volatile关键字

    Java 线程 volatile 关键字详解 Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile...volatile 变量是一种轻量级的同步机制,可以用于提供线程安全,但是需要满足一定的条件,并且不能用于所有的情况。

    java多线程_java多线程下变量共享_

    在Java中,线程共享变量可以通过两种方式实现:静态成员变量和实例成员变量。静态成员属于类,所有该类的实例都可以访问,因此在多线程环境下默认共享;实例成员变量则属于对象,每个线程都有自己独立的副本,除非...

    Java多线程编程经验

    #### 十九、Java线程:新特征-条件变量 条件变量是 `java.util.concurrent.locks` 包中的一个重要概念,它允许线程等待特定条件的发生,从而更好地协调线程间的协作。 #### 二十、Java线程:新特征-原子量 原子量...

    JAVA多线程并发操作全局变量

    NULL 博文链接:https://toknowme.iteye.com/blog/2212529

    java 多线程 加减变量

    java 多线程 加减变量

    多线程之条件变量

    在Java中,条件变量是`java.util.concurrent.locks.Condition`接口的实现,通常与锁(如`ReentrantLock`)一起使用。在C++的`std::condition_variable`库中,也有类似的实现。下面我们将深入探讨条件变量的概念、...

    Java 线程开发讲座

    - **线程变量**:使用`ThreadLocal`类可以为每个线程提供独立的变量副本,解决多线程之间的数据安全问题。 - **示例代码**: ```java public class ThreadLocalExample { private static final ThreadLocal...

    Java多线程

    #### 十九、Java线程:新特征-条件变量 条件变量允许线程等待某个条件成立后再继续执行,是实现高级同步控制的重要工具。 #### 二十、Java线程:新特征-原子量 原子量提供了一种无需锁即可进行原子更新的方式,...

    java多线程编程总结

    #### 三、Java线程:线程栈模型与线程的变量 - **线程栈模型** 每个线程都有自己的栈空间,用于存储方法调用的信息,如局部变量、方法参数等。线程栈随着方法的调用和返回而增长和缩小。 - **线程的变量** 线程...

    Java_多线程编程线程:大总结

    - **条件变量**:支持更复杂的线程同步场景。 - **原子量**:提供原子操作,避免了锁的使用,提高了性能。 - **障碍器**:协调一组线程的执行,例如等待所有线程完成初始化后再开始工作。 #### 六、总结 Java多...

    线程 JAVA java线程 java线程第3版 java线程第2版第3版合集

    电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...

    JAVA多线程总结

    **三、Java线程:线程栈模型与线程的变量** 每个线程有自己的调用栈,用于存储方法调用的状态。线程间的局部变量互不影响,但共享同一块内存区域(包括全局变量、静态变量)。 **四、Java线程:线程状态的转换** ...

    java ThreadLocal多线程专属的变量源码

    java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多线程专属的变量源码java ThreadLocal多...

    多线程线程变量赋值

    本话题聚焦于“多线程线程变量赋值”,讨论如何在不通过参数传递的情况下,为线程变量直接赋值。 首先,我们需要理解线程变量(Thread Local Variables)的概念。线程变量,也称为线程局部变量,是一种特殊类型的...

    JAVA多线程

    #### Java线程:线程栈模型与线程的变量 每个线程都有自己的执行栈,即线程栈,用于存储线程的局部变量和方法调用信息。线程栈随着线程的创建而创建,随着线程的结束而销毁。线程栈的大小可以在创建线程时指定。...

    Java多线程运算集合

    #### 十五、Java线程:新特征-条件变量 - **条件变量**: - 条件变量是 `java.util.concurrent.locks.Condition` 类提供的高级同步机制。 - 它提供了更加灵活的线程等待和唤醒机制,比传统的 `wait()` 和 `notify...

    java线程.pdf

    根据提供的信息,我们可以推断出这份文档主要关注的是Java线程的相关内容。下面将围绕“Java线程”这一主题展开详细的介绍与解释。 ### Java线程基础 在Java语言中,线程是程序执行流的基本单元。一个标准的Java...

Global site tag (gtag.js) - Google Analytics