java 多线程学习

一、如何实现多线程

  • 实现 Runnable 接口

  1 public static void main(String[] args) {
  2     MyThread myThread = new MyThread();// 一个实现了Runnable接口的类
  3     Thread t = new Thread(myThread);// 声明一个线程
  4     t.start();// 启动线程
  5 }
  6 
  7 public class MyThread implements Runnable {
  8     @Override
  9     public void run() {// 启动线程后执行的方法
 10         System.out.print("run the \"run()\" method!");
 11     }
 12 }
  • 继承 Thread 类

  1 public static void main(String[] args) {
  2     MyThread myThread = new MyThread();// 一个继承了Thread类的类
  3     myThread.start();// 启动线程
  4 }
  5 
  6 public class extends Thread {
  7     @Override
  8     public void run() {// 启动线程后执行的方法
  9         System.out.print("run the \"run()\" method!");
 10     }
 11 }
  • 使用 ExecutorService、Future 和 Callable 创建有返回值的线程

  1 public static void main(String[] args) {
  2     // 创建一个ExecutorService
  3     ExecutorService executorService =
  4             Executors.newCachedThreadPool();
  5     // new一个MyThread线程,并交给executorService执行,
  6     // 通过Future接收返回结果
  7     Future future =
  8             executorService.submit(new MyThread());
  9     try {
 10         // 从future中获取返回值
 11         Object result = future.get();
 12         System.out.println(result.toString());
 13     } catch (Exception e) {
 14         e.printStackTrace();
 15     }
 16 }
 17 
 18 // 一个有返回值的线程
 19 public class MyThread implements Callable {
 20     @Override
 21     public String call() {
 22         System.out.print("run the \"call()\" method!");
 23         return "test";
 24     }
 25 }

二、实质


  1 Runnable、Callable 接口和多线程的实现没有关系
  2 接口的作用是约束行为,Runnable 接口的作用是指定一个协议, 规定所有继承这些接口的类都要有一个无参无返回值的方法
  3 多线程的实现是由类来完成的
  4 java 中所有的多线程最终都通过 Thread 类来实现, 线程的资源分配、线程启动这些工作都是在 start()方法中完成的, 严格来讲, 在 start0() 方法中完成的。start0() 是一个 native 方法, 并不是用 java 语言实现的。
  1. 可以通过两种方式来定义多线程中要完成的工作

  2. 实现 Runnable 接口
    Runnable 接口中的 run() 方法, 没有返回值
  3. 实现 Callable 接口
    Callable 接口中的 call() 方法, 有返回值
  4. 需要对线程进行管理 (提交、启动、终止)

  5. Thread 类
    • Thread 类有一个构造函数可以传递一个 Runnable 类型的参数 target, 可以通过 target 来提交想要执行的任务 (实现 run() 方法, 然后传给 Thread 实例)
    • Thread 类本身实现了 Runnable 接口, 也可以直接通过实现 Thread 类来提交想要执行的任务 (重写 run() 方法)
    • Thread 类的 start()方法负责启动执行线程, 当 start() 方法执行时
      • 若已经重写了 run() 方法来执行任务, 则会执行该方法
      • 若传入了 target 参数, 则会调用 target 中的 run() 方法
    • Thread 类中有 stop 方法负责停止线程, 但是已经弃用
    • 可以通过 interrupt() 方法中断线程
  6. ExecutorService 接口
    • 可以通过 executor()方法或 submit() 方法提交任务
    • submit() 方法提交的任务会返回一个 Future 对象, 可以通过这个对象来获取返回结果
    • shutdown()和 shutdownNow() 方法可以用来停止线程池

