/**PageBeginHtml Block Begin **/ /***自定义返回顶部小火箭***/ /*生成博客目录的JS 开始*/ /*生成博客目录的JS 结束*/

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 语言实现的。
    • 可以通过两种方式来定义多线程中要完成的工作

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

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

      三、详解

        • 线程的生命周期

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

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

          posted @ 2017-09-04 13:09  一品堂.技术学习笔记  阅读(231)  评论(0编辑  收藏  举报