Java线程:新特征-条件变量

Java线程:新特征-条件变量
 
条件变量是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
 
对比以上三种方式,从控制角度上讲,第一种最灵活,第二种代码最简单,第三种容易犯错。
相关文章
相关标签/搜索