多线程概述

多线程的优势

多线程技术的应用程序可以更好地利用系统资源,充分利用了CPU的空闲时间片,可以用尽可能少的时间来对用户的要求做出响应,使得进程的整体运行效率得到较大的提高,同时增强了应用程序的灵活性。更重要的是,由于同一进程的所有线程是共享同一内存,所以不需要特殊的数据传送机制,不需要建立共享存储区或共享文件,从而使得不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。

总结:
1.进程之间不能共享内存,但线程之间可以且十分容易。
2.多线程来实现多任务并发比多进程的效率高。
3.提高系统的吞吐率。
4.提高响应性。
5.充分利用多核CPU的资源。
6.最小化对系统资源的使用。
7.简化程序的结构。

多线程的风险及应该注意什么?

应注意并发问题、安全问题、效率问题。

并发:指在同一时刻,只能有一条指令执行,但多个进程指令被快速轮换执行,使得在宏观上具有多个进程的同时执行的效果。
举例:我们一边使用开发工具写代码、一边打开网页再查API、同时还打开网易云听歌…
这些进程看上去是同时在工作,实际上是,对于CPU而言,他在某个时间点只能执行一个程序,即一个进程,CPU是不断的在这些进程之间轮换执行的,由于执行速度太快,所以我们感觉不到。

风险:
1.共享数据问题:多个线程共享数据问题的时候,如果没有采取相应的并发防访问措施,那么就可能产生数据不一致,如脏读,丢失更新。
2.上下文切换:如果线程过多,CPU资源出现紧张,上下文切换会增加系统的消耗,不利于系统的吞吐率。所以控制同时运行的线程数量很重要。一般会使用线程池技术,NIO(java NIO是在jdk1.4开始使用的,它既可以说成“新I/O”,也可以说成非阻塞式I/O。NIO的工作原理:1.由一个专门的线程来处理所有的IO事件,并负责分发。2.事件驱动机制:事件到的时候触发,而不是同步的去监视事件)等措施。
3.可靠性
4.线程生命体征问题:死锁、活锁、饥饿等问题。

线程池有什么作用?

线程池的作用就是限制系统中执行线程的数量。根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果,少了浪费了系统资源,多了造成系统用机效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕。再从队列中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待,当一个新任务需要运行时,若果线程池中有等待的工作线程,就可以开始运行了,否则,则进入等待队列。

为什么要引用线程池?

  1. 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

如何合理的配置线程数量?

分析线程处理的程序是CPU密集型,还是IO密集型.
CPU密集型:核心线程数 = CPU核数 + 1
IO密集型: 核心线程数 = CPU核数 * 2
注:IO密集型(某大厂实践经验)
核心线程数 = CPU核数 / (1 – 阻塞系数)
例如:阻塞系数是0.8,CPU核数为4 则核心线程数为20。

什么是CPU密集型、IO密集型

1.CPU密集型:
CPU密集型也叫计算密集型,指系统的硬盘、内存性能相对CPU要好很多。此时,系统运作大部分的状况是CPU Loading 100%,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完成,而CPU还有很多的运算要处理,CPU Loading很高。
2.IO密集型:
IO密集型指的是系统的CPU性能相对硬盘、内存要好很多。此时,系统运作,大部分状况是CPU在等I/O(硬盘/内存)的读/写操作,此时CPU Loading并不高。
多线程概述_第1张图片

Java并发编程:Callable、Future和FutureTask

创建线程的两种方式,一种是直接继承Thread,另外一种就是实现Runnable接口。
这两种方式都有一个缺陷就是:在执行任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程的通信方式来达到效果,这样使用起来比较麻烦。

Java 1.5开始提供了Callable、Future和FutureTask三个类使用方法。
在Executor框架中还有一种方法可以实现异步,那就是实现Callable,在Executor实际运行时,会将Callable的实例转化为RunnableFuture的实例,而RunnableFuture继承了Rannable和Future接口。
传统(一共用时6秒):

public class TestThread {

