ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

3.5 多线程

2021-04-13 17:35:11  阅读:207  来源: 互联网

标签:执行 5.2 Runnable run 3.5 线程 多线程 方法


5.1 线程与进程

5.1.1 概念

  1. 进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间
  2. 线程:进程中的一个执行路径共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程

5.1.2 线程调度

  • java采用抢占式调度。多线程不能提高运行速度,可以提高运行效率,提高CPU使用率。

5.1.3 同步与异步

  • 同步:排队执行,效率低但是安全
  • 异步:同时执行,效率高但是不安全

5.1.4 并发与并行(面试题)

  • 并发:两个或多个事件在同一个时间段内发生
  • 并行:两个或多个事件在同一时刻发生

5.2 多线程

5.2.1 实现途径

  1. 继承Thread类
  • 重写run方法,通过start方法调用
  1. 实现Runnable接口
  • 重写run方法,创建一个线程并为其分配任务
  1. 实现Callable接口(较少使用)
    //  1. 编写类实现Callable接口 , 实现call方法 
    class XXX implements Callable<T> { 
        @Override public <T> call() throws Exception { 
            return T; 
        } 
    } 
    //  2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象 
    FutureTask<Integer> future = new FutureTask<>(callable); 
    //  3. 通过Thread,启动线程 
    new Thread(future).start();

Runnable与Callable异同

  • 相同点:
  • 都是接口
  • 都可以编写多线程
  • 都采用Thread.start()启动线程
  • 不同点:
  • Runnable没有返回值;Callable可以返回执行结果
  • Callable接口的call()允许抛出异常;Runnable的run()不能抛出

Callable获取返回值

  • 调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞。

5.2.1.1 Runnable比Thread的优势

  1. 更适合多个线程执行相同任务的情况
  2. 避免单继承带来的局限性
  3. 任务与线程分离,提高程序健壮性
  4. 线程池技术只接受Runnable类型任务,不接受Thread线程

5.2.2 线程中断

  • 提醒线程该中断
  • 添加中断标记:interrupt

5.2.3 进程同步方法

  1. 法一:同步代码块
  • run()方法内通过将同一对象传入synchronized(),可调用this关键字或提前实例化一个对象
  1. 法二:同步方法
  • 方法权限修饰符后添加synchronized,通过同一任务不同线程的start方法调用
    * 若添加了同步代码块和同步方法,若某一线程抢到资源,则其他线程不能执行同步方法中的内容
  1. 法三:显式锁
  • run()方法前创建锁对象 Lock l=new ReentrantLock();
  • 上锁:l.lock(); 解锁:l.unlock();

5.2.3 显式锁和隐式锁区别

  1. 两者出身不同
  • synchronized是Java中的关键字,由JVM维护,是JVM层面的锁;
  • lock是JDK5之后才出现的具体的类,使用Lock是调用对应的API,是API层面的锁。
  1. 使用方式不同
  • synchronized隐式锁;lock是显式锁
  • 显式锁和隐式锁的区别在于:使用显式锁的时候,使用者需要手动去获取和释放锁。如果没有释放锁,就可能出现死锁的现象
  1. 加锁的时候是否可以公平

5.2.4 线程阻塞

  • 在某一时刻某一个线程在运行一段代码的时候,这时候另一个线程也需要运行,但是在运行过程中的那个线程执行完成之前,另一个线程是无法获取到CPU执行权的,这个时候就会造成线程阻塞。

5.2.5 公平锁和非公平锁

  • 公平锁:保证获取锁的线程按照先来后到的顺序
  • 非公平锁:获取锁的顺序和申请锁的顺序不一定一致

5.2.6 死锁

  • 两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁。

