Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包。

( 1 ) Java的线程并发库介绍
Java5的多线程并有两个大发库在java.util.concurrent包及子包中,子包主要的包有一下两个
1) java.util.concurrent包 (多线程并发库)
➢ java.util.concurrent 包含许多线程安全、测试良好、高性能的并发构建块。不客气地说,创建
java.util.concurrent 的目的就是要实现 Collection 框架对数据结构所执行的并发操作。通过提供一组可靠的、
高性能并发构建块,开发人员可以提高并发类的线程安全、可伸缩性、性能、可读性和可靠性,后面、我们会做介
绍。
➢ 如果一些类名看起来相似,可能是因为 java.util.concurrent 中的许多概念源自 Doug Lea 的
util.concurrent 库。

2) java.util.concurrent.atomic包 (多线程的原子性操作提供的工具类)
➢ 查看atomic包文档页下面的介绍,它可以对多线程的基本数据、数组中的基本数据和对象中的基本数据
进行多线程的操作(AtomicInteger、AtomicIntegerArray、AtomicIntegerFieldUpDater…)
➢ 通过如下两个方法快速理解atomic包的意义:
 AtomicInteger类的 boolean compareAndSet(expectedValue, updateValue);
 AtomicIntegerArray 类的int addAndGet(int i, int delta);
➢ 顺带解释volatile类型的作用,需要查看java语言规范。
 volatile 修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的值。(具有可见性)
 volatile没有原子性。

3) java.util.concurrent.lock包 (多线程的锁机制)
为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。该框架允许更灵活地使用锁和条件。
本包下有三大接口,下面简单介绍下:
➢ Lock 接口:支持那些语义不同(重入、公平等)的锁规则,可以在非阻塞式结构的上下文(包括 hand
over-hand 和锁重排算法)中使用这些规则。主要的实现是 ReentrantLock。
➢ ReadWriteLock 接口:以类似方式定义了一些读取者可以共享而写入者独占的锁。此包只提供了一个实
现,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、
适用于非标准要求的实现。
➢ Condition 接口:描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的
隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关
联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

( 2 ) Java的并发库入门
下面将分别介绍java.util.concurrent包下的常用类的使用。

1) java.util.concurrent包
java.util.concurrent包描述:
在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能
的类。此包下有一些组件,其中包括:
 执行程序(线程池)
 并发队列
 同步器
 并发Collocation
下面我们将java.util.concurrent包下的组件逐一简单介绍:

A. 执行程序
➢ Executors线程池工厂类
首次我们来说下线程池的作用:
线程池作用就是限制系统中执行线程的数量。
根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了
造成系统拥挤效率不高。用线程池控制线程数量,其他线程 排队等候。一个任务执行完毕,再从队列的中取
最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如
果线程 池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:
 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
 可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务
器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)

Executors详解:
Java里面线程池的顶级接口是 Executor,但是严格意义上讲Executor 并不是一个线程池,而只是一个
执行线程的工具。真正的线程池接口是 ExecutorService。ThreadPoolExecutor 是 Executors 类的底层实
现。我们先介绍下Executors。
线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线
程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反
复创建线程对象所带来的性能开销,节省了系统的资源。

//创建固定大小的线程池   
ExecutorService fPool = Executors.newFixedThreadPool(3);  
  //创建缓存大小的线程池   
ExecutorService cPool = Executors.newCachedThreadPool(); 
  //创建单一的线程池 
ExecutorService sPool = Executors.newSingleThreadExecutor();

下面我们通过简单示例来分别说明:
 固定大小连接池

import java.util.concurrent.Executors;     
import java.util.concurrent.ExecutorService;
     /**  
     * Java 线程:线程池-
     *
     * @author Administrator 2009-11-4 23:30:44
     */
     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-2 正在执行。。。     
pool-1-thread-1 正在执行。。。    
pool-1-thread-2 正在执行。。。

从上面的运行来看,我们 Thread 类都是在线程池中运行的,线程池在执行 execute 方法来执行 Thread 类中的run方法。不管execute执行几次,线程池始终都会使用2个线程来处理。不会再去创建出其他线程来处理run方法执行。这就是固定大小线程池。

 单任务连接池 

我们将上面的代码 

//创建一个可重用固定线程数的线程池
ExecutorService pool = Executors.newFixedThreadPool(2);   
    改为:    
 //创建一个使用单个 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 正在执行。。。

运行结果看出,单任务线程池在执行execute方法来执行Thread类中的run方法。不管execute执行几次,线程池
始终都会使用单个线程来处理。
补充:在java的多线程中,一但线程关闭,就会成为死线程。关闭后死线程就没有办法在启动了。再次启动就会出现
异常信息:Exception in thread "main" java.lang.IllegalThreadStateException。那么如何解决这个问题呢?
我们这里就可以使用Executors.newSingleThreadExecutor()来再次启动一个线程。

 可变连接池 

//创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。 
ExecutorService pool = Executors.newCachedThreadPool();  
 
运行结果: 
pool-1-thread-5 正在执行。。。     
pool-1-thread-1 正在执行。。。     
pool-1-thread-4 正在执行。。。     
pool-1-thread-3 正在执行。。。     
pool-1-thread-2 正在执行。。。

