Thread

Thread线程

创建多线程的方式

第一种:创建Thread类的子类

  • java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类

  • 实现步骤:

    1. 创建一个Thread类的子类
    2. 在Thread的子类中重写Thread类中的run方法,设置线程任务(开启线程要做什么)
    3. 创建一个Thread类的子类对象
    4. 调用Thread类中的方法start方法,开启新的线程,执行run方法
      • void start();使该线程开始执行;Java 虚拟机调用该线程的 run 方法。
  • 多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

第二种:实现Runnable接口

  • Java.lang.Runnable

    • Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
  • Java.lang.Thread的构造方法:

    • Thread(Runnable target);分配新的 Thread 对象。
    • Thread(Runnable target, String name);分配新的 Thread 对象。
  • 实现步骤:

    1. 创建一个Runnable接口的实现类
    2. 在实现类中重写Runnable接口的run方法,设置线程任务
    3. 创建一个Runnable接口的实现对象
    4. 创建Thread类对象,构造方法中传递Runnable接口的实现类对象
    5. 调用Thread类中的start方法,开启新的线程,执行run方法
  • 实现Runnable接口创建多线程的好处:

    1. 避免了单线程的局限性
      • 一个类只能继承一个类,类继承了Thread类就不能继承其他类
      • 实现了Runnable接口还可以继承其他类实现其他接口
    2. 增强了程序的扩展性,降低了程序的耦合性
      • 实现Runnable接口的方式,把设置线程任务和开启新的线程进行了分离
      • 实现类中,重写了run方法,用来设置线程任务
      • 创建Thread类对象,调用start方法,用来开启新的线程
  • 匿名内部类实现线程创建

    • 可以简化代码,把子类继承父类,重写父类的方法,创建子类对象合成一步完成,或者把实现类实现类接口,重写接口中的方法,创建实现类对象合成一步完成

    • 格式:

      new 父类/接口(){
          重写方法
      }
      
      //线程的父类是Thread
      new Thread() {
          @Override
          public void run() {
              for (int i = 0; i < 20; i++) {
                  System.out.println(Thread.currentThread().getName() + "  " + i);
              }
          }
      }.start();
      
      //线程的接口Runnable
      new Thread(new Runnable() {
          @Override
          public void run() {
              for (int i = 0; i < 20; i++) {
                  System.out.println(Thread.currentThread().getName() + "  " + i);
              }
          }
      }).start();

获取线程的名称

  • 获取线程的名称:

    1. 使用Thread类中的方法getName();
      • String getName();返回该线程的名称。
    2. 可以先获取到当前正在执行的线程,再使用线程中的方法getName()获取线程名称
      • static Thread currentThread();返回对当前正在执行的线程对象的引用。
  • 线程的名称:

    • 主线程:main
    • 子线程:Thread-0,Thread-1,Thread-2….

暂停线程

public static void sleep(long millis, int nanos); 在指定的毫秒数加指定的纳秒数内让当前正在执行的线程休眠(暂停执行)

  • 此操作受到系统计时器和调度程序精度和准确性的影响。
  • 注意:毫秒数结束后,线程继续执行

多线程内存图解

tq0zH1.jpg


多线程原理

tqBNEq.jpg


线程不安全的解决方案

第一种:使用同步代码块

  • 格式:

    synchronized (锁对象){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }
  • 注意:

    1. 同步代码块中的锁对象可以是任意的对象
    2. 但是必须保证多个线程使用的锁对象是同一个
    3. 锁对象的作用:把同步代码块锁住,只让一个线程在同步代码块中执行

第二种:使用同步方法

  • 使用步骤:

    1. 把访问了共享数据的方法抽取出来,放到一个方法中
    2. 在方法上添加一个修饰符synchronized
  • 格式:

    修饰符 synchronized 返回值类型 方法名(参数列表){
        可能会出现线程安全问题的代码(访问了共享数据的代码)
    }

第三种:Lock锁

  • java.util.concurrent.locks.lock接口

  • Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。

  • Lock接口中的常用方法:

    • void lock();获取锁。
    • void unlock();释放锁。
  • java.util.concurrent.locks.ReentrantLock implements Lock接口

  • 使用步骤:

    1. 在成员位置创建一个ReentrantLock对象
    2. 在可能会出现线程安全问题的代码前调用Lock接口中的方法lock()获取锁
    3. 在可能会出现线程安全问题的代码后调用Lock接口中的方法unlock()释放锁

线程池

  • 线程池:JDK1.5之后提供的

  • java.util.concurrent.Executors:线程池的工厂类,用来生产线程池

  • Executors类中的静态方法:

    • static ExecutorService newFixedThreadPool(int nThreads);创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
      • 参数:
        • int nThreads:创建线程池中包含的线程数量
      • 返回值:
        • ExecutorService接口,返回的是ExecutorService接口的实现类对象,我们可以使用ExecutorService接口接收
  • java.util.concurrent.ExecutorService:线程池接口

  • 用来从线程池中获取线程,调用start方法执行线程任务:

    • Future<?> submit(Runnable task);提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future。
  • 关闭销毁线程池的方法:

    • void shutdown();启动一次顺序关闭,执行以前提交的任务,但不接受新任务。
  • 线程池的使用步骤:

    1. 使用线程池工厂类Executors里边的静态方法newFixedThreadPool生产一个指定线程数量的线程池
    2. 创建一个类,实现Runnable接口,重写run方法,设置线程任务
    3. 调用ExecutorService中的方法submit传递线程任务,开启线程,执行run方法

线程池类似原理

tqr9SO.jpg

  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!

请我喝杯咖啡吧~

支付宝
微信