一步一步剖析java多线程(待续三)

新特征-线程池
 Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利。为了编写高效稳定可靠的多线程程序,线程部分的新增内容显得尤为重要。有关Java5线程新特征的内容全部在java.util.concurrent下面,里面包含数目众多的接口和类,熟悉这部分API特征是一项艰难的学习过程。目前有关这方面的资料和书籍都少之又少,大所属介绍线程方面书籍还停留在java5之前的知识层面上。当然新特征对做多线程程序没有必须的关系,在java5之前通用可以写出很优秀的多线程程序。只是代价不一样而已。线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。在Java5之前,要实现一个线程池是相当有难度的,现在Java5为我们做好了一切,我们只需要按照提供的API来使用,即可享受线程池带来的极大便利。
 Java5的线程池分好多种:固定尺寸的线程池、可变尺寸连接池、。在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。
 一、固定大小的线程池
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 

/** 
* Java线程:线程池- 
* 
* @author TheDream 2012年12月4日
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建一个可重用固定线程数的线程池 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 
                Thread t1 = new MyThread(); 
                Thread t2 = new MyThread(); 
                Thread t3 = new MyThread(); 
                Thread t4 = new MyThread(); 
                Thread t5 = new MyThread(); 
                //将线程放入池中进行执行 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 
class MyThread extends Thread{ 
        @Override 
        public void run() { 
                System.out.println(Thread.currentThread().getName()+"正在执行。。。"); 
        } 
}
 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 

Process finished with exit code 0
二、单任务线程池
 在上例的基础上改一行创建pool对象的代码为:
  //创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 
                ExecutorService pool = Executors.newSingleThreadExecutor(); 
输出结果为:
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 

Process finished with exit code 0

对于以上两种连接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。
一旦池中有线程完毕,则排队等待的某个线程会入池执行。
三、可变尺寸的线程池
 
与上面的类似,只是改动下pool的创建方式:
 //创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 
                ExecutorService pool = Executors.newCachedThreadPool();             
pool-1-thread-5正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-4正在执行。。。 
pool-1-thread-3正在执行。。。 
pool-1-thread-2正在执行。。。 

Process finished with exit code 0


 
四、延迟连接池
import java.util.concurrent.Executors; 
import java.util.concurrent.ScheduledExecutorService; 
import java.util.concurrent.TimeUnit; 

/** 
* Java线程:线程池- 
* 
* @author  TheDream 2012年12月4日
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 
                ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); 
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 
                Thread t1 = new MyThread(); 
                Thread t2 = new MyThread(); 
                Thread t3 = new MyThread(); 
                Thread t4 = new MyThread(); 
                Thread t5 = new MyThread(); 
                //将线程放入池中进行执行 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                //使用延迟执行风格的方法 
                pool.schedule(t4, 10, TimeUnit.MILLISECONDS); 
                pool.schedule(t5, 10, TimeUnit.MILLISECONDS); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 

class MyThread extends Thread { 
        @Override 
        public void run() { 
                System.out.println(Thread.currentThread().getName() + "正在执行。。。"); 
        } 
}
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 

Process finished with exit code 0



五、单任务延迟连接池
在四代码基础上,做改动
 //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 
                ScheduledExecutorService pool = Executors.newSingleThreadScheduledExecutor();           
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-1正在执行。。。 

Process finished with exit code 0 


 
六、自定义线程池 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.ThreadPoolExecutor; 
import java.util.concurrent.TimeUnit; 

/** 
* Java线程:线程池-自定义线程池 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建等待队列 
                BlockingQueue<Runnable> bqueue = new ArrayBlockingQueue<Runnable>(20); 
                //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。 
                ThreadPoolExecutor pool = new ThreadPoolExecutor(2,3,2,TimeUnit.MILLISECONDS,bqueue); 
                //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口 
                Thread t1 = new MyThread(); 
                Thread t2 = new MyThread(); 
                Thread t3 = new MyThread(); 
                Thread t4 = new MyThread(); 
                Thread t5 = new MyThread(); 
                Thread t6 = new MyThread(); 
                Thread t7 = new MyThread(); 
                //将线程放入池中进行执行 
                pool.execute(t1); 
                pool.execute(t2); 
                pool.execute(t3); 
                pool.execute(t4); 
                pool.execute(t5); 
                pool.execute(t6); 
                pool.execute(t7); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 

class MyThread extends Thread { 
        @Override 
        public void run() { 
                System.out.println(Thread.currentThread().getName() + "正在执行。。。"); 
                try { 
                        Thread.sleep(100L); 
                } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                } 
        } 
}
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 
pool-1-thread-2正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 
pool-1-thread-1正在执行。。。 
pool-1-thread-2正在执行。。。 

Process finished with exit code 0 


创建自定义线程池的构造方法很多,本例中参数的含义如下:

ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue)
用给定的初始参数和默认的线程工厂及处理程序创建新的  ThreadPoolExecutor。使用  Executors 工厂方法之一比使用此通用构造方法方便得多。
参数:
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由  execute 方法提交的  Runnable 任务。
抛出:
IllegalArgumentException - 如果 corePoolSize 或 keepAliveTime 小于零,或者 maximumPoolSize 小于或等于零,或者 corePoolSize 大于 maximumPoolSize。
NullPointerException - 如果  workQueue 为 null
 自定义连接池稍微麻烦些,不过通过创建的ThreadPoolExecutor线程池对象,可以获取到当前线程池的尺寸、正在执行任务的线程数、工作队列等等。
 有关Java5线程池的内容到此就没有了,更多的内容还需要研读API来获取。
新特征-有返回值的线程
 在Java5之前,线程是没有返回值的,常常为了“有”返回值,破费周折,而且代码很不好写。或者干脆绕过这道坎,走别的路了。现在Java终于有可返回值的任务(也可以叫做线程)了。可返回值的任务必须实现Callable接口,类似的,无返回值的任务必须Runnable接口。执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了。
 下面是个很简单的例子:
import java.util.concurrent.*; 

/** 
* Java线程:有返回值的线程 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) throws ExecutionException, InterruptedException { 
                //创建一个线程池 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                //创建两个有返回值的任务 
                Callable c1 = new MyCallable("A"); 
                Callable c2 = new MyCallable("B"); 
                //执行任务并获取Future对象 
                Future f1 = pool.submit(c1); 
                Future f2 = pool.submit(c2); 
                //从Future对象上获取任务的返回值,并输出到控制台 
                System.out.println(">>>"+f1.get().toString()); 
                System.out.println(">>>"+f2.get().toString()); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 

class MyCallable implements Callable{ 
        private String oid; 

        MyCallable(String oid) { 
                this.oid = oid; 
        } 

        @Override 
        public Object call() throws Exception { 
                return oid+"任务返回的内容"; 
        } 
}
>>>A任务返回的内容 
>>>B任务返回的内容 

Process finished with exit code 0




 
 非常的简单,要深入了解还需要看Callable和Future接口的API啊。
新特征-锁(上)
 在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks 包下面,里面有三个重要的接口Condition、Lock、ReadWriteLock。
ConditionCondition 将 Object 监视器方法(waitnotify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。
LockLock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
ReadWriteLockReadWriteLock 维护了一对相关的锁定,一个用于只读操作,另一个用于写入操作。
 有关锁的介绍,API文档解说很多,看得很烦,还是看个例子再看文档比较容易理解。
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.Lock; 
import java.util.concurrent.locks.ReentrantLock; 

/** 
* Java线程:锁 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建并发访问的账户 
                MyCount myCount = new MyCount("95599200901215522", 10000); 
                //创建一个锁对象 
                Lock lock = new ReentrantLock(); 
                //创建一个线程池 
                ExecutorService pool = Executors.newCachedThreadPool(); 
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊 
                User u1 = new User("张三", myCount, -4000, lock); 
                User u2 = new User("张三他爹", myCount, 6000, lock); 
                User u3 = new User("张三他弟", myCount, -8000, lock); 
                User u4 = new User("张三", myCount, 800, lock); 
                //在线程池中执行各个用户的操作 
                pool.execute(u1); 
                pool.execute(u2); 
                pool.execute(u3); 
                pool.execute(u4); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 

/** 
* 信用卡的用户 
*/ 
class User implements Runnable { 
        private String name;                //用户名 
        private MyCount myCount;        //所要操作的账户 
        private int iocash;                 //操作的金额,当然有正负之分了 
        private Lock myLock;                //执行操作所需的锁对象 

