线程的概念
在计算机开展的早期年代,在一般操作体系中一般都是以进程作为能独立运转的基本单位 的。而且进程的模型都是具有单个控制线程的履行程序。现在许多的操作体系都供给了单个 进程包含多个控制线程的支撑。以此来提高体系内程序并发履行的速度,从而可进一步提高 体系的吞吐量。咱们下面就来看看两者的比较。
- 进程—是现代操作体系的一个基本概念,是并发程序呈现后呈现的一个重要概念,它是指 程序在一个数据集合上运转的进程,是体系进行资源分配和调度运转的一个独立单位,有时 也称为活动、路径或使命。 假如说在操作体系中引进进程的目的,是为了使多个程序并发履行,以改进资源利用率及提 高体系的吞吐量:那么,在操作体系中再引进线程则是为了减少程序并发履行时所付出的时 空开支,使操作体系具有更好的并发性。
- 线程—是进程中的一个实体,是被体系调度和分配的基本单元。每个程序至少包含一个线 程,那便是主线程。线程自己只具有很少的体系资源(如程序计数器、一组寄存器和栈), 但它可与同属一个进程的其他线程同享所属进程所具有的悉数资源,同一进程中的多个线程 之间能够并发履行,从而更好地改进了体系资源的利用率。
并发和并行
- 并行指的是同一个时刻内同时产生多个事情,这个是由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();
就能够完成线程了