线程池

来源:互联网 发布:相机双重曝光软件 编辑:程序博客网 时间:2024/06/06 02:03
一、为什么使用线程池
线程要不停的创建和销毁,合理的使用线程池便可重复利用已创建的线程,以减少在创建线程和销毁线程上花费的时间和资源。

二、Executor、ExecutorService、AbstractExecutorService、ThreadPoolExecutor、Executors之间的关系


三、线程池的种类
为了更好的控制多线程,JDK提供了一套线程框架Executor,帮助开发人员有效的进行线程控制。它们都在java.util.concurrent包中,是JDK并发包的核心。其中有一个比较重要的类:Executors,它扮演这线程工厂的角色,我们
通过Executors可以创建特定功能的线程池。
newFixedThreadPool:线程池的数量是固定的,不会因为任务的增加而增加。当有一个任务提交时,如果线程池中空闲,则立即执行,如果没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。
newSingleThreadExecutor:只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个取代它,保证顺序执行,单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。
newCachedThreadPool:线程的数量是可以扩张或者收缩的,随着任务的增多而增多,减少而减少。特点就是在线程池空闲时,即线程池中没有可运行任务时,它会释放工作线程,从而释放工作线程所占用的资源。但是,但当出现新任务时,又要创建一新的工作线程,又要一定的系统开销。并且,在使用CachedThreadPool时,一定要注意控制任务的数量,否则,由于大量线程同时运行,很有会造成系统瘫痪,每一个空闲线程会在60秒后自动回收。
newScheduledThreadPool:创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。

示例代码:

public class ScheduledJob {public static void main(String args[]) throws Exception {Temp command = new Temp();ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);//启动时延迟五秒钟,然后一秒钟执行一次ScheduledFuture<?> scheduleTask = scheduler.scheduleWithFixedDelay(command, 5, 1, TimeUnit.SECONDS);}}class Temp extends Thread {public void run() {System.out.println("run");}}
自定义线程池
如果Executors工厂无法满足我们的需求,可以自己创建自定义线程池,其实Executors工厂类里面的创建
线程方法其内部均是用了ThreadPoolExecutor这个类,这个类可以自定义线程。构造方法如下:
 public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), defaultHandler);    }