        User(String name, MyCount myCount, int iocash, Lock myLock) { 
                this.name = name; 
                this.myCount = myCount; 
                this.iocash = iocash; 
                this.myLock = myLock; 
        } 

        public void run() { 
                //获取锁 
                myLock.lock(); 
                //执行现金业务 
                System.out.println(name + "正在操作" + myCount + "账户,金额为" + iocash + ",当前金额为" + myCount.getCash()); 
                myCount.setCash(myCount.getCash() + iocash); 
                System.out.println(name + "操作" + myCount + "账户成功,金额为" + iocash + ",当前金额为" + myCount.getCash()); 
                //释放锁,否则别的线程没有机会执行了 
                myLock.unlock(); 
        } 
} 

/** 
* 信用卡账户,可随意透支 
*/ 
class MyCount { 
        private String oid;         //账号 
        private int cash;             //账户余额 

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

        public String getOid() { 
                return oid; 
        } 

        public void setOid(String oid) { 
                this.oid = oid; 
        } 

        public int getCash() { 
                return cash; 
        } 

        public void setCash(int cash) { 
                this.cash = cash; 
        } 

        @Override 
        public String toString() { 
                return "MyCount{" + 
                                "oid='" + oid + '\'' + 
                                ", cash=" + cash + 
                                '}'; 
        } 
}
张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000 
张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000 
张三他爹正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为6000,当前金额为6000 
张三他爹操作MyCount{oid='95599200901215522', cash=12000}账户成功,金额为6000,当前金额为12000 
张三他弟正在操作MyCount{oid='95599200901215522', cash=12000}账户,金额为-8000,当前金额为12000 
张三他弟操作MyCount{oid='95599200901215522', cash=4000}账户成功,金额为-8000,当前金额为4000 
张三正在操作MyCount{oid='95599200901215522', cash=4000}账户,金额为800,当前金额为4000 
张三操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为800,当前金额为4800 