5.2.7 线程六种状态

  1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。
  2. 运行(RUNNABLE):Java线程中将就绪(ready)和**运行中(running)****两种状态笼统的称为“运行”。
    线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得CPU时间片后变为运行中状态(running)。
  3. 阻塞(BLOCKED):在进入synchronized关键字修饰的方法或代码块(获取锁)时的状态。
  4. 等待(WAITING):处于这种状态的线程不会被分配CPU执行时间,它们要等待被显式地唤醒,否则会处于无限期等待的状态。(wait方法)
  5. 超时等待(TIMED_WAITING):处于这种状态的线程不会被分配CPU执行时间,不过无须无限期等待被其他线程显示地唤醒,在达到一定时间后它们会自动唤醒。(sleep方法)
  6. 终止(TERMINATED):表示该线程已经执行完毕。

5.2.8 线程池(较少使用)

5.2.8.1 分类

  1. 缓存线程池
  2. 定长线程池
  3. 单线程线程池
  4. 周期性任务定长线程池

5.2.8.2 应用

    /*** 缓存线程池. 
    * (长度无限制) 
    * 执行流程: 
    * 1. 判断线程池是否存在空闲线程 
    * 2. 存在则使用 
    * 3. 不存在,则创建线程 并放入线程池, 然后使用 
    */ 
    ExecutorService service = Executors.newCachedThreadPool(); 
    //向线程池中 加入 新的任务 
    service.execute(new Runnable() { 
        @Override public void run() {        
            System.out.println("线程的名称:"+Thread.currentThread().getName()); 
        } 
    }); 
    /*** 定长线程池. 
    * (长度是指定的数值) 
    * 执行流程:
    * 1. 判断线程池是否存在空闲线程 
    * 2. 存在则使用 
    * 3. 不存在,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
    * 4. 不存在,且线程池已满的情况下,则等待线程池存在空闲线程 
    */ 
    ExecutorService service = Executors.newFixedThreadPool(2); //线程池个数
    service.execute(new Runnable() { 
        @Override public void run() {        
            System.out.println("线程的名称:"+Thread.currentThread().getName()); 
        }
    });
    //效果与定长线程池 创建时传入数值1 效果一致. 
    /*** 单线程线程池. 
    * 执行流程: 
    * 1. 判断线程池 的那个线程 是否空闲 
    * 2. 空闲则使用 
    * 3. 不空闲,则等待 池中的单个线程空闲后 使用 
    */ 
    ExecutorService service = Executors.newSingleThreadExecutor(); 
    service.execute(new Runnable() { 
        @Override public void run() {
            System.out.println("线程的名称:"+Thread.currentThread().getName()); 
        } 
    });
    /*** 周期任务 定长线程池. 
    * 执行流程: 
    * 1. 判断线程池是否存在空闲线程 
    * 2. 存在则使用 
    * 3. 不存在,且线程池未满的情况下,则创建线程 并放入线程池, 然后使用 
    * 4. 不存在,且线程池已满的情况下,则等待线程池存在空闲线程 
    */
    
    /*** 格式1:
    * 参数1. runnable类型的任务 
    * 参数2. 时长数字 
    * 参数3. 时长数字的单位 
    */ 
    ScheduledExecutorService service = Executors.newScheduledThreadPool(1); 
    service.schedule(new Runnable() { 
        @Override public void run() { 
            System.out.println("xxx"); 
        }
    },5,TimeUnit.SECONDS); //5s后执行
    
   /*** 格式2:
   * 参数1. runnable类型的任务 
   * 参数2. 时长数字(延迟执行的时长) 
   * 参数3. 周期时长(每次执行的间隔时间) 
   * 参数4. 时长数字的单位 
   */
   service.scheduleAtFixedRate(new Runnable() { 
        @Override public void run() { 
            System.out.println("俩人相视一笑~ 嘿嘿嘿"); 
        } 
    },5,2,TimeUnit.SECONDS);//5s后执行,每隔2s执行

5.3 Lambda表达式

5.3.1 概念

  • Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类.

5.3.2 作用与要求

  • 对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
  • @FunctionalInterface
    修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。
  • 语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda运算符 ,读作(goes to)

标签:执行,5.2,Runnable,run,3.5,线程,多线程,方法
来源: https://www.cnblogs.com/ly411/p/14654314.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有