juc并发编程入门(一)

60 篇文章 1 订阅

new出来的线程都是用户线程,main方法也是用户线程,也叫主线程;

守护线程就是在后台隐藏的线程,例如垃圾回收;

 public static void main(String[] args) {
       Thread t1=new Thread(()->{
           if(Thread.currentThread().isDaemon()){
               System.out.println("t1是守护线程");
           }else {
               System.out.println("t1是用户线程");
           }
           while (true){

           }
       });
       t1.start();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("main线程结束了");
    }

我们可以看到main线程结束了,并不会关闭t1线程

可以对t1线程进行修改设置为守护线程

我们在设置守护线程的时候一定要注意

t1.setDaemon(true);要放在t1.start前面,否则就报下面的错误

可以看到,当main线程结束后,守护线程和jvm一起停止

我们来看下FutureTask,就是异步任务,当前线程执行的任务,不会干扰main方法中其他的代码

还可以正常往下走

在不需要返回值的情况可以使用Runnable接口


public class Producer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //未来任务 当前线程 不需要返回值
        FutureTask<String> futureTask= new FutureTask<String>(new MyThread(),null);
        Thread t1=new Thread(futureTask);
        t1.start();
        System.out.println("主线程结束了");
    }
}

class MyThread implements Runnable {


    @Override
    public void run() {
        System.out.println("我是没有返回值的");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在需要获取返回值的时候,可以使用Callable接口

public class Producer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //未来任务 当前线程 不需要返回值
        FutureTask<String> futureTask= new FutureTask<String>(new MyThread());
        Thread t1=new Thread(futureTask);
        t1.start();
        //如果获取返回值,当前线程阻塞,当前拿到结果之后,才能往下去执行其他的代码
        String s = futureTask.get();
        System.out.println("获取线程返回值:"+s);
        System.out.println("主线程结束了");
    }
}

class MyThread implements Callable<String> {


    @Override
    public String call() throws Exception {
        return "我是有返回值的线程";
    }
}

我们来看下同步方法的耗时

public class Producer {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start=System.currentTimeMillis();
        aa();
        bb();
        cc();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
    }


    public static void aa(){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void bb(){
        try {
            Thread.sleep(1100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void cc(){
        try {
            Thread.sleep(1200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

可以看到同步的时候耗时:3323

接下来我们看下异步

public static void main(String[] args) throws ExecutionException, InterruptedException {
        //我们每次new线程的时候,都需要被垃圾回收多次,我们可以使用线程池来管理多个线程
        //创建一个固定3个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        long start=System.currentTimeMillis();
        FutureTask f1=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                Thread.sleep(1000);
                return "f1";
            }
        });

        FutureTask f2=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                Thread.sleep(1100);
                return "f2";
            }
        });

        FutureTask f3=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                Thread.sleep(1200);
                return "f3";
            }
        });
        executorService.submit(f1);
        executorService.submit(f2);
        executorService.submit(f3);

        System.out.println(f1.get());
        System.out.println(f2.get());
        System.out.println(f3.get());
        //关闭线程池
        executorService.shutdown();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));

    }

可以看到耗时:1214,比同步的要快很多

但是FutureTask有一个缺点就是,get方法会造成阻塞,导致无法往下执行;

一般都是把.get方法放到最后一行去处理

我们还可以有其他的方法来解决

3秒内拿不到结果,强行停止线程

.get(3,TimeUnit.SECONDS)

我们在来看下轮询的效果

package com.example.client.entity;

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.*;

public class Producer {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        //我们每次new线程的时候,都需要被垃圾回收多次,我们可以使用线程池来管理多个线程
        //创建一个固定3个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        long start=System.currentTimeMillis();
        FutureTask f1=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                Thread.sleep(1000);
                return "f1";
            }
        });

        FutureTask f2=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                Thread.sleep(1100);
                return "f2";
            }
        });

        FutureTask f3=new FutureTask(new Callable() {
            @Override
            public Object call() throws Exception {
                System.out.println("你好");
                Thread.sleep(2200);
                return "f3";
            }
        });
        executorService.submit(f1);
        executorService.submit(f2);
        executorService.submit(f3);

        System.out.println(f1.get());
        System.out.println(f2.get());
        while (true){
            if(f3.isDone()){
                //如果线程执行完成了 退出循环
                System.out.println(f3.get());
                break;
            }else {
                //不要那么频繁的去访问 我们设置一个小的阻塞时间
                Thread.sleep(500);
                System.out.println("业务正在处理中,请稍后");
            }
        }
        //关闭线程池
        executorService.shutdown();
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));

    }
}

