首页 > 代码库 > Java线程详解(二)

Java线程详解(二)

Java线程:新特征-线程池

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。

在使用线程池之前,必须知道如何去创建一个线程池,在Java5中,需要了解的是java.util.concurrent.Executors类的API,这个类提供大量创建连接池的静态方法,是必须掌握的。

Java通过Executors提供四种线程池,分别为:
  newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行。
  newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

定长线程池的大小最好根据系统资源进行设置。如Runtime.getRuntime().availableProcessors()

一、 固定大小的线程池 newFixedThreadPool

 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。示例代码如下:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        // 创建一个可重用固定线程数的线程池
        ExecutorService pool = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 10; i++) {
            final int index = i;
            pool.execute(new Runnable() {
                public void run() {
                    try {
                        System.out.println(Thread.currentThread().getName() + "  " + index);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
        // 关闭线程池
        pool.shutdown();
    }
}

 执行结果:

pool-1-thread-3  2
pool-1-thread-2  1
pool-1-thread-1  0
pool-1-thread-1  3
pool-1-thread-2  5
pool-1-thread-3  4
pool-1-thread-2  7
pool-1-thread-1  6
pool-1-thread-3  8
pool-1-thread-1  9

因为线程池大小为3,每个任务输出index后sleep 2秒,所以每两秒打印3个数字。


二、单任务线程池 newSingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。在上例的基础上改一行创建pool对象的代码为:

   //创建一个使用单个 worker线程的 Executor,以无界队列方式来运行该线程。
   ExecutorService pool = Executors.newSingleThreadExecutor();

输出结果为:

pool-1-thread-1  0
pool-1-thread-1  1
pool-1-thread-1  2
pool-1-thread-1  3
pool-1-thread-1  4
pool-1-thread-1  5
pool-1-thread-1  6
pool-1-thread-1  7
pool-1-thread-1  8
pool-1-thread-1  9

 结果依次输出,相当于顺序执行各个任务。


对于以上两种连接池,大小都是固定的,当要加入的池的线程(或者任务)超过池最大尺寸时候,则入此线程池需要排队等待。

一旦池中有线程完毕,则排队等待的某个线程会入池执行。

 

三、可变尺寸的线程池

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。与上面的类似,只是改动下pool的创建方式:

  // 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadPoolExecutorTest {
    public static void main(String[] args) {
        // 创建一个可重用固定线程数的线程池
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            final int index = i;
            pool.execute(new Runnable() {
                public void run() {
                    System.out.println(Thread.currentThread().getName() + "  " + index);
                }
            });
        }
        // 关闭线程池
        pool.shutdown();
    }
}

 

pool-1-thread-1  0
pool-1-thread-6  5
pool-1-thread-5  4
pool-1-thread-10  9
pool-1-thread-4  3
pool-1-thread-3  2
pool-1-thread-2  1
pool-1-thread-9  8
pool-1-thread-8  7
pool-1-thread-7  6


四、延迟连接池

 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例代码如下:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

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

public class TestThread {
    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, 3, TimeUnit.SECONDS);
        pool.schedule(t5, 3, TimeUnit.SECONDS);
        // 关闭线程池
        pool.shutdown();
    }
}

 执行结果:

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

 表示t4,t5延迟3秒执行。


定期执行示例代码如下:

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

public class TestThread {
    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.scheduleAtFixedRate(t4, 1, 3, TimeUnit.SECONDS);
        pool.scheduleAtFixedRate(t5, 1, 3, TimeUnit.SECONDS);

    }
}

执行结果:

pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。
pool-1-thread-2正在执行。。。
pool-1-thread-1正在执行。。。

表示t4,t5延迟1秒后每3秒执行一次。


五、单任务延迟连接池

 

在四代码基础上,做改动

   //创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
   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
线程:线程池-自定义线程池
*/

publicclass 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
        
publicvoid 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来获取.

本文出自 “ciyo技术分享” 博客,请务必保留此出处http://ciyorecord.blog.51cto.com/6010867/1939535

Java线程详解(二)