    public static void main(String[] args) throws InterruptedException {
       long start = System.currentTimeMillis();
       //等待ColdDishThread -- 必须要等待返回的结果,所以要调用join方法
       Thread t1 = new ColdDishThread();//1秒
       t1.start();
       //确认线程是何时结束的
       t1.join();
       //等待BumThread -- 必须要等待返回的结果,所以要调用join方法
        Thread t2 = new BumThread();//3秒
        t2.start();
        t2.join();
        //等待DangThread -- 必须要等待返回的结果,所以要调用join方法
        Thread t3 = new DangThread();//两秒
        t3.start();
        t3.join();

        long end = System.currentTimeMillis();
        //共执行6秒
        System.out.println("准备完毕时间:" + (end - start));
    }
}

使用Future(用时3秒执行时间为最长的等待时间):

public class TestFutureThread {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();

        //等BumThread
        //Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
        //callable的call方法可以抛出异常,而Runnable的run方法不能抛出异常
        Callable ca1 = new Callable() {
            @Override
            public Object call() throws Exception {
                try{
                    Thread.sleep(1000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                return "BumThread准备完毕";
            }
        };
        FutureTask<String> ft1 = new FutureTask<String>(ca1);
        new Thread(ft1).start();
        //等ColdDishThread
        Callable ca2 = new Callable() {
            @Override
            public Object call() throws Exception {
                try{
                    Thread.sleep(1000*3);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                return "ColdDishThread准备完毕";
            }
        };
        FutureTask<String> ft2 = new FutureTask<String>(ca2);
        new Thread(ft2).start();
        //等ColdDishThread
        Callable ca3 = new Callable() {
            @Override
            public Object call() throws Exception {
                try{
                    Thread.sleep(1000*2);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                return "DangThread准备完毕";
            }
        };
        FutureTask<String> ft3 = new FutureTask<String>(ca3);
        new Thread(ft3).start();
        //Future是一个接口,提供给我们方法来检测当前的任务是否已经结束,还可以等待任务结束并且拿到一个结果
        //Future的get()方法可以当任务结束后返回一个结果值,如果工作没有结束,则会阻塞当前线程,直到任务执行完毕
        System.out.println(ft1.get());
        System.out.println(ft2.get());
        System.out.println(ft3.get());

        long end = System.currentTimeMillis();
        System.out.println("准备完毕时间:" + (end-start));
    }
}

CompletableFuture supplyAsycn

CompletableFuture让Future的功能和使用场景得到了极大的完善和扩展,提供了函数式编程能力,通过回调的方式处理结果,对异常处理也有了更好的处理手段,对Java程序优化 缩短程序执行时间同步查询改为异步查询(CompletableFuture supplyAsycn) SupplyAsycn:可以支持返回值。
举例:查询一个SQL需要两秒,三个就是六秒,要不就优化SQL,不然就优化程序。
这个方法显然是用来优化程序的,作用就是将长耗时的SQL异步去执行,大大的减少了程序的等待时间,提升了响应。

//异步并发处理
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try{
        receiveProcess(message);
        return "Success";
    }
    catch (Exception ex){
        logger.error("处理线程错误",ex);
        return "Error";
    }
},pool);

线程池

在jdk7中,我们使用线程池可能会使用ExecutorService,默认有四种方式:
Executors.newSingleeThreadPool()
创建一个单线程化的线程池,他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行。
Executors.newFixedThreadPool()
创建一个定长线程池,可控制线程的最大并发数,超过的线程会在队列中等待。
Executors.newCacheThreadPool()
创建一个可缓存的线程池,如果线程池的长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Excutors.newScheduledThreadPool()
创建一个定长线程池,支持定时及周期性任务执行。
Executors.newWorkStealingPool();
抢占式(窃取)操作的线程池(Java8新出的含有足够多线程的线程池,来维持相应的并行级别,它会通过工作窃取的方式,使得多核的CPU不会闲置,总会有活着的线程让CPU去运行)

你可能感兴趣的:(多线程)