isDone就是线程执行完毕了,才调用获取返回值的方法

轮询获取,每次都给用户看到业务在处理中,不会让用户看不到效果

轮询非常占用cpu,而且不优雅

但是以上都不是我们想要的

我们来看下CompletableFuture(可完成的未来)

CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方

我们进入源码 可以看到

CompletableFuture实现了Future和 CompletionStage接口

Future有的功能,CompletableFuture都有

CompletionStage(完成阶段):表示异步计算过程中的某一个阶段,一个阶段完成后,可能会触发另外一个阶段

我们接下来先看下没有返回值的方法runAsync

他有2个参数,一个是Runnable,一个是线程池

我们先不加线程池看下什么效果

 public static void main(String[] args) throws Exception {

        CompletableFuture completableFuture=CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("返回值:"+completableFuture.get());
        System.out.println("主线程结束");
    }

我们可以看到没有返回值,并且不加线程池,线程的名字为ForkJoinPool.commonPool

我们加一下线程池看一下

 public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        CompletableFuture completableFuture=CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },executors);
        System.out.println("主线程结束");
    }

我们可以看到加了线程池之后,线程的名字就变成了pool-1-thread-1

我们在来看下有返回值的方法

   public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
       // ExecutorService executors=Executors.newFixedThreadPool(3);
        CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "aaaa";
        });
        System.out.println(completableFuture.get());
        System.out.println("主线程结束");
    }

可以看到有返回值的线程名称还是ForkJoinPool.commonPool

我们在看下加了线程池的效果

 public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
            System.out.println(Thread.currentThread().getName());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "aaaa";
        },executors);
        System.out.println(completableFuture.get());
        //关闭线程池
        executors.shutdown();
        System.out.println("主线程结束");

    }

加了线程池之后线程池的名称变成了pool-1

但是上面还是有点不足,还会造成阻塞

接下来我们看下下面的代码

使用whenComplete方法在线程完成之后,回调处理返回结果,这样不会造成阻塞

使用exceptionally方法 在线程出错了,抛出异常

可以看到,并没有打印我们的返回结果,为啥呢?因为没有加线程池

接下来我们把线程池加上

 public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        try {
            CompletableFuture<String> completableFuture=CompletableFuture.supplyAsync(()->{
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return "aaaa";
            },executors).whenComplete((v,e)->{
                //线程完成时的方法 进入回调接口
                if(e==null){
                    //如果没有异常 打印 线程返回的结果
                    System.out.println("线程返回结果:"+v+",你在这里可以处理其他的方法了");
                }
            }).exceptionally((e)->{
                //线程执行错误 抛出异常
                e.printStackTrace();
                return null;
            });
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            //关闭线程池
            executors.shutdown();
        }
        System.out.println("主线程结束");
    }

可以看到先走了主线程,我业务的线程等处理完成了才打印,这样就解决了阻塞

join()和get()方法是一样的,区别就是join不需要在箭头位置抛出异常

而get需要抛出异常

BiConsumer<? super T, ? super Throwable>

这个表示2个参数,一个v返回结果,一个Throwable 异常

接下来我们在看下下面的例子

public class Producer {

    public static void main(String[] args) throws Exception {
        List<String>list=new ArrayList<>();
        long start =System.currentTimeMillis();
        list.add(aa());
        list.add(bb());
        list.add(cc());
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
        System.out.println(list);
    }
    public static String aa() throws InterruptedException {
        Thread.sleep(1000);
        return "aa";
    }
    public static String bb() throws InterruptedException {
        Thread.sleep(1000);
        return "bb";
    }
    public static String cc() throws InterruptedException {
        Thread.sleep(1000);
        return "cc";
    }
}

拿到3个方法的返回值,放入list集合中

耗时3秒

我们使用异步的方式,来改造下代码

public class Producer {

