java并发(二)之五种线程池的用法和特征

五种线程池的使用:

线程池的好处:

a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。

newFixedThreadPool:

创建固定线程数量的,适用于负载均衡的服务器,使用了无界队列
特点:

  1. 线程池中的线程处于一定的量,可以很好的控制线程的并发量

  2. 线程可以重复被使用,在显示关闭之前,都将一直存在

  3. 超出一定量的线程被提交时候需在队列中等待

使用示例:

public class FixedThreadPool {
	public static void main(String[] args) {
		ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
		for (int i = 0; i < 10; i++) {//创建10个线程
			final int index = i;
			fixedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					try {
					
						System.out.println(index);
						//打印线程名便于分析
						System.out.println(Thread.currentThread().getName());
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
	}
}

执行结果:

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

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待,而且可重复用线程

newSingleThreadExecutor:

特点:

  1. 创建单个线程,需要顺序保证执行任务,不会有多个线程活动,使用无界队列
  2. 如果线程池中工作的线程死亡(发生异常),那么会重新启动一个线程代替它完成剩下的任务。

使用示例:

public class SingleThreadExecutor {
	public static void main(String[] args) {
		ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
		for (int i = 0; i < 10; i++) {
			final int index = i;
			singleThreadExecutor.execute(new Runnable() {
				@Override
				public void run() {
					try {
						System.out.println(index);
						System.out.println(Thread.currentThread().getName());
						if(index==3)//如果是i就不可以,必须是final。
							throw new RuntimeException();//这里扔出一个异常,然后后面的任务换了个线程继续执行。
						Thread.sleep(2000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
	}
}

结果:

0
pool-1-thread-1
1
pool-1-thread-1
2
pool-1-thread-1
3
pool-1-thread-1
Exception in thread "pool-1-thread-1" java.lang.RuntimeException
	at com.lt.ch1.fivethreadpool.SingleThreadExecutor$1.run(SingleThreadExecutor.java:18)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
4
pool-1-thread-2
5
pool-1-thread-2
6
pool-1-thread-2

当发生异常的时候,后面的线程会替代前一个线程完成未完成的任务。

newCachedThreadPool:

会根据需要来创建新线程,执行很多短期异步任务的程序,使用了SynchronousQueue
特征:

  1. 线程池中的数量无限制,可达到最大值(Integer.Max_value)
  2. 终止并从缓存中移除60秒没有未被使用的线程
  3. 当线程池中没有可用线程,会自动创建一个线程

使用示例:

public class CachedThreadPool {
	public static void main(String[] args) {
		ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
		for (int i = 0; i < 10; i++) {
			final int index = i;
			try {
				Thread.sleep(index * 100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			cachedThreadPool.execute(new Runnable() {
				@Override
				public void run() {
					System.out.println(index);
					System.out.println(Thread.currentThread().getName());
				}
			});
		}
	}
}

结果:

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-1-thread-1

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

newScheduledThreadPool:

特征:

  1. 创建一个定长线程池,支持定时及周期性任务执行 需要定期执行周期任务,不建议使用Timer,ScheduledExecutorService更安全,功能更强大。
  2. 可以包含多个线程的,线程执行周期任务,适度控制后台线程数量的时候
  3. 对比newSingleThreadScheduleExecutor:只包含一个线程,只需要单个线程执行周期任务,保证顺序的执行各个任务

方法说明:

  • Schedule:只执行一次,任务还可以延时执行,
  • ScheduleAtFixedRate:是以固定频率来执行线程任务,固定频率的含义就是可能设定的固定时间不足以完成线程任务,但是它不管,达到设定的延迟时间了就要执行下一次了。
  • scheduleWriteFixedDelay:不管线程任务的执行时间的,每次都要把任务执行完成后再延迟固定时间后再执行下一次。

在这里插入图片描述
使用示例:

public class ScheduleThreadPool {
	public static void main(String[] args) {
		ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3);
		for(int i=0;i<3;i++){
			scheduleThreadPool.schedule(new Runnable() {
				@Override
				public void run() {
					System.out.println("schedule延迟了2秒");
				}
			}, 2000, TimeUnit.MILLISECONDS);
		}
			scheduleThreadPool.scheduleAtFixedRate(new Runnable() {
				@Override
				public void run() {
					System.out.println("scheduleAtFixedRate延迟5秒执行,每3秒执行一次");
				}
			}, 5000, 3000, TimeUnit.MILLISECONDS);
	}
}

结果:

schedule延迟了2秒
schedule延迟了2秒
schedule延迟了2秒
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次
scheduleAtFixedRate延迟5秒执行,每3秒执行一次

它可安排在给定延迟后运行命令或者定期地执行。

newWorkStealingPool(JDK8以后):

特征:

  1. 基于ForkJoinPool实现。
  2. 会帮助其他线程完成任务
public class WorkStrealingPool {

    public static void main(String[] args) throws IOException {
        // 获取 CPU核心数
    	final int index = Runtime.getRuntime().availableProcessors();
    	System.out.println(index);
        ExecutorService newWorkStealingPool = Executors.newWorkStealingPool();
        //先让它运行一个任务比较长的线程。
        newWorkStealingPool.execute(new Runnable() {
    		@Override
			public void run() {
    			System.out.println(Thread.currentThread().getName()+"The One");
    			try {
					Thread.sleep(5000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
    	});
        //再执行几个任务时间比较短的线程
        for(int i=0;i<index;i++){
            newWorkStealingPool.execute(new Runnable() {
        		@Override
    			public void run() {
        			System.out.println(Thread.currentThread().getName()+"i");
        			try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
        		}
        	});
        }
        //对主线程阻塞,从而拿到输出信息。
        System.in.read();
    }
}

结果:

4
ForkJoinPool-1-worker-1The One
ForkJoinPool-1-worker-3i
ForkJoinPool-1-worker-2i
ForkJoinPool-1-worker-0i
ForkJoinPool-1-worker-3i

最后那个3i线程是最后帮The One这个线程,通过阻塞主线程打印出来的线程名。最后如果感兴趣可以了解下ForkJoinPool。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值