java多线程编程历史演变【截止到jdk8】

最近看了咕泡学视频教程,这里算是做一个记录。

一、JDK1.5之前时代

创建线程的方式

继承thread
package study.java5;

/**
 * @Auther: zhw
 * @Date: 2018/10/28 11:16
 * @Description: 1.5多线程方式1
 */
public class ThreadDemo {

    public static void main(String[] args) {

        Demo1 demo1 = new Demo1();

        demo1.start();
    }

}


class Demo1 extends Thread
{
    @Override
    public void run() {
        System.out.println("线程开始执行");
    }
}
实现Runnable接口
package study.java5;

/**
 * @Auther: zhw
 * @Date: 2018/10/28 12:29
 * @Description: //TODO
 */
public class RunnableDemo {

    public static void main(String[] args) {

        Demo2 demo2 =new Demo2();

        Thread thread = new Thread(demo2);

        thread.start();
    }

}


class Demo2 implements Runnable{

    @Override
    public void run() {
        System.out.println("线程开始执行");
    }
}

1.5总结:

1、缺少线程池的管理
2、想获取线程执行的结果非常麻烦

二、JDK1.5时代

2.1、Executors

这个可以算是线程的管理,其中包含各种线程服务
ExecutorService:最常见的执行者。生命周期
ScheduledExecutorService:带有任务调度的执行者
ThreadFactory:线程工厂,用于创建新线程

2.2、ExecutorService线程服务

public class ExecutorsExecutorService {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newFixedThreadPool(2);

        Future<String> future = executorService.submit(new Callable<String>(){

            @Override
            public String call() throws Exception {
                return "first executorService";
            }
        });

        //do other thing

        String result = "";
        try {
            result = future.get();
        }catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(result);
    }
}

如上面例子,创建了一个数量为2的线程池,返回了ExecutorServie,ExecutorServie调用了submit方法产生了一个Future结果.
区分Callable和Future

Callable:是源头,是任务,是产生结果的。
Future:Future是用于获取结果的。由于Callable的调用是异步的,也就是说结果不知道什么时候产生,也就是这个单词的意思“未来”,这是个未来结果。

详解Future

  1. get:这个方法会一直等待结果的完成,然后获取结果,这个方法还支持等待的最大时间,超过这个时间就不继续等待。
  2. isDone:判断结果是否完成
  3. cancel:取消任务,未开始或已完成返回false,参数表示是否中断执行中的线程 更详细的描述
  4. isCancelled:是否取消了。

除了submit还有invokeAll(也就是批量)、invokeAny(这个有点用)、execute,看jdk源码或者名字都很好理解
其他服务就不介绍了。

在1.5中已经出现了线程池、线程管理、获取线程的执行结果,已经很方便了,所以jdk1.5是个经典。

缺点:

多个线程之间交互需要提高(但是还是不影响1.5是经典)

三、JDK1.7

ForkJoinPool

背景:ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来即可。这种思想值得学习。
这里特别推荐一篇文章,写的非常好=====>精彩博文

主要有两个类:RecursiveAction和RecursiveTask

3.1 RecursiveAction:递归没有返回结果的任务

下面演示一个打印任务

public static void main(String[] args) throws Exception{
        PrintTask task = new PrintTask(0, 100);
        //创建实例,并执行分割任务
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(task);
        //线程阻塞,等待所有任务完成
        pool.shutdown();
        pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        System.out.println("线程池已经关闭");
    }

class PrintTask extends RecursiveAction{
    private static final int THRESHOLD = 10; //最多只能打印50个数
    private int start;
    private int end;
    
    public PrintTask(int start, int end) {
        super();
        this.start = start;
        this.end = end;
    }
    
    @Override
    protected void compute() {

        if(end - start < THRESHOLD){
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            for(int i=start;i<end;i++){
                System.out.println(Thread.currentThread().getName()+"的i值:"+i);
            }
        }else {
            int middle =(start+end)/2;
            PrintTask left = new PrintTask(start, middle);
            PrintTask right = new PrintTask(middle, end);
            //并行执行两个“小任务”
            left.fork();
            right.fork();
        }
    }
}

3.2 RecursiveTask:递归有返回结果的任务

 public static void main(String[] args) throws Exception {
        int[] arr = new int[100];
        Random random = new Random();
        int total =0;
        
        //方式一、初始化100个数组元素,并且通过单线程计算出总和
        for(int i=0,len = arr.length;i<len;i++){
            int temp = random.nextInt(20);
            //对数组元素赋值,并将数组元素的值添加到sum总和中
            total += (arr[i]=temp);
        }
        System.out.println("初始化数组总和:"+total);
        
        //方式二、通过递归多线程计算
        SumTask task = new SumTask(arr, 0, arr.length);
        ForkJoinPool pool = ForkJoinPool.commonPool();
        Future<Integer> future = pool.submit(task); //提交分解的SumTask 任务
        System.out.println("多线程执行结果:"+future.get());//这里会阻塞线程,等待所有子线程结束。
        pool.shutdown(); //关闭线程池
    }

class SumTask extends RecursiveTask<Integer>{
    private static final int THRESHOLD = 20; //每个小任务 最多只累加20个数
    private int arry[];
    private int start;
    private int end;

    /**
     * Creates a new instance of SumTask.
     * 累加从start到end的arry数组
     * @param arry
     * @param start
     * @param end
     */
    public SumTask(int[] arry, int start, int end) {
        super();
        this.arry = arry;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Integer compute() {
        int sum =0;
        //当end与start之间的差小于threshold时,开始进行实际的累加
        if(end - start <THRESHOLD){
            System.out.println(Thread.currentThread().getName()+"start:"+start+",end:"+end);
            for(int i= start;i<end;i++){
                sum += arry[i];
            }
            return sum;
        }else {//当end与start之间的差大于threshold,即要累加的数超过20个时候,将大任务分解成小任务
            int middle = (start+ end)/2;
            SumTask left = new SumTask(arry, start, middle);
            SumTask right = new SumTask(arry, middle, end);
            //并行执行两个 小任务
            left.fork();
            right.fork();
            //把两个小任务累加的结果合并起来
            return left.join()+right.join();
        }
    }
}

对于累加等操作jdk1.8提供了其他的类:LongAccumulator.

解释几个概念:

3.3 shutdown

中文解释: 开始一个有序的关闭,在关闭过程中以前提交的任务还是会被执行,
但是不会 接受新的提交的任务。
英文解释:

/**
     * Possibly initiates an orderly shutdown in which previously
     * submitted tasks are executed, but no new tasks will be
     * accepted. Invocation has no effect on execution state if this
     * is the {@link #commonPool()}, and no additional effect if
     * already shut down.  Tasks that are in the process of being
     * submitted concurrently during the course of this method may or
     * may not be rejected.
     */

3.4 shutdownNow

大致和shutdown相同,但是它会取消现有的和未执行的任务,非常符合方法名字的定义
英文解释:

 /**
     * Possibly attempts to cancel and/or stop all tasks, and reject
     * all subsequently submitted tasks. 

3.5 awaitTermination

它会阻塞当前线程,除非发生下面任何一种情况
1、在发出shutdown请求后,所有的任务都完成
2、出现timeout的情况
3、当前线程被中断。

所以在ForkJoinPool中经常看到这样的代码

//发出关闭线程请求,但是已提交的不会终止
pool.shutdown();
//等待子任务执行完成,如果子任务执行完成,线程会关闭
pool.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);

或者

pool.shutdown();
 while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {
            System.out.println("子线程还没有执行完成");
}
System.out.println("子线程池执行完成,线程池也关闭了");
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值