    public static void main(String[] args) throws Exception {
        List<String>list=new ArrayList<>();
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        long start =System.currentTimeMillis();
        try {
            CompletableFuture<String> c1 = CompletableFuture.supplyAsync(() -> {
                try {
                    return aa();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }, executors);

            CompletableFuture<String> c2 = CompletableFuture.supplyAsync(() -> {
                try {
                    return bb();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }, executors);

            CompletableFuture<String> c3 = CompletableFuture.supplyAsync(() -> {
                try {
                    return cc();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return null;
            }, executors);
            list.add(c1.get());
            list.add(c2.get());
            list.add(c3.get());
            System.out.println(list);
        }finally {
            executors.shutdown();
        }
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));

    }
    public static String aa() throws InterruptedException {
        Thread.sleep(1000);
        return "aa";
    }
    public static String bb() throws InterruptedException {
        Thread.sleep(1000);
        return "bb";
    }
    public static String cc() throws InterruptedException {
        Thread.sleep(1000);
        return "cc";
    }
}

可以看到耗时1秒

我们在看下thenApply(然后应用)这个方法

package com.example.client.entity;

import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.*;
import java.util.function.Function;

public class Producer {

    public static void main(String[] args) throws Exception {
        List<String>list=new ArrayList<>();
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        long start =System.currentTimeMillis();
        try {
            CompletableFuture<Integer> c1 = CompletableFuture.supplyAsync(() -> {
                System.out.println("我是第一阶段");
                return 1;
            }, executors)
            .thenApply(v->{
                System.out.println("我是第二阶段");
                return v+1;
            })
            .thenApply(v->{
                System.out.println("我是第三阶段");
                return v+1;
            })
            .whenComplete((v,e)->{
                if(e==null){
                    System.out.println("我是第四阶段"+v);
                }
            })
            .exceptionally(e->{
                e.printStackTrace();
                return null;
            })
            ;
        }finally {
            executors.shutdown();
        }
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
    }
}

意思就是当前supplyAsync执行完成之后,我还可以执行多个thenApply方法

等执行thenApply完了之后,在执行whenComplete方法

我们可以看到,如果是报错了,就不会往下走了

我们在看下handle(处理程序)这个方法,和thenApply的效果是一样的

那么我们看一下报错,是什么效果

可以看到,报错之后,下面的handle方法还是正常的运行,最后走到异常的方法

和thenApply区别就是,thenApply不会往下执行,直接进去异常方法

thenAccept这个方法是然后接受,没有返回值

 public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        long start =System.currentTimeMillis();
        try {
            CompletableFuture.supplyAsync(() -> {
                System.out.println("我是第一阶段");
                return 1;
            }, executors)
            .thenApply((v)->{
                System.out.println("我是第二阶段");
                return v+1;
            })
            .thenAccept(v->{
                System.out.println("我是没有返回值的:"+v);
            })
            ;
        }finally {
            executors.shutdown();
        }
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
    }

我们在来看下thenRun方法

 public static void main(String[] args) throws Exception {
        //创建3个固定的线程池
        ExecutorService executors=Executors.newFixedThreadPool(3);
        long start =System.currentTimeMillis();
        try {
            CompletableFuture<Void> aa = CompletableFuture.supplyAsync(() -> {
                System.out.println("我是第一阶段");
                return 1;
            }, executors)
                .thenRun(()->{
                    System.out.println("我是没有返回值,并且不需要依赖supplyAsync里面的结果");
                  }
                );
            System.out.println(aa.get());
        }finally {
            executors.shutdown();
        }
        long end=System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));
    }

当我们使用thenApplyAsync(然后应用同步)方法的时候

线程的名字和第一阶段的名字不一样

我们在来看下applyToEither方法,谁最快用谁

 public static void main(String[] args) throws Exception {
        CompletableFuture<String> aa=CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "aa";
        });
        CompletableFuture<String> bb=CompletableFuture.supplyAsync(()->{
            return "bb";
        });
        Object o = aa.applyToEither(bb,x->{
            return x+"最快";
        }).get();
        System.out.println(o);
    }

可以看到bb最快返回的就是bb

我们在看下thenCombine,把2个返回的结果组合到一块

public static void main(String[] args) throws Exception {
        CompletableFuture<String> aa=CompletableFuture.supplyAsync(()->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("aa执行完毕");
            return "aa";
        });
        CompletableFuture<String> bb=CompletableFuture.supplyAsync(()->{
            System.out.println("bb执行完毕");
            return "bb";
        });
        CompletableFuture<String> cc = aa.thenCombine(bb, (x, y) -> {
            return x + y;
        });
        System.out.println(cc.join());
    }

juc并发编程入门(二)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值