Process finished with exit code 0
从上面的输出可以看到,利用锁对象太方便了,比直接在某个不知情的对象上用锁清晰多了。 
但一定要注意的是,在获取了锁对象后,用完后应该尽快释放锁,以便别的等待该锁的线程有机会去执行。
新特征-锁(下)
 在上文中提到了Lock接口以及对象,使用它,很优雅的控制了竞争资源的安全访问,但是这种锁不区分读写,称这种锁为普通锁。为了提高性能,Java提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制,在一定程度上提高了程序的执行效率。Java中读写锁有个接口java.util.concurrent.locks.ReadWriteLock,也有具体的实现ReentrantReadWriteLock,详细的API可以查看JavaAPI文档。
下面这个例子是在文例子的基础上,将普通锁改为读写锁,并添加账户余额查询的功能,代码如下: 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.ReadWriteLock; 
import java.util.concurrent.locks.ReentrantReadWriteLock; 

/** 
* Java线程:锁 
* 
* @author TheDream 2012年12月4日
*/ 
public class Test { 
        public static void main(String[] args) { 
                //创建并发访问的账户 
                MyCount myCount = new MyCount("95599200901215522", 10000); 
                //创建一个锁对象 
                ReadWriteLock lock = new ReentrantReadWriteLock(false); 
                //创建一个线程池 
                ExecutorService pool = Executors.newFixedThreadPool(2); 
                //创建一些并发访问用户,一个信用卡,存的存,取的取,好热闹啊 
                User u1 = new User("张三", myCount, -4000, lock, false); 
                User u2 = new User("张三他爹", myCount, 6000, lock, false); 
                User u3 = new User("张三他弟", myCount, -8000, lock, false); 
                User u4 = new User("张三", myCount, 800, lock, false); 
                User u5 = new User("张三他爹", myCount, 0, lock, true); 
                //在线程池中执行各个用户的操作 
                pool.execute(u1); 
                pool.execute(u2); 
                pool.execute(u3); 
                pool.execute(u4); 
                pool.execute(u5); 
                //关闭线程池 
                pool.shutdown(); 
        } 
} 

