【面试需了解之多线程基础】线程创建、状态及常用方法

前言

线程概述、线程创建、状态及常用方法、ThreadLocal

文章目录

    • 前言
    • 概述
      • 系统设计的两种任务类型
      • 应用场景
    • 多线程
      • 概述
      • 线程创建
        • 1. 继承Thread类
        • 2. 实现Runnable接口
        • 3. 实现Callable接口
        • 三种方式对比
      • 线程的常用方法
        • 1. 设置、获取线程名字
        • 2. 线程休眠-sleep
        • 3. 设置、获取线程优先级
      • 线程的五种状态
      • ThreadLocal

概述

用编程语言编写让计算机可以在一个时间段内执行多个任务的程序

系统设计的两种任务类型

  • CPU密集型:CPU计算
  • IO密集型:数据库读取、文件读取

应用场景

  • 电商秒杀
  • 抢红包
  • 春运购票

多线程

概述

指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程

并发和并行

  • 并发-currency:把任务在不同的时间点交给处理器进行处理。在同一时间点,任务并不会同时运行
  • 并行-parallelism:把每一个任务分配给每一个处理器独立完成。在同一个时间点,任务一定是同时运行

进程和线程

  • 进程:计算机已执行的程序,系统进行资源调度和分配的基本单位
  • 线程:操作系统进行运算调度的最小单位,CPU进行调度和分配的基本单位;被包含在进程中,是进程中的实际运作单位

线程创建

1. 继承Thread类

  1. 定义类继承Thread

  2. 重写run方法

  3. 创建类对象

  4. 启动线程

    public class MyThread extends Thread {
        @Override
        public void run() {
            // 任务   
        }   
    }
    
    
    Thread mythread = new MyThread();
    mythread.start();
    

2. 实现Runnable接口

  1. 定义类实现Runnable

  2. 重写run方法

  3. 创建类对象

  4. 创建Thread引用,传入类对象

  5. 启动线程

    public class MyRunnable implements Runnable {
     
        @Override
        public void run() {
            // 任务
        }
    }
    
    Runnable thread = new MyRunnable();
    thread.start();
    

3. 实现Callable接口

  1. 定义类实现Callable

  2. 重写call方法

  3. 创建类对象

  4. 创建FutureTask引用,传入类对象

  5. 创建Thread引用,传入FutureTask类对象

  6. 启动线程

    public class MyCallable implements Callable<String> {
    
        @Override
        public Object call() throws Exception {
            // 任务
            return null;
        }
    }
    
    Callable<String> callable = new MyCallable();
    FutureTask<String> futureTask = new FutureTask<>(callable);
    Thread thread = new Thread(futureTask);
    thread.start();
    

三种方式对比

实现方式 优点 缺点
继承Thread 编程简单,可以执行使用Thread类中的方法 扩展性差
实现Runnable 扩展性好
适合多个相同线程来处理同一份资源的情况(成员变量)
编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread()方法
实现Callable 同上
可以有返回值;可以抛出异常
同上

线程的常用方法

1. 设置、获取线程名字

  • 继承方式
    • 获取名字:getName()
    • 设置名字:setName()
  • 实现接口方式
    • 获取当前线程:Thread.currentThread()
      • 获取名字同上继承方式

2. 线程休眠-sleep

  • Thread.sleep()

3. 设置、获取线程优先级

同级别的线程,先到先服务,轮流抢占CPU时间片

高优先级的线程,抢占CPU的概率更高

  • 设置优先级:setPriority()
    • 是否起作用与操作系统及虚拟机版本相关
  • 获取优先级:getPriority()

线程的五种状态

对应线程的生命周期

1. 新建

  • 创建线程

2. 就绪

  • 调用.start()方法
  • 没有获得CPU资源

3. 阻塞

  • 调用阻塞方法

4. 运行

  • 获得CPU资源

5. 消亡

  • 运行结束

ThreadLocal

  1. java中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据
  2. 底层是通过ThreadLocalMap来实现的,每个Thread对象中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值
  3. 如果在线程池中使用ThreadLocal会造成内存泄漏。因为当ThreadLocal对象使用完之后,应该要把设置的key、value(Entry对象 )进行回收,但线程池中的线程不会回收,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用对象指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏。解决办法是当使用了ThreadLocal对象后,手动调用remove方法,清除Entry对象
  4. 经典的应用场景是连接管理(一个线程持有一个连接,该连接对象可以在不同的方法之间进行传递,线程之间不共享同一个连接)

你可能感兴趣的:(#,面试知识点,面试,java,多线程,并发编程)