java多线程

引用 转载 参考 CV

https://blog.csdn.net/weixin_44797490/article/details/91006241

java 多线程 (重中之重)

  • 主线程:执行 main() 方法的线程

  • 并行:多个CPU同时执行多个任务,比如:多个人同时做不同的事

  • 并发:一个CPU(采用时间片)同时执行多个任务,比如秒杀平台,多个人做同件事

  • 线程的相关API

    • Thread.currentThread().getName()//获取当前线程的名字
    • start():1.启动当前线程2.调用线程中的run方法
    • run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
    • currentThread():静态方法,返回执行当前代码的线程
    • getName():获取当前线程的名字
    • setName():设置当前线程的名字
    • yield():主动释放当前线程的执行权
    • join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去
    • stop():过时方法。当执行此方法时,强制结束当前线程。
    • sleep(long millitime):线程休眠一段时间
    • isAlive():判断当前线程是否存活
  • 线程的调度

    • 调度策略
      • 时间片:线程的调度采用时间片轮转的方式
      • 抢占式:高优先级的线程抢占CPU
    • Java的调度方法
      • 对于同优先级的线程组成先进先出队列(先到先服务),使用时间片策略
      • 对高优先级,使用优先调度的抢占式策略
  • 线程的优先级

    • 等级
      • MAX_PRIORITY:10
      • MIN_PRIORITY:1
      • NORM_PRIORITY:5
    • 方法:
      • getPriority():返回线程优先级
      • setPriority(int newPriority):改变线程的优先级
    • 注意!:高优先级的线程要抢占低优先级的线程的cpu的执行权。但是仅是从概率上来说的,高优先级的线程更有可能被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。
  • 多线程的创建方式

    • 继承Thread类

      1. 创建一个集成于Thread类的子类
      2. 重写Thread类的run()方法
      3. 创建Thread子类的对象
      4. 通过此对象调用start()方法
    • 实现Runable接口

      1. 创建一个实现了Runable接口的类
      2. 实现类去实现Runnable中的抽象方法:run()
      3. 创建实现类的对象
      4. 将此对象作为参数传递到Thread类中的构造器中,创建Thread类的对象
      5. 通过Thread类的对象调用start()
    • 实现callable接口

      1. 创建一个实现callable的实现类
      2. 实现call方法
      3. 创建callable实现类的对象
      4. 将callable接口实现类的对象作为传递到FutureTask的构造器中,创建FutureTask的对象
      5. 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start方法启动(通过FutureTask的对象调用方法get获取线程中的call的返回值)
    • 与使用runnable方式相比,callable功能更强大些:

      • runnable重写的run方法不如callaalbe的call方法强大,call方法可以有返回值
      • 方法可以抛出异常
      • 支持泛型的返回值
      • 需要借助FutureTask类,比如获取返回结果
    • 使用线程池

      • 背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。

      • 思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。(数据库连接池)

      • 好处:提高响应速度(减少了创建新线程的时间)。降低资源消耗(重复利用线程池中线程,不需要每次都创建)。便于线程管理。

    • 创建线程池的7个参数

      1. corePoolSize线程池的核心线程数
      2. maximumPoolSize能容纳的最大线程数
      3. keepAliveTime空闲线程存活时间
      4. unit 存活的时间单位
      5. workQueue 存放提交但未执行任务的队列
      6. threadFactory 创建线程的工厂类
      7. handler 等待队列满后的拒绝策略
      • JDK 5.0 起提供了线程池相关API:

        tors工具类,线程池的工厂类,用于创建并返回不同类型的线程池
        tors.newCachedThreadPool()创建一个可根据需要创建新线程的线程池
        tors.newFixedThreadPool(n)创建一个可重用固定线程数的线程池
        tors.newSingleThreadExecutor():创建一个只有一个线程的线程池
        tors.newScheduledThreadPool(n)创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
        • ExecutorService 和 Executors
        • ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor.
        • void execute(Runnable coommand):执行任务/命令,没有返回值,一般用来执行Runnable
        • Futuresubmit(Callable task):执行任务,有返回值,一般又来执行Callable
        • void shutdown():关闭连接池。
      1. 创建实现runnable或者callable接口方式的对象
      2. 创建 executorservice 线程池
      3. 将创建好的实现了runnable接口类的对象放入executorService对象的execute方法中执行。
      4. 关闭线程池
  • 线程通信方法

    • wait()/ notify()/ notifayAll():此三个方法定义在Object类中的,因为这三个方法需要用到锁,而锁是任意对象都能充当的,所以这三个方法定义在Object类中。
    • wait()/ notify()/ notifayAll() 这三个方法均只能使用在同步代码块或者同步方法中。
    1. 每一个对象除了有一个锁之外,还有一个等待队列(wait set),当一个对象刚创建的时候,它的等待队列是空的。
    2. 我们应该在某个线程获取对象的锁后,在该对象锁控制的同步块或同步方法中去调用该对象的wait方法,将该线程挂起放入该对象等待队列。
    3. 当调用该对象的notify方法时,将从该对象的等待队列中随机唤醒一个线程,这个线程将再次成为可运行的线程。
    4. 所以我们使用wait和notify方法的时候必须确定这两个线程在同一个对象(必须是控制该同步块或同步方法的对象)的等待队列。
  • 线程的分类

    • 守护线程(如垃圾回收线程,异常处理线程),
    • 用户线程(如主线程)
  • 线程的生命周期
    线程的生命周期

  • 解决线程的安全问题

    1. 同步代码块 ,使用同步监视器(锁)(任何一个的对象都可以充当锁)。

         Synchronized(同步监视器){
         //需要被同步的代码
         }
      
    2. Runable天生共享锁,而Thread中需要用static对象或者this关键字或者当前类来充当唯一锁

    3. 同步方法,对方法进行synchronized关键字修饰

    4. JDK5.0新增的lock锁方法 (ReentrantLock )

  • sleep和wait的异同

    • 相同点:一旦执行方法以后,都会使得当前的进程进入阻塞状态
    • 不同点:
      1. 两个方法声明的位置不同,Thread类中声明sleep,Object类中声明wait。
      2. 调用的要求不同,sleep可以在任何需要的场景下调用,wait必须使用在同步代码块或者同步方法中
      3. 关于是否释放同步监视器,如果两个方法都使用在同步代码块或同步方法中,sleep不会释放,wait会释放
©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页