这个构造方法对于队列是什么类型的比较关键:
1、在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数量小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,若线程数大于maximumPoolSize,则执行拒绝策略,或其他自定义方式。
2、在使用无界的任务队列时,LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新的任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
JDK拒绝策略:
AbortPolicy:直接抛出异常,系统正常工作;
CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务;
DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务;
DiscardPolicy:丢弃无法处理的任务,不给予任何处理。
如果需要自定义拒绝策略可以实现RejectedExecutionHandler接口。
四、Concurrent常用工具类
CyclicBarrier使用:
假设有只有的一个场景:每个线程代表一个跑步运动员,当运动员都准备好后,才一起出发,只要有一个人没有准备好,大家都等待。
示例代码:
public class UseCyclicBarrier {static class Runner implements Runnable {      private CyclicBarrier barrier;      private String name;          public Runner(CyclicBarrier barrier, String name) {          this.barrier = barrier;          this.name = name;      }      @Override      public void run() {          try {              Thread.sleep(1000 * (new Random()).nextInt(5));              System.out.println(name + " 准备OK.");              barrier.await();          } catch (InterruptedException e) {              e.printStackTrace();          } catch (BrokenBarrierException e) {              e.printStackTrace();          }          System.out.println(name + " Go!!");      }  }     public static void main(String[] args) throws IOException, InterruptedException {          CyclicBarrier barrier = new CyclicBarrier(3);  // 3         ExecutorService executor = Executors.newFixedThreadPool(3);                  executor.submit(new Thread(new Runner(barrier, "zhangsan")));          executor.submit(new Thread(new Runner(barrier, "lisi")));          executor.submit(new Thread(new Runner(barrier, "wangwu")));            executor.shutdown();      }  }
CountDownLacth使用:
经常用于监听某些初始化操作,等初始化执行完毕后,通知主线程继续工作。
示例代码:
public class UseCountDownLatch {public static void main(String[] args) {final CountDownLatch countDown = new CountDownLatch(2);Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {try {System.out.println("进入线程t1" + "等待其他线程处理完成...");countDown.await();System.out.println("t1线程继续执行...");} catch (InterruptedException e) {e.printStackTrace();}}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {try {System.out.println("t2线程进行初始化操作...");Thread.sleep(3000);System.out.println("t2线程初始化完毕,通知t1线程继续...");countDown.countDown();} catch (InterruptedException e) {e.printStackTrace();}}});Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {try {System.out.println("t3线程进行初始化操作...");Thread.sleep(4000);System.out.println("t3线程初始化完毕,通知t1线程继续...");countDown.countDown();} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();t2.start();t3.start();}}
注意:CyclicBarrier和CountDownLacth的区别:
CyclicBarrier是针对多个线程,CountDownLacth针对一个线程
Callable和Future使用:
jdk给我们一个实现的封装,使用简单。Future模式非常适合在处理耗时很长的业务逻辑时进行使用,可以有效的减小系统的响应时间,提高系统的吞吐量。
示例代码:
public class UseFuture implements Callable<String>{private String para;public UseFuture(String para){this.para = para;}/** * 这里是真实的业务逻辑,其执行可能很慢 */@Overridepublic String call() throws Exception {//模拟执行耗时Thread.sleep(5000);String result = this.para + "处理完成";return result;}//主控制函数public static void main(String[] args) throws Exception {String queryStr = "query";//构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类FutureTask<String> future = new FutureTask<String>(new UseFuture(queryStr));FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr));//创建一个固定线程的线程池且线程数为1,ExecutorService executor = Executors.newFixedThreadPool(2);//这里提交任务future,则开启线程执行RealData的call()方法执行//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值Future f1 = executor.submit(future);//单独启动一个线程去执行的Future f2 = executor.submit(future2);System.out.println("请求完毕");try {//这里可以做额外的数据操作,也就是主程序执行其他业务逻辑System.out.println("处理实际的业务逻辑...");Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}//调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待System.out.println("数据:" + future.get());System.out.println("数据:" + future2.get());executor.shutdown();}}
五、重入锁、读写锁
重入锁:在需要进行同步的代码部分加上锁定,但不要忘记最后一定要释放锁定,不然会造成锁永远无法释放,其他线程永远进不来的结果。
示例代码如下:
public class UseReentrantLock {private Lock lock = new ReentrantLock();public void method1(){try {lock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method1..");Thread.sleep(1000);System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method1..");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void method2(){try {lock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入method2..");Thread.sleep(2000);System.out.println("当前线程:" + Thread.currentThread().getName() + "退出method2..");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseReentrantLock ur = new UseReentrantLock();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {ur.method1();ur.method2();}}, "t1");t1.start();try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}//System.out.println(ur.lock.getQueueLength());}}
condition的使用:
public class UseCondition {private Lock lock = new ReentrantLock();private Condition condition = lock.newCondition();public void method1(){try {lock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入等待状态..");Thread.sleep(3000);System.out.println("当前线程:" + Thread.currentThread().getName() + "释放锁..");condition.await();// Object waitSystem.out.println("当前线程:" + Thread.currentThread().getName() +"继续执行...");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void method2(){try {lock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入..");Thread.sleep(3000);System.out.println("当前线程:" + Thread.currentThread().getName() + "发出唤醒..");condition.signal();//Object notify} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseCondition uc = new UseCondition();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {uc.method1();}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {uc.method2();}}, "t2");t1.start();t2.start();}}
多condition:
public class UseManyCondition {private ReentrantLock lock = new ReentrantLock();private Condition c1 = lock.newCondition();private Condition c2 = lock.newCondition();public void m1(){try {lock.lock();System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m1等待..");c1.await();System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m1继续..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m2(){try {lock.lock();System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m2等待..");c1.await();System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m2继续..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m3(){try {lock.lock();System.out.println("当前线程:" +Thread.currentThread().getName() + "进入方法m3等待..");c2.await();System.out.println("当前线程:" +Thread.currentThread().getName() + "方法m3继续..");} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m4(){try {lock.lock();System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");c1.signalAll();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void m5(){try {lock.lock();System.out.println("当前线程:" +Thread.currentThread().getName() + "唤醒..");c2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) {final UseManyCondition umc = new UseManyCondition();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {umc.m1();}},"t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {umc.m2();}},"t2");Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {umc.m3();}},"t3");Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {umc.m4();}},"t4");Thread t5 = new Thread(new Runnable() {@Overridepublic void run() {umc.m5();}},"t5");t1.start();// c1t2.start();// c1t3.start();// c2try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}t4.start();// c1try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}t5.start();// c2}}

公平锁和非公平锁:
Lock lock = new ReentrantLock(boolean isFair);
Lock用法:
tryLock():尝试获得锁,获得结果用true/false返回。在给定的时间内尝试获得锁,获得结果用true/false返回;
isFair():是否公平锁;
isLocked():是否锁定
getHoldCount():查询当前线程保持此锁的个数,也就是调用lock次数;
lockInterruptibly():优先享用中断的锁;
getQueueLength():返回正在等待获取此锁定的线程数;
getWaitQueueLength():返回等待与锁定相关的给定条件Condition的线程数;
hasQueuedThread(Thread thread): 查询指定的线程是否正在等待此锁;
hasQueuedThreads():查询是否有线程正在等待此锁;
hasWaiters():查询是否有线程正在等待与此锁定有关的condition条件。
读写锁:ReentrantReadWriteLock,其核心就是实现读写分离的锁,在高并发情况下,尤其是读多写少的情况下,性能要远高于重入锁。
口诀:读读共享,写写互斥,读写互斥。
示例代码如下:
public class UseReentrantReadWriteLock {private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();private ReadLock readLock = rwLock.readLock();private WriteLock writeLock = rwLock.writeLock();public void read(){try {readLock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");Thread.sleep(3000);System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");} catch (Exception e) {e.printStackTrace();} finally {readLock.unlock();}}public void write(){try {writeLock.lock();System.out.println("当前线程:" + Thread.currentThread().getName() + "进入...");Thread.sleep(3000);System.out.println("当前线程:" + Thread.currentThread().getName() + "退出...");} catch (Exception e) {e.printStackTrace();} finally {writeLock.unlock();}}public static void main(String[] args) {final UseReentrantReadWriteLock urrw = new UseReentrantReadWriteLock();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {urrw.read();}}, "t1");Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {urrw.read();}}, "t2");Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {urrw.write();}}, "t3");Thread t4 = new Thread(new Runnable() {@Overridepublic void run() {urrw.write();}}, "t4");//t1.start();//t2.start();//t1.start(); // R //t3.start(); // Wt3.start();t4.start();}}


原创粉丝点击