条件变量是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线程:新特征-条件变量的更多相关文章

  1. (转)Java线程:新特征-条件变量

    Java线程:新特征-条件变量   条件变量是Java5线程中很重要的一个概念,顾名思义,条件变量就是表示条件的一种变量.但是必须说明,这里的条件是没有实际含义的,仅仅是个标记而已,并且条件的含义往往 ...

  2. Java线程新特征——Java并发库

    一.线程池   Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定 ...

  3. Java多线程-新特征-阻塞队列ArrayBlockingQueue

    阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素 ...

  4. Linux线程同步:条件变量

    条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用.使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化.一旦其它 ...

  5. 线程同步,条件变量pthread_cond_wait

    与互斥锁不同,条件变量是用来等待而不是用来上锁的.条件变量用来自动阻塞一个线程,直到某特殊情况发生为止.条件变量使我们可以睡眠等待某种条件出现.条件变量是利用线程间共享的全局变量进行同步的一种机制,主 ...

  6. (转)java线程安全问题之静态变量、实例变量、局部变量

    java多线程编程中,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的,如同<java并发编程实践>中所说: 写道 给线程安全下定义比较困难.存在很多种定义,如 ...

  7. java线程安全问题之静态变量、实例变量、局部变量

    java多线程编程中,存在很多线程安全问题,至于什么是线程安全呢,给出一个通俗易懂的概念还是蛮难的,如同<java并发编程实践>中所说: 写道 给线程安全下定义比较困难.存在很多种定义,如 ...

  8. java多线程技术之条件变量

    上一篇讲述了并发包下的Lock,Lock可以更好的解决线程同步问题,使之更面向对象,并且ReadWriteLock在处理同步时更强大,那么同样,线程间仅仅互斥是不够的,还需要通信,本篇的内容是基于上篇 ...

  9. [b0034] python 归纳 (十九)_线程同步_条件变量

    代码: # -*- coding: utf-8 -*- """ 学习线程同步,使用条件变量 逻辑: 生产消费者模型 一个有3个大小的产品库,一个生产者负责生产,一个消费者 ...

  10. linux线程同步(2)-条件变量

    一.概述                                                    上一篇,介绍了互斥量.条件变量与互斥量不同,互斥量是防止多线程同时访问共享的互斥变量来保 ...

随机推荐

  1. WiFi基础(二):最新WiFi信道、无线OSI模型与802.11b/g/n

    liwen01 2024.09.01 前言 最近十几年,通信技术发展迅猛,通信标准更新频繁,有的设备还在使用 802.11/b/g/n 协议,有的已支持到 WiFi6.WiFi7. 而国内有关无线 W ...

  2. 合合信息智能文字识别产品通过中国信通院“可信AI—OCR智能化服务”评估

    近年来,我国对数据的重视程度不断加强.2022年1月,国务院印发的<"十四五"数字经济发展规划>进一步提出,到2025年要初步建立数据要素市场体系,并对充分发挥数据要素 ...

  3. CA-TCC: 半监督时间序列分类的自监督对比表征学习《Self-supervised Contrastive Representation Learning for Semi-supervised Time-Series Classification》(时间序列、时序表征、时间和上下文对比、对比学习、自监督学习、半监督学习、TS-TCC的扩展版)

    现在是2023年11月27日,10:48,今天把这篇论文看了. 论文:Self-supervised Contrastive Representation Learning for Semi-supe ...

  4. WiFi基础(四):WiFi工作原理及WiFi接入过程

    liwen01 2024.09.16 前言 802.11 无线 WiFi 网有三类帧:数据帧.管理帧.控制帧.与有线网相比,无线 WiFi 网会复杂很多.大部分应用软件开发对 WiFi 的控制帧和管理 ...

  5. ++i与i++在效率上的细微差别

    在一些特定的使用中, i++ 可能将原值用中间量存起来以待使用,下面看相关程序的汇编代码(使用 gcc ). i++ 源程序: #include <stdio.h> int main(){ ...

  6. C#/.NET/.NET Core技术前沿周刊 | 第 8 期(2024年10.01-10.06)

    前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...

  7. Android MTP流程

    概要 本文的目的是介绍Android系统中MTP的一些相关知识.主要的内容包括:第1部分 MTP简介 对Mtp协议进行简单的介绍.第2部分 MTP框架 介绍Android系统下MTP的框架.第3部分 ...

  8. Eigen矩阵除法

    看了网上很多帖子,很多都没有说Eigen如何做矩阵除法.我这里补充一下.其他运算一般都可以查到: 对于Matrix来说,我们需要先将其转换成数组,因为Eigen矩阵不能做除法(很烦). 比如我们一个2 ...

  9. 墨天轮访谈 | 华为云温云博:从客户视角出发,GaussDB(for Redis)究竟“香”在哪里?

    分享嘉宾:温云博 华为云数据库NoSQL团队研发工程师 整理:墨天轮社区 导读 GaussDB(for Redis)采用云原生分布式架构,完全兼容Redis协议,支持丰富数据类型. 提供数据实时持久化 ...

  10. WinSCP 脚本实现将 jar 包一键上传 Ubuntu 并 docker 部署

    准备 首先,在 Ubuntu 写一个.sh 脚本用于自动更新 jar 包的 docker 容器和镜像,然后在 Windows 写一个.bat 脚本用于上传 jar 包并运行.sh 脚本. deploy ...