多线程与高并发学习笔记(四)

前述文章介绍了一些线程相关的知识点,今天我们讨论一下线程池,可以说线程池是我们编程人员必不可少的知识点之一;那什么是线程池呢? 简单理解其实就是装线程和任务的一个容器;有了它我们可以最大程度的减少频繁的创建和销毁线程所带来的资源开销,其实总的来说就是为了减小资源的浪费,使效率尽可能的最大化;通常我们常见的大概有两类线程池ThreadPollExecutor和ForkJoinPoll

线程池相关的几个类

在这里插入图片描述
Executor接口定义了execute()方法;ExecutorService里定义了线程生命周期相关的方法:

Callable与Future

说到线程池,那么我们先介绍下Callable与Future接口;说到线程的实现,我们随口而来的就是继承Thread、实现Runnable接口重写run()方法,但其实还有一种方法就是实现Callable接口重写call()方法
但此时或许大家会有所疑问,为什么已经有了Runnable这种实现方式了,还要设计出Callable呢?这是因为Callable是对Runnable的一种扩展,对Callable的调用可以有返回值;那返回值又该如何操作呢?这就又不得不说到Future了,Future正是用来接收Callable中的call()方法的返回值而出现的,可以说二者相辅相成;在多种线程池的实现里都用到了Callable和Future,因此我们需要对这两个接口有所了解。
Callable的使用

public class Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<String> c = new Callable() {
            @Override
            public String call() throws Exception {
                return "Hello Callable";
            }
        };

        ExecutorService service = Executors.newCachedThreadPool();
        Future<String> future = service.submit(c); //异步

        System.out.println(future.get());//阻塞 获得call()的返回值

        service.shutdown();
    }

}

FutureTask

FutureTask是一个集Future和Runnabla特性于一身的一个接口(实现了Future接口和Runnable接口)即可以用来接收一个线程将来的返回值,又可以当做任务来交给其他线程去执行。
示例代码

public class T06_00_Future {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		
		FutureTask<Integer> task = new FutureTask<>(()->{
			TimeUnit.MILLISECONDS.sleep(500);
			return 1000;
		}); //new Callable () { Integer call();}
		
		new Thread(task).start();
		
		System.out.println(task.get()); //阻塞
	}
}

CompletableFuture

这是一个多任务执行协调、规划和结果处理的类,管理多个Future
示例代码

/**
 * 假设你能够提供一个服务
 * 这个服务查询各大电商网站同一类产品的价格并汇总展示
 */
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

public class CompletableFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start, end;
		//单线程执行方式
        /*
        start = System.currentTimeMillis();
        priceOfTM();
        priceOfTB();
        priceOfJD();
        end = System.currentTimeMillis();
        System.out.println("use serial method call! " + (end - start));
        */

		//使用CompletableFuture
        start = System.currentTimeMillis();

        CompletableFuture<Double> futureTM = CompletableFuture.supplyAsync(()->priceOfTM());
        CompletableFuture<Double> futureTB = CompletableFuture.supplyAsync(()->priceOfTB());
        CompletableFuture<Double> futureJD = CompletableFuture.supplyAsync(()->priceOfJD());
		//将上面三个异步组织起来,只有当3个都完成时才会执行下面的代码
        CompletableFuture.allOf(futureTM, futureTB, futureJD).join();
		
		//另一种写法 将多条步骤统一
        /*
        CompletableFuture.supplyAsync(()->priceOfTM())    //异步调用priciOfTM()
                .thenApply(String::valueOf)    			  //执行完成调用String的valueOf()
                .thenApply(str-> "price " + str)		//然后拼接“price”+str
                .thenAccept(System.out::println);       //输出结果
		*/

        end = System.currentTimeMillis();
        System.out.println("use completable future! " + (end - start));

        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static double priceOfTM() {
        delay();
        return 1.00;
    }

    private static double priceOfTB() {
        delay();
        return 2.00;
    }

    private static double priceOfJD() {
        delay();
        return 3.00;
    }

    private static void delay() {
        int time = new Random().nextInt(500);
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.printf("After %s sleep!\n", time);
    }
}

ThreadPollExecutor

public class HelloThreadPool {

    static class Task implements Runnable {
        private int i;

        public Task(int i) {
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Task " + i);
            try {
                System.in.read();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return "Task{" +
                    "i=" + i +
                    '}';
        }
    }

