2020-10-18

多线程

线程与进程

进程:

  • 是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

进程:

  • 进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。
  • 线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

线程调度

线程调度是指Java虚拟机按照特定机制为多个线程分配CPU的使用权。

分时调度:

  • 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

抢占式调度:

  • 优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。 处于运行状态的线程会一直运行,直至它不得不放弃CPU。

​ CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使用率更高。

放弃CPU原因

  • java虚拟机让当前线程暂时放弃CPU,转到就绪状态,使其它线程获得运行机会。
  • 当前线程因为某些原因而进入阻塞状态 。
  • 线程结束运行 。

注意:

	线程的调度不是跨平台的,它 不仅仅取决于java虚拟机,还依赖于操作系统。在某些操作系统中,只要运行中的线程没有遇到阻塞,就不会放弃CPU;在某些操作系统中,即使线程没有遇到阻塞,也会运行一段时间后放弃CPU,给其它线程运行的机会。 

让一个线程给另一线程运行机会:

  • 调整各个线程的优先级

  • 让处于运行状态的线程调用Thread.sleep()方法

  • 让处于运行状态的线程调用Thread.yield()方法

  • 让处于运行状态的线程调用另一个线程的join()方法

  • 线程切换:不是所有的线程切换都需要进入内核模式

同步与异步

  • 同步:排队执行,效率低安全。
  • 异步:同时执行,效率高,数据不安全。

区别:

  • 同步,是所有的操作都做完,才返回给用户结果。即写完数据库之后,再响应用户,用户体验不好。
  • 异步,不用等所有操作都做完,就响应用户请求。即先响应用户请求,然后慢慢去写数据库,用户体验较好。为了避免短时间大量的数据库操作,就使用缓存机制,也就是消息队列。先将数据放入消息队列,然后再慢慢写入数据库。

并发与并行

三种解释:

  • 并发:指两个或者多个事件在同一时间段内发生。
  • 并行:指两个或者多个事件在同一时刻发生。
  • 并行是在不同实体上的多个事件。
  • 并发是在同一实体上的多个事件。
  • 并行是在多台处理器上同时处理多个任务。如 hadoop 分布式集群。
  • 并发是在一台处理器上“同时”处理多个任务。

​ 并行在多处理器系统中存在,而并发可以在单处理器和多处理器系统中都存在,并发能够在单处理器系统中存在是因为并发是并行的假象,并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行)。

​ 当有多个线程在操作时,如果系统只有一个 CPU,则它根本不可能真正同时进行一个以上的线程,它只能把 CPU 运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态.这种方式我们称之为并发(Concurrent)。

​ 当系统有一个以上 CPU 时,则线程的操作有可能非并发。当一个 CPU 执行一个线程时,另一个 CPU 可以执行另一个线程,两个线程互不抢占 CPU 资源,可以同时进行,这种方式我们称之为并行(Parallel)。

Runnable 与 Callable

    接口定义
    //Callable接口
    public interface Callable<V> {
        V call() throws Exception;
    }
    //Runnable接口
    public interface Runnable {
        public abstract void run();
    }

Callable使用步骤

  1. 编写类实现Callable接口 , 实现call方法
   class XXX implements Callable<T> {
       @Override
       public <T> call() throws Exception {
           return T;
       }
   }
  1. 创建FutureTask对象 , 并传入第一步编写的Callable类对象
FutureTask<Integer> future = new FutureTask<>(callable);
  1. 通过Thread,启动线程
new Thread(future).start();

Runnable 与 Callable的相同点

  • 都是接口
  • 都可以编写多线程程序
  • 都采用Thread.start()启动线程

Runnable 与 Callable的不同点

  • Runnable没有返回值Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable获取返回值

​ Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

线程池Executors

​ 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低 系统的效率,因为频繁创建线程和销毁线程需要时间. 线程池就是一个容纳多个线程的容器,池中的线程可以反复使用,省去了频繁创建线程对象的操作,节省了大量的时间和资源。

好处

  • 降低资源消耗。
  • 提高响应速度。
  • 提高线程的可管理性

分类:四种线程池 . ExecutorService

  1. 缓存线程池

        /**
         * 缓存线程池.
         * (长度无限制)
         * 执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在,则创建线程 并放入线程池, 然后使用
         */
        ExecutorService service = Executors.newCachedThreadPool();
    //向线程池中 加入 新的任务
    service.execute(new
    
        Runnable() {
            @Override
            public void run () {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
    service.execute(new
    
        Runnable() {
            @Override
            public void run () {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
    service.execute(new
    
        Runnable() {
            @Override
            public void run () {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
    
  2. 定长线程池

        /**
         * 定长线程池.
         * (长度是指定的数值)
         * 执行流程:
         * 1. 判断线程池是否存在空闲线程
         * 2. 存在则使用
         * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
         * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
         */
        ExecutorService service = Executors.newFixedThreadPool(2);
        service.execute(new
    
        Runnable() {
            @Override
            public void run () {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run () {
                System.out.println("线程的名称:" + Thread.currentThread().getName());
            }
        });
    
  3. 单线程线程池

        效果与定长线程池 创建时传入数值1 效果一致.
    /**
     * 单线程线程池.
     * 执行流程:
     * 1. 判断线程池 的那个线程 是否空闲
     * 2. 空闲则使用
     * 4. 不空闲,则等待 池中的单个线程空闲后 使用
     */
            ExecutorService service = Executors.newSingleThreadExecutor();
            service.execute(new Runnable() {
            @Override
                public void run() {
                    System.out.println("线程的名称:"+Thread.currentThread().getName());
                    }
            });
            service.execute(new Runnable() {
            @Override
                public void run() {
                    System.out.println("线程的名称:"+Thread.currentThread().getName());
                }
            });
    
  4. 周期性任务定长线程池

        public static void main(String[] args) {
            /**
             * 周期任务 定长线程池.
             * 执行流程:
             * 1. 判断线程池是否存在空闲线程
             * 2. 存在则使用
             * 3. 不存在空闲线程,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用
             * 4. 不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程
             *
             * 周期性任务执行时:
             * 定时执行, 当某个时机触发时, 自动执行某任务 .
             */
            ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
            /**
             * 定时执行
             * 参数1. runnable类型的任务
             * 参数2. 时长数字
             * 参数3. 时长数字的单位
             */
            /*service.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println("俩人相视一笑~ 嘿嘿嘿");
                }
            },5,TimeUnit.SECONDS);
            */
            /**
             * 周期执行
             * 参数1. runnable类型的任务
             * 参数2. 时长数字(延迟执行的时长)
             * 参数3. 周期时长(每次执行的间隔时间)
             * 参数4. 时长数字的单位
             */
            service.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    System.out.println("俩人相视一笑~ 嘿嘿嘿");
                }
            }, 5, 2, TimeUnit.SECONDS);
        }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值