线程基础

前言

线程是Java语言中不可获取的重要功能,它们能使复杂的异步代码变得简单,从而极大地简化了复杂系统的开发。此外,要想充分发挥多核处理器系统的强大计算能力,最简单的方式就是使用线程。

并发编程的背景

  • 为什么需要并发编程:当前CPU的计算能力强大且成本相对较低,采用并发编程可以更好的利用CPU的高性能,提升复杂应用程序的性能;也能有效降低程序的开发和维护成本(简化异步事件的处理、充分利用CPU的能力)
  • 计算机背景知识
    • 计算机体系架构中,CPU、内存、I/O设备这三者的计算速度存在很大的差异,CPU>>内存>>>>I/O设备;
    • 为了合理利用CPU,平衡三者的速度差异,计算机体系、操作系统、编译程序分别做出了贡献,主要体现为:
      • 计算机体系:CPU增加了缓存,以均衡与内存的速度差异
      • 操作系统:增加了进程、线程,可以分时复用CPU,以均衡CPU与I/O设备的速度差异
      • 编译程序:优化指令执行顺序,使缓存能够得到更加合理地利用

并发编程带来的问题

安全性、活跃性、性能问题

  • 安全性主要概括为三大问题:可见性、原子性、有序性
    • 可见性:是指变量修改的可见性。即一个线程对共享变量的修改,其他线程能够立马看到。(由于共享变量都在存放在共享内存中,而每个线程有一个本地缓存,线程操作数据都会先将变量加载到本地缓存中再操作,所以就可能会产生变量不可见的问题)
    • 原子性:是指内存模型操作的原子性。指一个或者多个操作在CPU执行的过程中不会因为切换线程而导致中断的特性。(由于CPU的线程数有限,在运行应用程序时,会随时切换线程)
    • 有序性:是指代码执行的有序性。即如果下一步操作需要依赖上一步的操作结果,那么必须保证其按顺序执行。(JVM会对代码进行重排序以提升效率)
  • 活跃性问题主要有:死锁、活锁、线程饥饿问题
    • 线程饥饿:线程因其他线程一直抢占CPU资源,导致一直得不到执行;
    • 死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。死锁的四个必要条件:互斥(进程在某一时间独占资源)、请求与保持(进程因请求资源造成阻塞时,已占有资源不释放)、不可抢占(进程未使用完的资源不能被剥夺)、循环等待(线程A等待线程B占有的资源,线程B等待线程A占有的资源)
    • 活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。例如:死循环、自旋锁。
  • 性能问题主要有:锁的竞争、线程上下文切换、线程阻塞、内存同步

线程相关概念

  • 什么是线程和进程:进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元;一个程序至少有一个进程,一个进程至少有一个线程。
  • 用户线程和守护线程:
    • 用户(User)线程就是用来运行程序代码的线程,如:运行程序的线程;
    • 守护(Daemon)线程就是用来为用户线程提供服务的辅助线程,如:垃圾回收线程。守护线程通过Thread.setDaemon(true)设置,必须在Thread.start()之前调用。程序不一定需要守护线程,但当用户线程全都结束时,守护线程也会全都自动结束
  • 线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。
  • 线程上下文切换:多线程需要共同使用服务器的CPU资源,当多线程数超过系统给程序分配的CPU数量时,为了保证每个线程都能被执行,需要轮转使用CPU。不同线程切换使用CPU所产生的切换数据叫上下文切换
  • 线程同步和互斥
    • 线程同步:线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到得到消息后才被唤醒。
    • 线程互斥:线程互斥是指对于共享的进程系统资源,同一时刻最多只允许一个线程访问,其他线程必须等待占用者释放资源后才能访问。
  • 线程封闭:仅在单线程内访问数据,不对其他线程共享。这种技术被称为线程封闭。常见应用:JDBC的Connection对象。

线程的生命周期

image-20220801205400291

  • NEW(初始化状态):创建Thread对象
  • RUNNABLE(可运行/运行状态):调用线程对象的 start() 方法
  • BLOCKED(阻塞状态):线程等待 synchronized 的隐式锁。线程调用阻塞式 API 时,只会使操作系统中的线程转换到休眠状态,JVM中的线程不会发生变化。
  • WAITING(无时限等待):(1)获得 synchronized 隐式锁的线程,调用无参数的Object.wait()方法;(2)调用无参数的Thread.join()方法;(3)调用LockSupport.park()方法
  • TIMED-WAITING(有时限等待):(1)调用带超时参数的Thread.sleep(long millis) 方法;(2)获得 synchronized 隐式锁的线程,调用带超时参数的Object.wait(long timeout)方法;(3)调用带超时参数的Thread.join(long millis) 方法;(4)调用带超时参数的LockSupport.parkNanos(Object blocker, long deadline)方法;(5)调用带超时参数的LockSupport.parkUntil(long deadline)方法
  • TERMINATED(终止状态):(1)线程执行完run方法;(2)执行run方法的时候抛出异常;(3)调用interrupt方法强制中断run方法

Thread基本方法说明

构造方法

public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}

属性设置

//设置类加载器
public void setContextClassLoader(ClassLoader cl) {...}
//设置为守护线程
public final void setDaemon(boolean on) {...}
//设置线程名称
public final synchronized void setName(String name) {...}
//设置线程优先级
public final void setPriority(int newPriority) {...}

管理生命周期

//运行线程,线程状态由NEW转变为RUNNABLE
public synchronized void start() {...}
//线程等待,
public final synchronized void join(long millis) throws InterruptedException {...}
//线程让步,当前线程让出已占用的CPU资源和锁,重新参与竞争
public static native void yield();
//线程休眠,当前线程不让出已占用的CPU资源和锁,待休眠时间到了再继续执行
public static native void sleep(long millis) throws InterruptedException;
//线程中断,
public void interrupt() {}

检测状态

//检测是否为活跃状态
public final native boolean isAlive();
//检测是否为守护线程
public final boolean isDaemon() {...}
//检测是否中断
public boolean isInterrupted() {...}

获取当前线程

public static native Thread currentThread();

创建线程

(1)继承Thread类重写run方法;(2)实现Runnable接口;(3)实现Callable接口;(4)通过线程池的线程工厂方法创建

//继承Thread类重写run方法
public static void main(String[] args) {
TestThread testThread=new TestThread();
testThread.start();
}
static class TestThread extends Thread{
@Override
public void run(){
System.out.println("hello world");
}
}
//实现Runnable接口
Thread thread=new Thread(()->{
System.out.println("hello world");
});
thread.start();
//实现Callable接口
FutureTask futureTask=new FutureTask(()->"hello world");
Thread thread=new Thread(futureTask);
thread.start();
//通过线程池的线程工厂方法创建
ExecutorService executor=new ThreadPoolExecutor(8,8,1L
,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(8));
executor.submit(()->"hello world");
posted @   晚秋的风  阅读(28)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
点击右上角即可分享
微信分享提示