运行结果看出,可变任务线程池在执行 execute 方法来执行 Thread 类中的 run 方法。这里 execute 执行多次,线程池就会创建出多个线程来处理 Thread 类中 run 方法。所有我们看到连接池会根据执行的情况,在程序运行时创建多个线程来处理,这里就是可变连接池的特点。
那么在上面的三种创建方式,Executors 还可以在执行某个线程时,定时操作。那么下面我们通过代码简单演示下。

 延迟连接池 

import java.util.concurrent.Executors;
     import java.util.concurrent.ScheduledExecutorService;
     import java.util.concurrent.TimeUnit;
     /**  
      * Java 线程:线程池-
    * 
   * @author Administrator 2009-11-4 23:30:44
    */
     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);
  //t4 和 t5 在 10 秒后执行
   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 正在执行。。。

➢ ExecutorService执行器服务
java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务。因此一
个 ExecutorService 很类似于一个线程池。实际上,存在于 java.util.concurrent 包里的 ExecutorService 实
现就是一个线程池实现。

以下是一个简单的 ExecutorService 例子: 

//线程工厂类创建出线程池 
ExecutorService executorService = Executors.newFixedThreadPool(10);     
//执行一个线程任务
 executorService.execute(new Runnable() {
       public void run() {
           System.out.println("Asynchronous task");
       }   
});     
//线程池关闭 
executorService.shutdown();

上面代码首先使用 newFixedThreadPool() 工厂方法创建一个 ExecutorService。这里创建了一个十个线程执行任务的线程池。然后,将一个 Runnable 接口的匿名实现类传递给 execute() 方法。这将导致 ExecutorService 中的某个线程执行该 Runnable。这里可以看成一个任务分派,示例代码中的任务分派我们可以理解为:

一个线程将一个任务委派给一个 ExecutorService 去异步执行。
一旦该线程将任务委派给 ExecutorService,该线程将继续它自己的执行,独立于该任务的执行

ExecutorService实现:
既然 ExecutorService 是个接口,如果你想用它的话就得去使用它的实现类之一。
java.util.concurrent 包提供了 ExecutorService 接口的以下实现类:
 ThreadPoolExecutor
 ScheduledThreadPoolExecutor
ExecutorService创建:
ExecutorService 的创建依赖于你使用的具体实现。但是你也可以使用 Executors 工厂类来创建
ExecutorService 实例。代码示例:

ExecutorService executorService1 = Executors.newSingleThreadExecutor();
  //之前 Executors 已介绍 
ExecutorService executorService2 = Executors.newFixedThreadPool(10);   
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

ExecutorService使用:
有几种不同的方式来将任务委托给 ExecutorService 去执行:
 execute(Runnable)
 submit(Runnable)
 submit(Callable)
 invokeAny(…)
 invokeAll(…)
接下来我们挨个看一下这些方法。
✓ execute(Runnable)
execute(Runnable) 方法要求一个 java.lang.Runnable 对象,然后对它进行异步执行。以下是使用
ExecutorService 执行一个 Runnable 的示例:

//从 Executors 中获得 ExecutorService  
ExecutorService executorService = Executors.newSingleThreadExecutor();   
//执行 ExecutorService 中的方法 
executorService.execute(new Runnable() { 
      public void run() {
           System.out.println("Asynchronous task");
       }
   });   
//线程池关闭
  executorService.shutdown();

特点:没有办法得知被执行的 Runnable 的执行结果。如果有需要的话你得使用一个 Callable(以下将做介绍)。

✓ submit(Runnable)
submit(Runnable) 方法也要求一个 Runnable 实现类,但它返回一个 Future 对象。这个 Future 对象可
以用来检查 Runnable 是否已经执行完毕。以下是 ExecutorService submit() 示例:

//从 Executors 中获得 ExecutorService 
 ExecutorService executorService = Executors.newSingleThreadExecutor();   
 
Future future = executorService.submit(new Runnable() {
       public void run() {
           System.out.println("Asynchronous task");
       }   
});     
future.get();  //获得执行完 run 方法后的返回值,这里使用的 Runnable,所以这里没有返回值,返回的是 null。 
executorService.shutdown();

✓ submit(Runnable)
submit(Callable) 方法类似于 submit(Runnable) 方法,除了它所要求的参数类型之外。Callable 实例除了它的 call() 方法能够返回一个结果之外和一个 Runnable 很相像。Runnable.run() 不能够返回一个结果。Callable 的结果可以通过 submit(Callable) 方法返回的 Future 对象进行获取。
以下是一个 ExecutorService Callable 示例:

 

//从 Executors 中获得 ExecutorService  
ExecutorService executorService = Executors.newSingleThreadExecutor();   
 
Future future = executorService.submit(new Callable(){
       public Object call() throws Exception {           System.out.println("Asynchronous Callable");
           return "Callable Result"; 
      }
   });
System.out.println("future.get() = " + future.get()); 
executorService.shutdown(); 
 
输出:
 Asynchronous Callable 
future.get() = Callable Result

Executors关闭:
使用shutdown和shutdownNow可以关闭线程池
两者的区别:shutdown只是将空闲的线程interrupt() 了,shutdown()之前提交的任务可以继续执行直到结束。
shutdownNow 是 interrupt 所有线程, 因此大部分线程将立刻被中断。之所以是大部分,而不是全部 ,是因为interrupt()方法能力有限。