并发编程(三)Thread类详解

文章更新时间:2021/09/06

一、引言

  Thread类中存在着许多操作线程的方法,学习Thread类是非常有必要的,前面我们也嘘唏了创建线程的几种方式,若线程的创建不是以继承Thread类的方式创建的,那我们又改如何使用Thread中的方法呢?下面一起来学习一下多线程操作的重点--Thread类。

二、实例详解

/**
 * 认识Thread类的常用方法
 */
public class MyThread {

    public static void main(final String[] arguments) throws InterruptedException {
        /*一个以实现Runnable接口的方式创建的线程,如何使用Thread类中的方法?*/
        //1、创建一个线程实例(此线程是以实现Runnable接口的方式创建的)
        TestThread runableThread = new TestThread();
        //2、通过Thread类的构造函数,用刚才创建的线程生成一个Thread类的实例
        Thread thread = new Thread(runableThread);

        /*Thread类中重要的操作线程方法*/
        thread.setName("线程1");//设置线程名称
        thread.setPriority(1);//设置线程优先级(1~10;默认为5;10为最高优先级;优先级高并不一定就先执行!
        thread.setDaemon(false);//设置该线程是否为守护线程(true:是;false:不是)
        thread.start();//启动线程
        thread.isAlive();//查看当前线程是否存活
        thread.join(3000L);//等待该线程执行X秒后再执行下面的代码(PS:若设值大于线程执行时间,则执行完毕后立即执行下面的线程)
        thread.interrupt();//终止线程(PS:若线程中存在sleep方法,且线程还没运行完毕,调用interrupt时会报错)

        /*Thread类中的静态方法*/
        thread.sleep(1000L);//线程休眠1秒(当前线程回到可执行状态,并不一定能立即执行!
        thread.yield();//暂停执行当前线程,并执行其他线程(当前线程回到可执行状态)PS:也有可能刚暂停一下,当前线程立马就获得CPU时间片又继续执行
        thread.currentThread();//返回当前线程的实例对象
    }

}

三、ThreadLocal

什么是ThreadLocal

  定义:每一个Thread对象均含有一个ThreadLocalMap类型的成员变量threadLocals,它存储本线程中所有ThreadLocal对象及其对应的值。

  作用:维护一个归属线程所有的变量,再整个线程存活期间都可以获取出来。

  好处:

  • 1、若一条业务线内部需要使用一个变量执行很多方法,那么使用ThreadLocal就可以不用每个方法都显式声明该变量。
  • 2、由于ThreadLocal是线程安全的,那么线程中特有的公共变量可以用这种形式来封装

小结:

  • ThreadLocalMap 由一个个 Entry 对象构成。
  • Entry 继承自 WeakReference<ThreadLocal<?>> ,一个 Entry 由 ThreadLocal 对象和 Object 构成。
  • 执行threadLocal.set()方法时,ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。
  • ThreadLocalMap是以当前ThreadLocal对象为key,将值存储进ThreadLocalMap对象中的。
  • 每一条线程均含有各自私有的ThreadLocalMap容器,因此不会存在线程安全性问题。

ThreadLocal使用示例

  PS:ThreadLocal实例通常总是以静态字段初始化的。

/**
 * ThreadLocal测试类
 *
 * @author 有梦想的肥宅
 * @date 2021/9/6
 */
public class ThreadLocalTest {

    public static void main(String[] args) throws Exception {
        MyThreadLocal myThreadTest1 = new MyThreadLocal();
        myThreadTest1.setName("线程1");
        ;
        myThreadTest1.start();
    }

}

/**
 * 创建一个类,继承Thread,重写run方法
 *
 * @author 有梦想的肥宅
 */
class MyThreadLocal extends Thread {

    //1、创建一个threadLocal对象【PS:ThreadLocal实例通常总是以静态字段初始化的】
    static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();

    @Override
    public void run() {

        //2、在threadLocal中保存一个值
        int num = (int) (Math.random() * 100D);
        threadLocal.set(num);

        //3、执行业务逻辑
        Business business = new Business();
        business.business();
    }


}

/**
 * 模拟线程方法中调用的业务类
 *
 * @author 有梦想的肥宅
 */
class Business {

    //业务方法
    public void business() {

        //模拟在业务代码中获取threadLocal的值
        System.out.println(Thread.currentThread().getName() + "输出num:" + MyThreadLocal.threadLocal.get());

        //清除
        MyThreadLocal.threadLocal.remove();
    }
}

ThreadLocal的内存泄露问题

什么是内存泄露?

  内存泄露指的是程序在申请内存后,无法释放已申请的内存空间

产生原因

  原因:在线程池中创建的线程,并设置了ThreadLocal。

  解析:因为ThreadLocal的生命周期是跟随线程的,而线程池中的线程执行完任务后不会回收。虽然ThreadLocalMap中的key是弱引用,在GC时会被回收,但是其value却是强引用,是无法被回收的。所以如果我们需要使用ThreadLocal,一定要记得最后手动调用一下threadLocal.remove()方法,防止内存泄露。

posted @ 2019-12-05 16:43  有梦想的肥宅  阅读(765)  评论(0编辑  收藏  举报