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

来源:互联网 发布:php行业发展前景 编辑:程序博客网 时间:2024/04/27 15:36
新特征-线程池
 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 
从上面结果可以看到,程序并没结束,二是阻塞住了,原因是栈已经满了,后面追加元素的操作都被阻塞了。




原创粉丝点击