    public static void main(String[] args) {
        ThreadPoolExecutor tpe = new ThreadPoolExecutor(2, 4,
                60, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(4),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.CallerRunsPolicy());

        for (int i = 0; i < 8; i++) {
            tpe.execute(new Task(i));
        }

        System.out.println(tpe.getQueue());

        tpe.execute(new Task(100));

        System.out.println(tpe.getQueue());

        tpe.shutdown();
    }
}

参数含义
1.核心线程数
2.最大线程数
3.非核心线程存活时间
4.时间单位
5.线程等待队列
6.线程的创建方式(名称、优先级等信息)
7.拒绝策略(无空闲线程和队列满时,其他任务到来时的拒绝策略)
注:刚开始线程池内线程数为0,当有任务进来时,才会创建线程;当任务达到核心线程数时,再有其他任务进来会进入等待队列,当队列已满核心线程也不空闲时,如果还没有达到最大线程数时,会再创建线程 …

常见的一些ThreadPollExecutor的线程池

JDK提供的一些线程池,底层还是ThreadPollExecutor,只是为了方便使用又做了一层包装。

SingleThreadPool

单个线程的线程池,可以保证线程的顺序执行;LinkedBlockingQueue实际上并不是无界阻塞队列,它的大小为Integer.MAX_VALUE。
在这里插入图片描述

CachedThreadPool

缓冲线程池,队列使用SynchronousQueue(队列大小为0,当某个线程想往队列放数据时,如果没有线程去拿,那么放数据的线程阻塞等待,直到有线程来拿;可这么理解,需要手把手交货),根据该队列的特点,每来一个任务,该线程池就会启动一个线程去执行直到Integer.MAX_VALUE;但一般情况下我们的机器支持不了这么多线程。使用于短时间内的高并发场景(但高并发也得有个限量)
在这里插入图片描述

FixedThreadPoll

固定线程池,使用于长时间高并发场景。
在这里插入图片描述

ScheduledThreadPool

适用于执行定时任务的线程池。
在这里插入图片描述

ForkJoinPoll类线程池

该类线程池相对于ThreadPollExecutor来说比较复杂;该类线程池设计的思想是将一个大任务分割为一个个的子任务在不同的线程里执行,然后汇总。
在这里插入图片描述
使用该类线程池,任务要定义为特定的类型 ForkJoinTask;常用的实现类有RecursiveAction(无返回值的)和RecursiveTask(有返回值的)

具体使用参考

public class ForkJoinPool {
	static int[] nums = new int[1000000];
	static final int MAX_NUM = 50000;
	static Random r = new Random();
	//单线程的执行
	static {
		for(int i=0; i<nums.length; i++) {
			nums[i] = r.nextInt(100);
		}
		
		System.out.println("---" + Arrays.stream(nums).sum()); //stream api
	}
	
	//分成一个个小任务执行
	static class AddTask extends RecursiveAction {

		int start, end;

		AddTask(int s, int e) {
			start = s;
			end = e;
		}

		@Override
		protected void compute() {

			if(end-start <= MAX_NUM) {
				long sum = 0L;
				for(int i=start; i<end; i++) sum += nums[i];
				System.out.println("from:" + start + " to:" + end + " = " + sum);
			} else {

				int middle = start + (end-start)/2;

				AddTask subTask1 = new AddTask(start, middle);
				AddTask subTask2 = new AddTask(middle, end);
				subTask1.fork();
				subTask2.fork();
			}


		}

	}
	//有返回值的实现
	static class AddTaskRet extends RecursiveTask<Long> {
		
		private static final long serialVersionUID = 1L;
		int start, end;
		
		AddTaskRet(int s, int e) {
			start = s;
			end = e;
		}

		@Override
		protected Long compute() {
			
			if(end-start <= MAX_NUM) {
				long sum = 0L;
				for(int i=start; i<end; i++) sum += nums[i];
				return sum;
			} 
			
			int middle = start + (end-start)/2;
			
			AddTaskRet subTask1 = new AddTaskRet(start, middle);
			AddTaskRet subTask2 = new AddTaskRet(middle, end);
			subTask1.fork();
			subTask2.fork();
			
			return subTask1.join() + subTask2.join();
		}
		
	}
	
	public static void main(String[] args) throws IOException {
		/*ForkJoinPool fjp = new ForkJoinPool();
		AddTask task = new AddTask(0, nums.length);
		fjp.execute(task);*/

		ForkJoinPool fjp = new ForkJoinPool();
		AddTaskRet task = new AddTaskRet(0, nums.length);
		fjp.execute(task);
		long result = task.join();
		System.out.println(result);
		
		//System.in.read();
		
	}
}

workStealingPoll

ForkJoinPoll的一个实现;该线程池的每个线程都维护一个线程等待队列,当有线程空闲而其他线程队列还有任务时,该线程会去其他队列里取出任务执行
在这里插入图片描述

在这里插入图片描述

具体使用参考

public class WorkStealingPool {
	public static void main(String[] args) throws IOException {
		ExecutorService service = Executors.newWorkStealingPool();
		System.out.println(Runtime.getRuntime().availableProcessors());

		service.execute(new R(1000));
		service.execute(new R(2000));
		service.execute(new R(2000));
		service.execute(new R(2000)); //daemon
		service.execute(new R(2000));
		
		//由于产生的是精灵线程(守护线程、后台线程),主线程不阻塞的话,看不到输出
		System.in.read(); 
	}

	static class R implements Runnable {

		int time;

		R(int t) {
			this.time = t;
		}

		@Override
		public void run() {
			
			try {
				TimeUnit.MILLISECONDS.sleep(time);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println(time  + " " + Thread.currentThread().getName());
			
		}

	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值