/** 
* 信用卡的用户 
*/ 
class User implements Runnable { 
        private String name;                //用户名 
        private MyCount myCount;        //所要操作的账户 
        private int iocash;                 //操作的金额,当然有正负之分了 
        private ReadWriteLock myLock;                //执行操作所需的锁对象 
        private boolean ischeck;        //是否查询 

        User(String name, MyCount myCount, int iocash, ReadWriteLock myLock, boolean ischeck) { 
                this.name = name; 
                this.myCount = myCount; 
                this.iocash = iocash; 
                this.myLock = myLock; 
                this.ischeck = ischeck; 
        } 

        public void run() { 
                if (ischeck) { 
                        //获取读锁 
                        myLock.readLock().lock(); 
                        System.out.println("读:" + name + "正在查询" + myCount + "账户,当前金额为" + myCount.getCash()); 
                        //释放读锁 
                        myLock.readLock().unlock(); 
                } else { 
                        //获取写锁 
                        myLock.writeLock().lock(); 
                        //执行现金业务 
                        System.out.println("写:" + name + "正在操作" + myCount + "账户,金额为" + iocash + ",当前金额为" + myCount.getCash()); 
                        myCount.setCash(myCount.getCash() + iocash); 
                        System.out.println("写:" + name + "操作" + myCount + "账户成功,金额为" + iocash + ",当前金额为" + myCount.getCash()); 
                        //释放写锁 
                        myLock.writeLock().unlock(); 
                } 
        } 
} 

/** 
* 信用卡账户,可随意透支 
*/ 
class MyCount { 
        private String oid;         //账号 
        private int cash;             //账户余额 

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

        public String getOid() { 
                return oid; 
        } 

        public void setOid(String oid) { 
                this.oid = oid; 
        } 

        public int getCash() { 
                return cash; 
        } 

        public void setCash(int cash) { 
                this.cash = cash; 
        } 

        @Override 
        public String toString() { 
                return "MyCount{" + 
                                "oid='" + oid + '\'' + 
                                ", cash=" + cash + 
                                '}'; 
        } 
}
写:张三正在操作MyCount{oid='95599200901215522', cash=10000}账户,金额为-4000,当前金额为10000 
写:张三操作MyCount{oid='95599200901215522', cash=6000}账户成功,金额为-4000,当前金额为6000 
写:张三他弟正在操作MyCount{oid='95599200901215522', cash=6000}账户,金额为-8000,当前金额为6000 
写:张三他弟操作MyCount{oid='95599200901215522', cash=-2000}账户成功,金额为-8000,当前金额为-2000 
写:张三正在操作MyCount{oid='95599200901215522', cash=-2000}账户,金额为800,当前金额为-2000 
写:张三操作MyCount{oid='95599200901215522', cash=-1200}账户成功,金额为800,当前金额为-1200 
读:张三他爹正在查询MyCount{oid='95599200901215522', cash=-1200}账户,当前金额为-1200 
写:张三他爹正在操作MyCount{oid='95599200901215522', cash=-1200}账户,金额为6000,当前金额为-1200 
写:张三他爹操作MyCount{oid='95599200901215522', cash=4800}账户成功,金额为6000,当前金额为4800 

