线程的基础和使用
线程的概念
在计算机发展的早期年代,在一般操作系统中一般都是以进程作为能独立运行的基本单位 的。并且进程的模型都是具有单个控制线程的执行程序。现在许多的操作系统都提供了单个 进程包含多个控制线程的支持。以此来提高系统内程序并发执行的速度,从而可进一步提高 系统的吞吐量。我们下面就来看看两者的比较。
- 进程---是现代操作系统的一个基本概念,是并发程序出现后出现的一个重要概念,它是指 程序在一个数据集合上运行的过程,是系统进行资源分配和调度运行的一个独立单位,有时 也称为活动、路径或任务。 如果说在操作系统中引入进程的目的,是为了使多个程序并发执行,以改善资源利用率及提 高系统的吞吐量:那么,在操作系统中再引入线程则是为了减少程序并发执行时所付出的时 空开销,使操作系统具有更好的并发性。
- 线程---是进程中的一个实体,是被系统调度和分配的基本单元。每个程序至少包含一个线 程,那就是主线程。线程自己只拥有很少的系统资源(如程序计数器、一组寄存器和栈), 但它可与同属一个进程的其他线程共享所属进程所拥有的全部资源,同一进程中的多个线程 之间可以并发执行,从而更好地改善了系统资源的利用率。
并发和并行
- 并行指的是同一个时间内同时发生多个事情,这个是由CPU的核心数决定的。
- 并发是指两个或多个事件在同一时间间隔内发生,这个词可以从宏观和微观两个层面来讲,如果从微观 角度来看。以线程为例,假设当前电脑的cpu是单核,但是能不能支持多线程呢?当然也是能的,此时 如果是多线程运行的话,那么CPU是通过不断分配时间片的方式来实现线程切换,由于切换的速度足够 快,我们很难感知到卡顿的过程(实则一个CPU同一时间只能处理一件事情)
java线程的生命周期
java线程的状态
这里需要注意的是java线程的状态并不是系统线程的状态,java并没有线程,java的线程仅仅是对系统线程的使用,在系统线程的基础上又分为了几种状态 从上图可以看出一共是有6种状态
-
新建(New)
-
可运行(Runnable) 可能正在运行,也可能正在等待 CPU 时间片。 包含了操作系统线程状态中的 Running 和 Ready。
-
阻塞(Blocking) 如下代码所示:
public class BlockedThread extends Thread { @Override public void run() { synchronized (BlockedThread.class) { try { while (true){ TimeUnit.SECONDS.sleep(100); } } catch (InterruptedException e) { throw new RuntimeException(e); } } } }
private static void test_blocked() { new Thread(new BlockedThread(), "thread_1").start(); new Thread(new BlockedThread(), "thread_2").start(); }
-
无限期等待(Waiting)
进入方法 退出方法 wait()不传时间 notify()/notifyAll() join()不传时间 被调用的线程执行完毕 LockSupport.park() LockSupport.unPark() 代码示例
private static void test() { new Thread(() -> { synchronized (Test.class){ try { Test.class.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } },"thread_1").start(); }
-
限期等待(Timed Waiting)
进入方法 退出方法 wait()传时间 时间结束/notify()/notifyAll() join()传时间 时间结束/被调用的线程执行完毕 sleep() 时间结束 代码示例
private static void test() { new Thread(() -> { try { while (true) { TimeUnit.SECONDS.sleep(100); } } catch (InterruptedException e) { e.printStackTrace(); } },"thread_1").start(); }
-
死亡(Terminated)
- jps:查询当前java进程的PID
- jsstack 根据pid查询进程中线程的堆栈信息
操作系统中的状态
- new: 新建状态
- ready: 表示线程已经被创建,正在等待系统调度
- running: 正在执行中的线程
- waiting: 表示线程被挂起
- Terminated: 死亡状态
线程的启动、终止
启动
new Thread(new BlockedThread(), "thread_1").start();
调用start方法
底层调用的是start0方法也就是jvm提供的方法,所以从这里看出java是没有线程的,具体调用流程如下:
说白了我们用的java也是个高级语言底层的线程使用封装都在jvm,所以我们在使用线程的时候仅仅是调用了jvm提供的方法
终止
- 正常情况下的终止,也就是任务执行完成
- 抛出异常终止
- stop()方法终止
如下代码所示:
再看运行结果private static void test() { thread = new Thread(() -> { for (int i = 0; i < 10000; i++) { // System.out.println(i); try { TimeUnit.SECONDS.sleep(1); System.out.println(i); } catch (InterruptedException e) { throw new RuntimeException(e); } } },"thread_1"); thread.start(); } public static void main(String[] args) throws InterruptedException { Test.test(); TimeUnit.SECONDS.sleep(5); thread.stop(); }
这是强制中断线程通常不建议这样做,应为你的线程任务执行程度不可控了
- interrupt()
interrupt
会设置一个变量值为true,并且如果线程处于阻塞状态的情况会唤醒线程如下代码:
private static void test() {
thread = new Thread(() -> {
int i = 0;
while (!Thread.currentThread().isInterrupted()){
System.out.println(i++);
}
},"thread_1");
thread.start();
}
public static void main(String[] args) throws InterruptedException {
Test.test();
TimeUnit.SECONDS.sleep(2);
thread.interrupt();
}
如果我们平时在写业务的时候需要一直执行某个任务直至某个情况的发生然后暂停可以使用这种方式,
private static void test() {
thread = new Thread(() -> {
int i = 0;
while (!Thread.currentThread().isInterrupted()){
try {
TimeUnit.SECONDS.sleep(2);
System.out.println(i++);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
// throw new RuntimeException(e);
}
}
},"thread_1");
thread.start();
}
public static void main(String[] args) throws InterruptedException {
Test.test();
TimeUnit.SECONDS.sleep(6);
thread.interrupt();
}
这种情况就是唤醒了正在阻塞的线程,但是需要注意的是一旦发生异常会触发线程复位,也就是
isInterrupted
会变成false
所以可以发现在catch中继续调用Thread.currentThread().interrupt();
就可以完成线程了
转载自:https://juejin.cn/post/7224422626943418424