三、详解

  1. 线程的生命周期

  2. new thread(新建):创建一个线程实例, 比如通过 new 操作创建一个 Thread 类的实例, 此时线程未被启动
  3. runnable(可运行):一个线程创建好之后, 需要通知 cpu 这个线程可以开始执行了, 比如 thread 类的 start() 方法执行后, 此时线程在就绪队列中等待 cpu 分配资源
  4. running(运行中):线程获得 cpu 资源后开始运行, 比如运行 run() 方法中的逻辑, 此时除非线程自动放弃 cpu 资源或者有优先级更高的线程进入, 否则将执行到线程结束
  5. dead(死亡):线程正常执行结束, 或者被 kill 调, 此时线程将不会再次被执行
  6. block(阻塞):线程主动让出 cpu 使用权、其它更高优先级的线程进入、该线程的时间片用完,但此时该线程还没有执行完成, 都会使线程进入 block 状态, 进入 block 状态的线程还可以回到就绪队列中等待再次执行。
  7. Thread 类中的方法

  8. start:启动一个线程, 这个方法会是线程进入 Runnable 状态, 等待执行
  9. isAlive:判断线程是否处于活动状态(Runnable 或 running)
  10. sleep:强制让线程放弃当前时间片进入休眠状态一定时间, 此时线程会进入 block 状态, 直到休眠的时间结束, 再进入 Runnable 状态。sleep 是静态方法, 只能控制所在线程。sleep(0) 会直接触发下一次 cpu 竞争, 如果没有优先级更高的线程, 则会继续工作
  11. wait(override Object):放弃对象锁, 进入等待池, 只有针对此对象调用 notify() 方法之后, 才会再次进入 Runnable 状态
  12. join:阻塞等待线程结束, 可以接收参数 millis 和 nanos 指定等待的最大时间
  13. interrupt: 中断线程, 这个方法并不能中断正在运行的线程, 运行该方法后, 只有当线程被 join(),sleep() 和 wait() 方法所阻塞时, 才会被 interrupted 方法所中断, 并抛出一个 InterruptedException 异常
  14. static yield:主动放弃 cpu 使用权, 回到 Runnable 状态

四、Tips

  1. block 状态
    1. 等待阻塞:运行线程执行了 wait 方法, 该线程会释放占用的所有资源包括对象锁, 进入等待队列中。进入等在队列的线程是不能自动唤醒的, 必须依靠其它线程调用 notify()、notifyAll() 来进行唤醒(该状态下线程会释放对象锁)
    2. 同步阻塞:运行的线程在获取对象同步锁时, 同步锁已被其它线程占用, 则该线程会进入锁队列等待获取同步锁, 直到获取到同步锁之后回再次进入 Runnable 状态(该状态下线程还没有获得对象锁)
    3. 其它阻塞:运行的线程调用了 sleep()或 join() 方法, 或者发出 IO 请求, 该线程会进入阻塞状态, 知道 sleep 超时、join 所等待的线程结束或是 IO 操作完成, 则会再次进入 Runnable 状态(该状态下线程只会放弃 cpu 而不会释放对象锁)
  2. sleep(0)
    sleep(0) 会重新触发一次 cpu 竞争, 当 Runable 队列中有大于或等于当前线程优先级的线程时, 当前线程会进入 Runnable 队列将 cpu 的使用权让出, 否则会继续运行
  3. sleep()和 wait()
    • sleep 方法会让出 cpu, 但不会释放对象锁, 等到 sleep 超时之后会自动进入 Runnable 队列
    • wait 方法会让出 cpu, 并释放对象锁, 需要其它线程调用 notify()、notifyAll() 才能重新进入 Runnable 队列
  4. interrupt()
    interrupt 方法的作用更倾向于告诉线程, 你可以结束了, 而不是直接地中断线程, 知道线程进入阻塞状态时, 才能中断线程。对于陷入死循环、IO 等待等难以进入阻塞状态的线程来说,interrupt 方法是不能有效中断的。
  5. sleep()和 yield()
    这两个方法都会让出 cpu 使用权,sleep 会进入 block 状态, 而 yield 会直接进入 Runnable 状态


  本文出自:hacpai:Zl992532172

转载于:https://www.cnblogs.com/ios9/p/7473148.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值