Process finished with exit code 0


   
在实际开发中,最好在能用读写锁的情况下使用读写锁,而不要用普通锁,以求更好的性能。
新特征-信号量
 Java的信号量实际上是一个功能完毕的计数器,对控制一定资源的消费与回收有着很重要的意义,信号量常常用于多线程的代码中,并能监控有多少数目的线程等待获取资源,并且通过信号量可以得知可用资源的数目等等,这里总是在强调“数目”二字,但不能指出来有哪些在等待,哪些资源可用。因此,本人认为,这个信号量类如果能返回数目,还能知道哪些对象在等待,哪些资源可使用,就非常完美了,仅仅拿到这些概括性的数字,对精确控制意义不是很大。目前还没想到更好的用法。
 下面是一个简单例子:
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.Semaphore; 

/** 
* Java线程:新特征-信号量 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) { 
                MyPool myPool = new MyPool(20); 
                //创建线程池 
                ExecutorService threadPool = Executors.newFixedThreadPool(2); 
                MyThread t1 = new MyThread("任务A", myPool, 3); 
                MyThread t2 = new MyThread("任务B", myPool, 12); 
                MyThread t3 = new MyThread("任务C", myPool, 7); 
                //在线程池中执行任务 
                threadPool.execute(t1); 
                threadPool.execute(t2); 
                threadPool.execute(t3); 
                //关闭池 
                threadPool.shutdown(); 
        } 
} 

/** 
* 一个池 
*/ 
class MyPool { 
        private Semaphore sp;     //池相关的信号量 

        /** 
         * 池的大小,这个大小会传递给信号量 
         * 
         * @param size 池的大小 
         */ 
        MyPool(int size) { 
                this.sp = new Semaphore(size); 
        } 

        public Semaphore getSp() { 
                return sp; 
        } 

        public void setSp(Semaphore sp) { 
                this.sp = sp; 
        } 
} 

class MyThread extends Thread { 
        private String threadname;            //线程的名称 
        private MyPool pool;                        //自定义池 
        private int x;                                    //申请信号量的大小 

        MyThread(String threadname, MyPool pool, int x) { 
                this.threadname = threadname; 
                this.pool = pool; 
                this.x = x; 
        } 

        public void run() { 
                try { 
                        //从此信号量获取给定数目的许可 
                        pool.getSp().acquire(x); 
                        //todo:也许这里可以做更复杂的业务 
                        System.out.println(threadname + "成功获取了" + x + "个许可!"); 
                } catch (InterruptedException e) { 
                        e.printStackTrace(); 
                } finally { 
                        //释放给定数目的许可,将其返回到信号量。 
                        pool.getSp().release(x); 
                        System.out.println(threadname + "释放了" + x + "个许可!"); 
                } 
        } 
}
任务B成功获取了12个许可! 
任务B释放了12个许可! 
任务A成功获取了3个许可! 
任务C成功获取了7个许可! 
任务C释放了7个许可! 
任务A释放了3个许可! 

Process finished with exit code 0
 从结果可以看出,信号量仅仅是对池资源进行监控,但不保证线程的安全,因此,在使用时候,应该自己控制线程的安全访问池资源。
新特征-阻塞队列
 阻塞队列是Java5线程新特征中的内容,Java定义了阻塞队列的接口java.util.concurrent.BlockingQueue,阻塞队列的概念是,一个指定长度的队列,如果队列满了,添加新元素的操作会被阻塞等待,直到有空位为止。同样,当队列为空时候,请求队列元素的操作同样会阻塞等待,直到有可用元素为止。
 有了这样的功能,就为多线程的排队等候的模型实现开辟了便捷通道,非常有用。
 java.util.concurrent.BlockingQueue继承了java.util.Queue接口,可以参看API文档。
 下面给出一个简单应用的例子:
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.ArrayBlockingQueue; 

/** 
* Java线程:新特征-阻塞队列 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) throws InterruptedException { 
                BlockingQueue bqueue = new ArrayBlockingQueue(20); 
                for (int i = 0; i < 30; i++) { 
                        //将指定元素添加到此队列中,如果没有可用空间,将一直等待(如果有必要)。 
                        bqueue.put(i); 
                        System.out.println("向阻塞队列中添加了元素:" + i); 
                } 
                System.out.println("程序到此运行结束,即将退出----"); 
        } 
}
输出结果:
向阻塞队列中添加了元素:0 
向阻塞队列中添加了元素:1 
向阻塞队列中添加了元素:2 
向阻塞队列中添加了元素:3 
向阻塞队列中添加了元素:4 
向阻塞队列中添加了元素:5 
向阻塞队列中添加了元素:6 
向阻塞队列中添加了元素:7 
向阻塞队列中添加了元素:8 
向阻塞队列中添加了元素:9 
向阻塞队列中添加了元素:10 
向阻塞队列中添加了元素:11 
向阻塞队列中添加了元素:12 
向阻塞队列中添加了元素:13 
向阻塞队列中添加了元素:14 
向阻塞队列中添加了元素:15 
向阻塞队列中添加了元素:16 
向阻塞队列中添加了元素:17 
向阻塞队列中添加了元素:18 
向阻塞队列中添加了元素:19 
可以看出,输出到元素19时候,就一直处于等待状态,因为队列满了,程序阻塞了。这里没有用多线程来演示,没有这个必要。
另外,阻塞队列还有更多实现类,用来满足各种复杂的需求:ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue, SynchronousQueue ,具体的API差别也很小。
新特征-阻塞栈
对于阻塞栈,与阻塞队列相似。不同点在于栈是“后入先出”的结构,每次操作的是栈顶,而队列是“先进先出”的结构,每次操作的是队列头。这里要特别说明一点的是,阻塞栈是Java6的新特征。、Java为阻塞栈定义了接口:java.util.concurrent.BlockingDeque,其实现类也比较多,具体可以查看JavaAPI文档。
下面看一个简单例子:
import java.util.concurrent.BlockingDeque; 
import java.util.concurrent.LinkedBlockingDeque; 

/** 
* Java线程:新特征-阻塞栈 
* 
* @author TheDream 2012年12月4日 
*/ 
public class Test { 
        public static void main(String[] args) throws InterruptedException { 
                BlockingDeque bDeque = new LinkedBlockingDeque(20); 
                for (int i = 0; i < 30; i++) { 
                        //将指定元素添加到此阻塞栈中,如果没有可用空间,将一直等待(如果有必要)。 
                        bDeque.putFirst(i); 
                        System.out.println("向阻塞栈中添加了元素:" + i); 
                } 
                System.out.println("程序到此运行结束,即将退出----"); 
        } 
}
输出结果:
向阻塞栈中添加了元素:0 
向阻塞栈中添加了元素:1 
向阻塞栈中添加了元素:2 
向阻塞栈中添加了元素:3 
向阻塞栈中添加了元素:4 
向阻塞栈中添加了元素:5 
向阻塞栈中添加了元素:6 
向阻塞栈中添加了元素:7 
向阻塞栈中添加了元素:8 
向阻塞栈中添加了元素:9 
向阻塞栈中添加了元素:10 
向阻塞栈中添加了元素:11 
向阻塞栈中添加了元素:12 
向阻塞栈中添加了元素:13 
向阻塞栈中添加了元素:14 
向阻塞栈中添加了元素:15 
向阻塞栈中添加了元素:16 
向阻塞栈中添加了元素:17 
向阻塞栈中添加了元素:18 
向阻塞栈中添加了元素:19 
从上面结果可以看到,程序并没结束,二是阻塞住了,原因是栈已经满了,后面追加元素的操作都被阻塞了。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值