文章目录
- 第一章 Java多线程技能
- 1.1进程和线程的定义以及多线程的优点
- 1.2 使用多线程
- 1.2.1继承Thread类
- 1.2.2常见的3个命令分析线程的信息
- 方法一\:cmd+jsp
- 方法二\:jmc.exe
- 方法三:jvisualcm.exe
- 1.2.3 线程随机性的展现
- 1.2.4 执行start()的顺序不代表执行run()的顺序
- 1.2.5 实现Runnable接口
- 1.2.6 使用Runnable接口实现多线程的优点
- 1.2.7 public Thread(Runnable target)中的target参数
- 1.2.8 实例变量共享导致的“非线程安全”问题与相应的解决方案
- 1、不共享数据的情况
- 2、共享数据的情况
- 1.2.9 Servlet技术也会引起“非线程安全”问题
- 1.2.10 留意i--与System.out.println()出现的“非线程安全”问题
- 1.2.11 方法run()被JVM所调用
- 1.3 方法currentThread()
- 1.4 方法isAlive()
- 关于Thread.currentThread().getName() 和 this.getName()区别详解
- currentThread的详解
- ****为什么为main呢**?**
- 创建一个新的线程
- 1.5 方法sleep(long millis)
- 1.6 方法sleep(long millis, int nanos)
- 1.7 方法StackTraceElement[] getStackTrace()
- 1.8 方法static void dumpStack()
- 1.9 方法Map<Thread, StackTraceElement[]> getAllStackTraces()
- 1.10 方法getId()
- 11.1 停止线程
- 11.1.1 停止不了的线程
- 11.1.2 判断是不是停止状态
- 1.11.3 清除中断状态的使用场景
- 1.11.4 能停止的线程——异常法
- 1.11.5 在sleep状态下停止
- 1.11.6 使用stop()暴力停止线程
- 1.11.7 方法stop()与java.lang.ThreadDeath
- 1.11.8 使用stop()释放锁导致数据结果不一致
- 1.11.9 使用return;语句停止线程的缺点及相应解决方案
- 1.12 暂停线程
- 1.12.2 suspend()与resume()方法的缺点:独占
- 1.12.3 suspend()与resume()方法的缺点:数据不完整
- 1.12.4 使用LockSupport类实现线程的暂停恢复
- 1.13 方法yield()
- 1.14 线程的优先级
- 1.14.1 线程优先级的继承特性
- 1.14.2 线程优先级的规律性
- 1.14.3 线程优先级的随机性
- 1.15 守护线程
- 1.16 并发与并行
- 1.17 同步和异步
- 1.18 多核CPU不一定比单核CPU运行的快
第一章 Java多线程技能
1.1进程和线程的定义以及多线程的优点
-
什么是线程:
进程中独立运行的主任务
(比如:在QQ运行中其他子任务也在运行)
进程负责向操作系统申请资源 -
进程和线程的总结
- 进程虽然是互相独立的,但他们可以互相通信,较为通用的方式为Socket和Http协议
- 进程拥有共享的系统资源
- 进程较重,因为创建进程需要操作系统分配资源,会占用内存
- 线程存在于进程中,是进程的一个子集,先有进程,后有线程
- 虽然线程更轻,但线程的上下文切换的时间成本非常高
- 什么场景需要多线程技术
- 阻塞:一旦系统出现阻塞现象,则可以根据实际情况使用多线程提升运行效率
- 依赖:业务分为两个执行结果(A,B),当b不依赖a业务执行的结果时,可以采用多线程
1.2 使用多线程
public class Test {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName());
}
}
在控制台输出的main其实就是一个名称为main的线程在执行main()方法中的代码,main线程由JVM创建。另外需要说明一下,在控制台输出的main和main方法没有任何关系,它们仅仅是名字相同而已。
1.2.1继承Thread类
实现多线程编程的方式主要有两种:一种是继承Thread类,另外一种是实现Runnable接口。
Thread类的结构构成:
public class Thread implements Runnable
例:
public class MyThread extends Thread{
public void run(){
super.run();
System.out.println("MyThread");
}
}
<!---->
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();//耗时大
System.out.println("运行结束");//耗时小
}
}
方法start()耗时多的原因是内部执行了多个步骤,步骤如下:
- 通过JVM告诉操作系统创建Thread;
- 操作系统开辟内存并使用Windows SDK中的createThread()函数创建Thread线程对象;
- 操作系统对Thread对象进行调度,以确定执行时机;
- Thread在操作系统中被成功执行。
public class Run2 {
public static void main(String[] args) throws InterruptedException {
MyThread mythread = new MyThread();
mythread.start();
Thread.sleep(200);
System.out.println("运行结束!");
}
Thread.sleep()方法的参数是毫秒,代码可读性不好,而TimeUnit可以更加方便地使用指定的时间单位实现sleep操作,代码可读性好。其他时间单位为NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS、MINUTES、HOURS、DAYS。
public class Run2 {
public static void main(String[] args) throws InterruptedException {
MyThread mythread = new MyThread();
mythread.start();
System.out.println("开始时间"+System.currentTimeMillis());
TimeUnit.SECONDS.sleep(5);
System.out.println("运行结束!");
System.out.println("结束时间"+System.currentTimeMillis());
}
}
1.2.2常见的3个命令分析线程的信息
方法一:cmd+jsp
方法二:jmc.exe
下载地址:https://jdk.java.net/jmc/8/
方法三:jvisualcm.exe
在JDK1.6后的版本是自带这个工具,它就在你的jdk的bin目录上
1.2.3 线程随机性的展现
public class MyThread extends Thread{
public void run() {
for (int i = 0; i < 10000; i++) {
System.out.println("run=" + Thread.currentThread().getName());
}
}
}
<!---->
public class Test {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("myThread");
thread.start();
for (int i = 0; i < 10000; i++) {
System.out.println("main=" + Thread.currentThread().getName());
}
}
}
多线程随机输出的原因是CPU将时间片分给不同的线程,线程获得时间片后就执行任务,所以这些线程在交替执行并输出,导致输出结果呈乱序。
CPU在不同的线程上进行切换时是需要耗时的,所以并不是创建的线程越多,软件运行效率就越快,相反,线程数过多反而会降低软件的执行效率。
1.2.4 执行start()的顺序不代表执行run()的顺序
public class MyThread extends Thread{
private int i;
public MyThread(int i) {
super();
this.i = i;
}
@Override
public void run() {
System.out.println(i);
}
}
<!---->
public class Test {
public static void main(String[] args) {
MyThread t11 = new MyThread(1);
MyThread t12 = new MyThread(2);
MyThread t13 = new MyThread(3);
MyThread t14 = new MyThread(4);
MyThread t15 = new MyThread(5);
MyThread t16 = new MyThread(6);
MyThread t17 = new MyThread(7);
MyThread t18 = new MyThread(8);
MyThread t19 = new MyThread(9);
MyThread t110 = new MyThread(10);
MyThread t111 = new MyThread(11);
MyThread t112 = new MyThread(12);
MyThread t113 = new MyThread(13);
t11.start();
t12.start();
t13.start();
t14.start();
t15.start();
t16.start();
t17.start();
t18.start();
t19.start();
t110.start();
t111.start();
t112.start();
t113.start();
}
}
1.2.5 实现Runnable接口
和Thread基本一样,为异步执行
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("运行中");
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N05实现Runnable接口;
public class Run {
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
System.out.println("运行结束!");
}
}
1.2.6 使用Runnable接口实现多线程的优点
使用继承Thread类的方式来开发多线程应用程序在设计上是有局限的,因为==Java是单继承,不支持多继承,所以为了改变这种限制,可以使用实现Runnable接口的方式来实现多线程
package org.example.第一章Java多线程技能.N02使用多线程.N06使用Runnable接口实现多线程的优点;
public class AServer {
public void a_save_method() {
System.out.println("a中的保存数据方法被执行");
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N06使用Runnable接口实现多线程的优点;
public class BServer1 extends AServer implements Runnable {
public void b_save_method() {
System.out.println("b中的保存数据方法被执行");
}
@Override
public void run() {
a_save_method();
b_save_method();
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N06使用Runnable接口实现多线程的优点;
public class Test {
public static void main(String[] args) {
BServer1 bServer1=new BServer1();
bServer1.run();
}
}
1.2.7 public Thread(Runnable target)中的target参数
Thread下的run方法:
target属性为Runnable:
1.2.8 实例变量共享导致的“非线程安全”问题与相应的解决方案
1、不共享数据的情况
package org.example.第一章Java多线程技能.N02使用多线程.N08实例变量共享导致的非线程安全问题与相应的解决方案;
public class MyThread extends Thread{
private int count=5;
public MyThread(String name){
super();
this.setName(name);//设置线程名称
}
@Override
public void run() {
super.run();
while (count>0){
count--;
System.out.println("由 " + this.currentThread().getName() + " 计算,count=" + count);
}
}
}
<!---->
package org.example.第一章Java多线程技能.N02使用多线程.N08实例变量共享导致的非线程安全问题与相应的解决方案;
public class Run {
public static void main(String[] args) {
MyThread a=new MyThread("A");
MyThread b=new MyThread("B");
MyThread c=new MyThread("C");
a.start();
b.start();
c.start();
}
}
一共创建了3个线程,每个线程都有各自的count变量,自己减少自己的count变量的值
2、共享数据的情况
package org.example.第一章Java多线程技能.N02使用多线程.N08实例变量共享导致的非线程安全问题与相应的解决方案;
public class MyThread2 extends Thread{
int count=5;
@Override
public void run() {
super.run();
count--;
// 此示例不要用while语句,会造成其他线程得不到运行的机会
// 因为第一个执行while语句的线程会将count值减到0
// 一直由一个线程进行减法运算
System.out.println("由 "+this.currentThread().getName()+" 计算,count="+ count);
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N08实例变量共享导致的非线程安全问题与相应的解决方案;
public class Run2 {
public static void main(String[] args) {
MyThread2 myThread2=new MyThread2();
Thread a=new Thread(myThread2,"A");
Thread b=new Thread(myThread2,"B");
Thread c=new Thread(myThread2,"C");
Thread d=new Thread(myThread2,"D");
Thread e=new Thread(myThread2,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
A和B是基本上同时运行的,产生了非线程安全问题
1.2.9 Servlet技术也会引起“非线程安全”问题
package org.example.第一章Java多线程技能.N02使用多线程.N09Servlet技术也会引起非线程安全问题;
public class LoginSerlvet {
private static String usernameRef;
private static String passwordRef;
public static void doPost(String username,String password){
try {
usernameRef=username;
if (usernameRef.equals("a")){
Thread.sleep(5000);
}
passwordRef = password;
System.out.println("username=" + usernameRef + " password=" + password);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N09Servlet技术也会引起非线程安全问题;
public class ALogin extends Thread{
@Override
public void run() {
super.run();
LoginSerlvet.doPost("a","aa");
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N09Servlet技术也会引起非线程安全问题;
public class BLogin extends Thread{
@Override
public void run() {
super.run();
LoginSerlvet.doPost("b","bb");
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N09Servlet技术也会引起非线程安全问题;
public class Run {
public static void main(String[] args) {
ALogin aLogin=new ALogin();
aLogin.start();
BLogin bLogin=new BLogin();
bLogin.start();
}
}
解决相关问题使用synchronized关键字:
可以保证同一时间只有一个线程在执行方法
1.2.10 留意i–与System.out.println()出现的“非线程安全”问题
println()方法与i–联合使用时“有可能”出现的另外一种异常情况,并说明其产生的原因。
package org.example.第一章Java多线程技能.N02使用多线程.N10i递减和输出非线程安全问题;
public class MyThread extends Thread{
private int i=5;
@Override
public void run() {
super.run();
System.out.println("i=" + (i--) + "threadName="+Thread.currentThread().getName());
}
}
package org.example.第一章Java多线程技能.N02使用多线程.N10i递减和输出非线程安全问题;
public class Run {
public static void main(String[] args) {
MyThread run=new MyThread();
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
Thread t3 = new Thread(run);
Thread t4 = new Thread(run);
Thread t5 = new Thread(run);
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}
虽然println()方法在内部是synchronized同步的,但i–操作却是在进入println()之前发生的,所以有一定概率发生非线程安全问题
1.2.11 方法run()被JVM所调用
当start()方法执行后,由JVM调用run()方法:
1.3 方法currentThread()
说明main方法是被名为main的线程调用的。
这里的main指的是名为main的线程
package org.example.第一章Java多线程技能.N03方法currentThread;
public class MyThread extends Thread{
public MyThread(){
System.out.println("构造方法的打印:" + Thread.currentThread().getName());
}
@Override
public void run() {
super.run();
System.out.println("run方法的打印:" + Thread.currentThread().getName());
}
}
package org.example.第一章Java多线程技能.N03方法currentThread;
public class Run2 {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
}
}
构造方法的打印:main
run方法的打印:Thread-0
MyThread.java类的构造方法是被main线程调用的,
而run方法是被名为Thread-0的线程调用的,run方法是被JVM自动调用的方法。
将Run2进行修改
package org.example.第一章Java多线程技能.N03方法currentThread;
public class Run2 {
public static void main(String[] args) {
MyThread myThread=new MyThread();
// myThread.start();
myThread.run();
}
}
构造方法的打印:main
run方法的打印:main
start和run方法的区别:
1)my.run();:立即执行run()方法,不启动新的线程。
2)my.start();:执行run()方法时机不确定时,启动新的线程。
不要认为所有的构造方法都必须由main线程调用,要结合实际情况与写法确定,其他线程也可以调用构造方法,比如main方法启动A线程,又在A线程中的run()方法里启动B线程
package org.example.第一章Java多线程技能.N03方法currentThread;
public class CountOperate extends Thread{
public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("this.getName()=" + this.getName());
System.out.println("CountOperate---end");
}
@Override
public void run() {
super.run();
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("this.getName()=" + this.getName());
System.out.println("run---end");
}
}
package org.example.第一章Java多线程技能.N03方法currentThread;
public class Run {
public static void main(String[] args) {
CountOperate countOperate=new CountOperate();
Thread thread=new Thread(countOperate);
thread.setName("A");
thread.start();
}
}
CountOperate---begin
Thread.currentThread().getName()=main
this.getName()=Thread-0
CountOperate---end
run---begin
Thread.currentThread().getName()=A
this.getName()=Thread-0
run---end
代码this.getName()代表CountOperate对象的name名称,由于CountOperate对象的name名称从未设置,所以默认为Thread-0。
1.4 方法isAlive()
isAlive()方法的功能是判断线程对象是否存活。
package org.example.第一章Java多线程技能.N04方法isAlive;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("run="+this.isAlive());
}
}
package org.example.第一章Java多线程技能.N04方法isAlive;
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
System.out.println("begin:"+myThread.isAlive());
myThread.start();
System.out.println("end:"+myThread.isAlive());
}
}
end:虽然其输出的值是true,但此值是不确定的,输出true值是因为mythread线程还未执行完毕
package org.example.第一章Java多线程技能.N04方法isAlive;
public class Run {
public static void main(String[] args) throws InterruptedException {
MyThread myThread=new MyThread();
System.out.println("begin:"+myThread.isAlive());
myThread.start();
Thread.sleep(1000);
System.out.println("end:"+myThread.isAlive());
}
}
end:输出的结果为false,因为mythread对象已经在1秒之内执行完毕。(main主线程执行的Thread.sleep(1000)方法会使main主线程停止1秒,而不是将mythread线程停止1秒)
package org.example.第一章Java多线程技能.N04方法isAlive;
public class CountOperate extends Thread{
public CountOperate() {
System.out.println("CountOperate---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("CountOperate---end");
}
@Override
public void run() {
System.out.println("run---begin");
System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName());
System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive());
System.out.println("this.getName()=" + this.getName());
System.out.println("this.isAlive()=" + this.isAlive());
System.out.println("run---end");
}
}
package org.example.第一章Java多线程技能.N04方法isAlive;
public class Run1 {
public static void main(String[] args) {
CountOperate countOperate=new CountOperate();
Thread thread = new Thread(countOperate);
System.out.println("main begin t1 isAlive=" + thread.isAlive());
thread.setName("A");
thread.start();
System.out.println("main end t1 isAlive=" + thread.isAlive());
}
}
在使用isAlive()方法时,如果将线程对象以构造参数的方式传递给Thread对象进行start()启动,则运行的结果和前面的示例是有差异的,造成这样的差异的原因是Thread.currentThread()和this的差异,
关于Thread.currentThread().getName() 和 this.getName()区别详解
currentThread的详解
currentThread方法是Thread类的一个静态方法,用来获取当前运行的代码段,正在被哪个线程调用。我们先来看一眼源码。
public static void main(String[] args) {
String name = Thread.currentThread().getName();
System.out.println(name);
}
输出的结果为main。
为什么为main呢?
java的项目在启动的时候,会创立一个进程,这个进程同样也是一个线程,在java里面他就叫做main线程。他的名字在设定的时候就是main。我们可以看到上面的代码就是在main方法下执行的,也就是由main线程来执行,所以我们打印出来的名字是main。
创建一个新的线程
public class TestThread extends Thread {
public TestThread() {
System.out.println("构造方法:" + Thread.currentThread().getName());
}
@Override
public void run() {
System.out.println("run方法:" + Thread.currentThread().getName());
}
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.start();
}
}
构造方法:main
run方法:Thread-0
这里我们只是声明了一个线程对象类,这个新的线程没有创建也没有启动,我们仅仅把它理解为一个普通对象即可。那么由于是在main方法里面,那么执行他的一定是main线程,所以可以看到构造方法输出的结果是main。
再来看start方法里面,为什么变成了thread-0了呢。
我们知道java的多线程创建的一种方式就是继承thread类。然后实现里面的run方法。这样当线程start的时候,就会调用内部的start0的本地方法,实际就是会执行run的实现方法。当run方法执行的时候,一定是由我们创建的线程去执行的,而不是main线程,所以我们就可以得知打印出来的是新线程的名字thread0。
更多见
1.5 方法sleep(long millis)
sleep()方法的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行),这个“正在执行的线程”是指this.currentThread()返回的线程。
package org.example.第一章Java多线程技能.N05方法sleep;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run threadName=" + this.currentThread().getName() + " begin "+System.currentTimeMillis());
Thread.sleep(2000);
System.out.println("run threadName=" + this.currentThread().getName() + " end "+System.currentTimeMillis());
}catch (InterruptedException e){
}
}
}
package org.example.第一章Java多线程技能.N05方法sleep;
public class Run {
public static void main(String[] args) {
MyThread mythread = new MyThread();
System.out.println("begin =" + System.currentTimeMillis());
mythread.start();
System.out.println("end =" + System.currentTimeMillis());
}
}
由于main线程与MyThread2线程是异步执行的,所以首先输出的信息为begin和end,而MyThread2线程是后运行的
1.6 方法sleep(long millis, int nanos)
public static void sleep(long millis, intnanos)方法的作用是让当前正在执行的线程在指定的毫秒数加指定的纳秒数内休眠(暂停执行),此操作受到系统计时器及调度程序精度及准确性的影响
1.7 方法StackTraceElement[] getStackTrace()
作用:返回一个表示该线程的堆栈跟踪元素数组。如果该线程尚未启动或已经终止,则该方法将返回一个零长度数组。如果返回的数组不是零长度的,则其第一个元素代表堆栈顶,它是该数组中最新的方法调用。最后一个元素代表堆栈底,是该数组中最旧的方法调用。
package org.example.第一章Java多线程技能.方法StackTraceElementgetStackYrace;
public class Test1 {
public void a() {
b();
}
public void b() {
c();
}
public void c() {
d();
}
public void d() {
e();
}
public void e(){
StackTraceElement[] stackTraceElements=Thread.currentThread().getStackTrace();
if (stackTraceElements!=null){
for (int i=0;i<stackTraceElements.length;i++){
StackTraceElement stackTraceElement=stackTraceElements[i];
System.out.println("className="+stackTraceElement.getClassName()+" methodName="
+ stackTraceElement.getMethodName() +
" fileName=" + stackTraceElement.getFileName() +
" lineNumber=" + stackTraceElement.getLineNumber());
//StackTraceElement.getLineNumber()方法返回一个的源代码行的行号包含由该堆栈跟踪元素所表示的执行点。
}
}
}
public static void main(String[] args) {
Test1 test1=new Test1();
test1.a();
}
}
1.8 方法static void dumpStack()
作用是将当前线程的堆栈信息输出至标准错误流。该方法仅用于调试。
package org.example.第一章Java多线程技能.N08方法staticvoiddumpStack;
public class Test {
public void a() {
b();
}
public void b() {
c();
}
public void c() {
d();
}
public void d() {
e();
}
public void e() {
int age = 0;
age = 100;
if (age == 100) {
Thread.dumpStack();
}
}
public static void main(String[] args) {
Test test = new Test();
test.a();
}
}
1.9 方法Map<Thread, StackTraceElement[]> getAllStackTraces()
作用是返回所有活动线程的堆栈信息的一个映射。
Map的key是线程对象,
而Map的value是一个StackTraceElement数组,该数组表示相应Thread的堆栈信息。
在调用该方法的同时,线程可能也在执行。每个线程的堆栈信息仅代表线程当时状态的一个快照。
package org.example.第一章Java多线程技能.N09方法Map;
import java.util.Iterator;
import java.util.Map;
public class Test1 {
public void a() {
b();
}
public void b() {
c();
}
public void c() {
d();
}
public void d() {
e();
}
public void e(){
Map<Thread,StackTraceElement[]> map=Thread.currentThread().getAllStackTraces();
if (map!=null && map.size()!=0) {
Iterator keyIterator = map.keySet().iterator();
while (keyIterator.hasNext()) {
Thread eachThread = (Thread) keyIterator.next();
StackTraceElement[] array = map.get(eachThread);
System.out.println("------每个线程的基本信息");
System.out.println(" 线程名称:" + eachThread.getName());
System.out.println(" StackTraceElement[].length=" + array.length);
System.out.println("线程状态:" + eachThread.getState());
if (array.length != 0) {
System.out.println(" 打印StackTraceElement[]数组具体信息:");
for (int i = 0; i < array.length; i++) {
StackTraceElement eachElement = array[i];
System.out.println(" " + eachElement.getClassName() +
" " + eachElement.getMethodName() +
" " + eachElement.getFileName() +
" " + eachElement.getLineNumber());
}
} else {
System.out.println(" 没有StackTraceElement[]信息,因为线程" + eachThread.getName() + "中的StackTraceElement[].length==0");
}
System.out.println();
System.out.println();
}
}
}
public static void main(String[] args) {
Test1 test1=new Test1();
test1.a();
}
}
------每个线程的基本信息
线程名称:Monitor Ctrl-Break
StackTraceElement[].length=4
线程状态:RUNNABLE
打印StackTraceElement[]数组具体信息:
java.net.InetAddress <clinit> InetAddress.java 347
java.net.InetSocketAddress <init> InetSocketAddress.java 229
java.net.Socket <init> Socket.java 287
com.intellij.rt.execution.application.AppMainV2$1 run AppMainV2.java 51
------每个线程的基本信息
线程名称:Common-Cleaner
StackTraceElement[].length=5
线程状态:TIMED_WAITING
打印StackTraceElement[]数组具体信息:
java.lang.Object wait Object.java -2
java.lang.ref.ReferenceQueue remove ReferenceQueue.java 155
jdk.internal.ref.CleanerImpl run CleanerImpl.java 140
java.lang.Thread run Thread.java 833
jdk.internal.misc.InnocuousThread run InnocuousThread.java 162
------每个线程的基本信息
线程名称:Signal Dispatcher
StackTraceElement[].length=0
线程状态:RUNNABLE
没有StackTraceElement[]信息,因为线程Signal Dispatcher中的StackTraceElement[].length==0
------每个线程的基本信息
线程名称:Finalizer
StackTraceElement[].length=4
线程状态:WAITING
打印StackTraceElement[]数组具体信息:
java.lang.Object wait Object.java -2
java.lang.ref.ReferenceQueue remove ReferenceQueue.java 155
java.lang.ref.ReferenceQueue remove ReferenceQueue.java 176
java.lang.ref.Finalizer$FinalizerThread run Finalizer.java 183
------每个线程的基本信息
线程名称:Attach Listener
StackTraceElement[].length=0
线程状态:RUNNABLE
没有StackTraceElement[]信息,因为线程Attach Listener中的StackTraceElement[].length==0
------每个线程的基本信息
线程名称:Notification Thread
StackTraceElement[].length=0
线程状态:RUNNABLE
没有StackTraceElement[]信息,因为线程Notification Thread中的StackTraceElement[].length==0
------每个线程的基本信息
线程名称:main
StackTraceElement[].length=8
线程状态:RUNNABLE
打印StackTraceElement[]数组具体信息:
java.lang.Thread dumpThreads Thread.java -2
java.lang.Thread getAllStackTraces Thread.java 1662
org.example.第一章Java多线程技能.N09方法Map.Test1 e Test1.java 24
org.example.第一章Java多线程技能.N09方法Map.Test1 d Test1.java 20
org.example.第一章Java多线程技能.N09方法Map.Test1 c Test1.java 16
org.example.第一章Java多线程技能.N09方法Map.Test1 b Test1.java 12
org.example.第一章Java多线程技能.N09方法Map.Test1 a Test1.java 8
org.example.第一章Java多线程技能.N09方法Map.Test1 main Test1.java 55
------每个线程的基本信息
线程名称:Reference Handler
StackTraceElement[].length=3
线程状态:RUNNABLE
打印StackTraceElement[]数组具体信息:
java.lang.ref.Reference waitForReferencePendingList Reference.java -2
java.lang.ref.Reference processPendingReferences Reference.java 253
java.lang.ref.Reference$ReferenceHandler run Reference.java 215
进程已结束,退出代码0
1.10 方法getId()
getId()方法可以取得线程的唯一数字标识。
package org.example.第一章Java多线程技能.N10方法getId;
public class Test1 {
public static void main(String[] args) {
Thread runThread = Thread.currentThread();
System.out.println(runThread.getName() + " " + runThread.getId());
Thread t1 = new Thread();
System.out.println(t1.getName() + " " + t1.getId());
Thread t2 = new Thread();
System.out.println(t2.getName() + " " + t2.getId());
Thread t3 = new Thread();
System.out.println(t3.getName() + " " + t3.getId());
Thread t4 = new Thread();
System.out.println(t4.getName() + " " + t4.getId());
Thread t5 = new Thread();
System.out.println(t5.getName() + " " + t5.getId());
}
}
main 1
Thread-0 24
Thread-1 25
Thread-2 26
Thread-3 27
Thread-4 28
从运行结果来看,当前执行代码的线程名为main,线程id值为1。
而Thread-0线程的id值直接到达24,说明中间有23个id值被隐藏的线程占有
11.1 停止线程
停止一个线程意味着在线程处理完任务之前停掉正在做的操作,也就是放弃当前操作。
虽然停止一个线程可以用stop方法但是并不推荐使用
大多数情况还是使用interrupt方法停止一个线程,但此方法不会停止衣蛾正在运行的线程,需要加入一个判断才可以完线程的停止
java线程停止的三种方法:
- 使用退出标志是线程正常退出
- stop强行停止,和suspend,resume一样,都是过期作废的方法
- 使用interrupt中断线程
11.1.1 停止不了的线程
interrupt方法的使用效果不像for+break那样马上停止循环,只是在线程中打上一个停止的标志。
11.1.2 判断是不是停止状态
public static boolean interrupted()
:测试currentThread是否已经中断,有清除状态的作用(连续两次调用,第二次返回false)public boolean this.isInterrupted()
:测试this关键字所在线程类的对象是否已经中断
1.11.3 清除中断状态的使用场景
package org.example.第一章Java多线程技能.N10方法getId.N03清除中断状态;
import java.lang.reflect.Array;
import java.util.ArrayList;
public class Box {
public static ArrayList list1=new ArrayList();
public static ArrayList list2=new ArrayList();
}
package org.example.第一章Java多线程技能.N10方法getId.N03清除中断状态;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
while (true) {
if (this.isInterrupted()) {
throw new InterruptedException("1 线程被中断了");
//模拟执行任务的耗时,不能用sleep,遇到interrupt方法会产生异常
}
for (int i = 0; i < 10000; i++) {
new String("" + Math.random());
}
Box.list1.add("生产数据A");
System.out.println("list1 size=" + Box.list1.size());
}
}catch (InterruptedException e){
e.printStackTrace();
}
try {
while (true){
if (this.isInterrupted()){
throw new InterruptedException("2线程被中断");
}
for (int i=0;i<10000;i++){
new String(""+Math.random());
}
Box.list2.add("生产数据B");
System.out.println("list2 size="+Box.list2.size());
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N10方法getId.N03清除中断状态;
public class Text1 {
public static void main(String[] args) throws InterruptedException{
MyThread myThread=new MyThread();
myThread.start();;
boolean list1Isinterrupted=false;
boolean list2Isinterrupted=false;
while (myThread.isAlive()){
if (Box.list1.size()>=500 && list1Isinterrupted==false){
myThread.interrupt();
list1Isinterrupted=true;
}
if (Box.list2.size()>=600 && list2Isinterrupted==false){
myThread.interrupt();
list2Isinterrupted=true;
}
Thread.sleep(50);
}
}
}
使用了isInterrupt()
方法作为判断条件,方法不会清除中断状态返回值一直是true
当使用interupted()
时,两个list都添加上了数据,清除了中断状态,进程被销毁,程序没出现问题
1.11.4 能停止的线程——异常法
package org.example.第一章Java多线程技能.N11停止线程.N04异常法;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
for (int i=0;i<600000;i++){
if (this.interrupted()){
System.out.println("已经是停止状态了,我要退出了" );
break;
}
System.out.println("i="+i);
}
System.out.println("被输出,for语句之后又继续运行,线程未停止");
}
}
package org.example.第一章Java多线程技能.N11停止线程.N04异常法;
public class Run {
public static void main(String[] args) {
try {
MyThread myThread=new MyThread();
myThread.start();
Thread.sleep(1000);
myThread.interrupt();
}catch (InterruptedException e){
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
package org.example.第一章Java多线程技能.N11停止线程.N04异常法;
public class MyThread2 extends Thread{
@Override
public void run() {
try {
super.run();
for (int i=0;i<600000;i++){
if (this.interrupted()){
System.out.println("已经是停止状态了,我要退出了" );
// break;
throw new InterruptedException();
}
System.out.println("i="+i);
}
System.out.println("我在for下面");
}catch (InterruptedException e){
System.out.println("进入MyThread中的catch");
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N04异常法;
public class Run2 {
public static void main(String[] args) {
try {
MyThread2 myThread=new MyThread2();
myThread.start();
Thread.sleep(1000);
myThread.interrupt();
}catch (InterruptedException e){
System.out.println("main catch");
e.printStackTrace();
}
System.out.println("end");
}
}
for语句后面的输出语句没有输出
1.11.5 在sleep状态下停止
package org.example.第一章Java多线程技能.N11停止线程.N05在sleep状态下停止;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(200000);
System.out.println("ren end");
}catch (Exception e){
System.out.println("在sleep下 被停止,进入catch"+this.isInterrupted());
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N05在sleep状态下停止;
public class Run {
public static void main(String[] args) {
try {
MyThread myThread=new MyThread();
myThread.start();
Thread.sleep(200);
myThread.interrupt();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("end");
}
}
不管调用顺序,只要
interrupt()
和sleep()
方法同时出现就会产生异常
1)sleep下运行interrupt方法会产生异常
2)interrupt给线程打了中断标记,再执行sleep会出现异常
1.11.6 使用stop()暴力停止线程
package org.example.第一章Java多线程技能.N11停止线程.N06暴力停止stop;
public class MyThread extends Thread{
private int i=0;
@Override
public void run() {
super.run();
try {
while (true){
i++;
System.out.println("i="+i);
Thread.sleep(1000);
}
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N06暴力停止stop;
public class Run {
public static void main(String[] args) {
try {
MyThread myThread=new MyThread();
myThread.start();
Thread.sleep(8000);
myThread.stop();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
stop方法呈删除线状态,,是过期作废的方法,
stop方法容易造成业务处理的不确定性(不能确定在哪里停止)
1.11.7 方法stop()与java.lang.ThreadDeath
package org.example.第一章Java多线程技能.N11停止线程.N07ThreadDeath;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
try {
// Thread.sleep(2000);
// int i=100;
// System.out.println("begin");
// if (i==100){
// this.stop();
// }
// System.out.println("end");
this.stop();;
}catch (ThreadDeath e){
System.out.println("进入catch");
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N07ThreadDeath;
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
}
}
package org.example.第一章Java多线程技能.N11停止线程.N07ThreadDeath;
public class MyThread2 extends Thread{
@Override
public void run() {
super.run();
try {
for (int i=1;i<500000;i++){
System.out.println("i="+i);
}
}catch (ThreadDeath e){
System.out.println("进入MyThread2下面的catch");
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N07ThreadDeath;
public class Run2 {
public static void main(String[] args) {
try {
MyThread2 myThread=new MyThread2();
myThread.start();
Thread.sleep(100);
myThread.stop();
}catch (InterruptedException e){
System.out.println("Run2下的InterruptedException");
e.printStackTrace();
}
catch (ThreadDeath e){
System.out.println("Run2下的ThreadDeath");
e.printStackTrace();
}
}
}
外界对线程对象调用stop方法时,线程内部抛出ThreadDeath异常
外界不会抛出ThreadDeath异常
1.11.8 使用stop()释放锁导致数据结果不一致
package org.example.第一章Java多线程技能.N11停止线程.N08stop释放锁导致数据结果不一致;
public class MyService {
private String username="a";
private String password="aa";
synchronized public String getUsername(){
return username;
}
synchronized public String getPassword(){
return password;
}
synchronized public void printString(String username,String password){
try {
this.username=username;
Thread.sleep(1000000);
this.password=password;
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N08stop释放锁导致数据结果不一致;
public class MyThreadA extends Thread{
public MyService myService;
public MyThreadA(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
super.run();
myService.printString("b","bb");
}
}
package org.example.第一章Java多线程技能.N11停止线程.N08stop释放锁导致数据结果不一致;
public class MyThreadB extends Thread{
public MyService myService;
public MyThreadB(MyService myService) {
super();
this.myService = myService;
}
@Override
public void run() {
super.run();
System.out.println(myService.getUsername()+" "+myService.getPassword());
System.out.println("end"+System.currentTimeMillis());
}
}
package org.example.第一章Java多线程技能.N11停止线程.N08stop释放锁导致数据结果不一致;
public class Run {
public static void main(String[] args) throws InterruptedException{
MyService myService=new MyService();
MyThreadA myThreadA=new MyThreadA(myService);
MyThreadB myThreadB=new MyThreadB(myService);
myThreadA.start();
Thread.sleep(500);
myThreadB.start();
System.out.println("begin"+System.currentTimeMillis());
Thread.sleep(500);
myThreadA.stop();
}
}
当执行stop方法时才会释放锁,线程b才能同步get方法
并且synchronized同步的方法取出的是未处理完的半成品方法
1.11.9 使用return;语句停止线程的缺点及相应解决方案
package org.example.第一章Java多线程技能.N11停止线程.N09使用return停止线程;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
while (true){
if (this.isInterrupted()){
System.out.println("停止了");
return;
}
System.out.println("time:"+System.currentTimeMillis());
}
}
}
package org.example.第一章Java多线程技能.N11停止线程.N09使用return停止线程;
public class Run {
public static void main(String[] args) throws InterruptedException{
MyThread myThread=new MyThread();
myThread.start();
Thread.sleep(2000);
myThread.interrupt();
}
}
相比于抛异常法,在代码结构上可以更方便的停止线程,不过还是建议使用抛异常法,该方法可以在catch块对异常信息进行统一处理
return
package org.example.第一章Java多线程技能.N11停止线程.N09使用return停止线程;
public class MyThread1 extends Thread{
@Override
public void run() {
super.run();
//insert
if (this.interrupted()){
System.out.println("写入log info");
return;
}
//insert
if (this.interrupted()){
System.out.println("写入log info");
return;
}
//insert
if (this.interrupted()){
System.out.println("写入log info");
return;
}
//insert
if (this.interrupted()){
System.out.println("写入log info");
return;
}
System.out.println("for for for for");
}
}
catch
package org.example.第一章Java多线程技能.N11停止线程.N09使用return停止线程;
public class MyThread2 extends Thread{
@Override
public void run() {
try {
super.run();
//insert
if (this.interrupted()){
throw new InterruptedException();
}
//insert
if (this.interrupted()){
throw new InterruptedException();
}
//insert
if (this.interrupted()){
throw new InterruptedException();
}
//insert
if (this.interrupted()){
throw new InterruptedException();
}
}catch (InterruptedException e){
System.out.println("写入log info");
e.printStackTrace();
}
}
}
catch可以进行集中处理
1.12 暂停线程
暂停线程意味着线程暂停后还能继续运行
suspend:暂停线程
resume():恢复线程
package org.example.第一章Java多线程技能.N12暂停线程.N01suspend和resume;
public class MyThread extends Thread{
private long i=0;
public long getI() {
return i;
}
@Override
public void run() {
super.run();
while (true){
i++;
}
}
}
package org.example.第一章Java多线程技能.N12暂停线程.N01suspend和resume;
import java.util.Map;
public class Run {
public static void main(String[] args) {
try {
MyThread myThread=new MyThread();
myThread.start();
Thread.sleep(5000);
myThread.suspend();
System.out.println("A="+System.currentTimeMillis()+" i="+myThread.getI());
Thread.sleep(5000);
System.out.println("A="+System.currentTimeMillis()+" i="+myThread.getI());
myThread.resume();
Thread.sleep(5000);
myThread.suspend();
System.out.println("B="+System.currentTimeMillis()+" i="+myThread.getI());
Thread.sleep(5000);
System.out.println("B="+System.currentTimeMillis()+" i="+myThread.getI());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
stop()方法用于销毁线程对象如果想要继续运行线程,则必须用start()重新启用线程
suspend()方法用于让线程不再执行任务,但线程对象不被销毁,未来还可以恢复运行
1.12.2 suspend()与resume()方法的缺点:独占
情况1:
package org.example.第一章Java多线程技能.N12暂停线程.N02独占;
public class SynchronizedObject {
synchronized public void printString(){
System.out.println("begin");
if (Thread.currentThread().getName().equals("a")){
System.out.println("a线程永远suspend了");
Thread.currentThread().suspend();
}
System.out.println("end");
}
}
package org.example.第一章Java多线程技能.N12暂停线程.N02独占;
public class Run {
public static void main(String[] args) {
try {
final SynchronizedObject object=new SynchronizedObject();
Thread thread1=new Thread(){
@Override
public void run() {
super.run();
object.printString();
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(1000);
Thread thread2=new Thread(){
@Override
public void run() {
super.run();
System.out.println("thread2启动了。但进入不了printString,只打印一个begin");
System.out.println("printString被a线程锁定并永远的暂停了");
object.printString();
}
};
thread2.start();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
package org.example.第一章Java多线程技能.N12暂停线程.N02独占;
import java.util.Map;
public class MyThread extends Thread {
private long i=0;
@Override
public void run() {
super.run();
while (true){
i++;
}
}
}
package org.example.第一章Java多线程技能.N12暂停线程.N02独占;
public class Run2 {
public static void main(String[] args) {
try {
MyThread myThread=new MyThread();
myThread.start();;
Thread.sleep(1000);
System.out.println("main end");
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
main进程虽然被销毁了,但myThread还在运行
package org.example.第一章Java多线程技能.N12暂停线程.N02独占;
import java.util.Map;
public class MyThread extends Thread {
private long i=0;
@Override
public void run() {
super.run();
while (true){
i++;
System.out.println(i);
}
}
}
当 System.out.println ()方法被暂停时,同步锁时不释放的
main线程未销毁,当因为锁未销毁,不能输出main end
1.12.3 suspend()与resume()方法的缺点:数据不完整
package org.example.第一章Java多线程技能.N12暂停线程.N03数据不完整;
public class MyObject {
private String username="1";
private String password="11";
public void setValue(String u,String p){
this.username=u;
if (Thread.currentThread().getName().equals("a")){
System.out.println("停止a线程");
Thread.currentThread().suspend();
}
this.password=p;
}
public void printup(){
System.out.println(username+" "+password);
}
}
package org.example.第一章Java多线程技能.N12暂停线程.N03数据不完整;
public class Run {
public static void main(String[] args) throws InterruptedException{
final MyObject myObject=new MyObject();
Thread thread1=new Thread(){
@Override
public void run() {
super.run();
myObject.setValue("a","aa");
System.out.println("123");
}
};
thread1.setName("a");
thread1.start();
Thread.sleep(500);
Thread thread2=new Thread(){
@Override
public void run() {
super.run();
myObject.printup();
}
};
thread2.start();
}
}
1.12.4 使用LockSupport类实现线程的暂停恢复
package org.example.第一章Java多线程技能.N12暂停线程.ThreadSupport;
import java.util.concurrent.locks.LockSupport;
public class MyThreaad1 extends Thread{
@Override
public void run() {
super.run();
System.out.println("begin"+System.currentTimeMillis());
LockSupport.park();
System.out.println("end"+System.currentTimeMillis());
}
}
package org.example.第一章Java多线程技能.N12暂停线程.ThreadSupport;
import java.util.concurrent.locks.LockSupport;
public class Run {
public static void main(String[] args) throws InterruptedException{
MyThreaad1 myThreaad1=new MyThreaad1();
myThreaad1.start();;
Thread.sleep(2000);
LockSupport.unpark(myThreaad1);
}
}
park:线程暂停
unprk:恢复线程运行
1.13 方法yield()
yield() 作用是放弃当前CPU资源,让其他任务去占用CPU的执行时间,放弃时间不确定,有可能刚刚放弃,马上又获得CPU时间片
package org.example.第一章Java多线程技能.N13yield;
public class MyThread extends Thread{
@Override
public void run() {
super.run();
long begin=System.currentTimeMillis();
int count=0;
for (int i=0;i<50000000;i++){
// Thread.yield();
count=count+(i+1);
}
long end=System.currentTimeMillis();
System.out.println("用时"+(end-begin));
}
}
package org.example.第一章Java多线程技能.N13yield;
public class Run {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
}
}
1.14 线程的优先级
在操作系统中,优先级较高的线程得到的CPU资源较多,CPU优先执行优先级较高的线程对象中的任务(让优先级较高的线程获得更多的CPU时间片)
1.14.1 线程优先级的继承特性
A线程启动B线程,A、B线程优先级一样
package org.example.第一章Java多线程技能.N14优先级.N01继承特性;
public class MyThread1 extends Thread{
@Override
public void run() {
super.run();
System.out.println("A:"+this.getPriority());
MyThread2 myThread2=new MyThread2();
myThread2.start();
}
}
package org.example.第一章Java多线程技能.N14优先级.N01继承特性;
public class MyThread2 extends Thread{
@Override
public void run() {
super.run();
System.out.println("B"+this.getPriority());
}
}
package org.example.第一章Java多线程技能.N14优先级.N01继承特性;
public class Run {
public static void main(String[] aSSrgs) {
System.out.println("main begin:"+Thread.currentThread().getPriority());
// Thread.currentThread().setPriority(7);
System.out.println("main end"+Thread.currentThread().getPriority());
MyThread1 myThread1=new MyThread1();
myThread1.start();
}
}
1.14.2 线程优先级的规律性
package org.example.第一章Java多线程技能.N14优先级.N02规律性;
import java.util.Random;
public class MyThread1 extends Thread{
@Override
public void run() {
super.run();
Long beginTime=System.currentTimeMillis();
Long addResult= Long.valueOf(0);
for (int i=0;i<50000;i++){
Random random=new Random();
random.nextInt();
addResult=addResult+i;
}
long endTime=System.currentTimeMillis();
System.out.println("Thread1:"+(endTime-beginTime));
}
}
package org.example.第一章Java多线程技能.N14优先级.N02规律性;
import java.util.Random;
public class MyThread2 extends Thread{
@Override
public void run() {
super.run();
Long beginTime=System.currentTimeMillis();
Long addResult= Long.valueOf(0);
for (int i = 0; i < 50000; i++) {
Random random = new Random();
random.nextInt();
addResult = addResult + i;
}
long endTime=System.currentTimeMillis();
System.out.println("Thread2:"+(endTime-beginTime));
}
}
package org.example.第一章Java多线程技能.N14优先级.N02规律性;
public class Run {
public static void main(String[] args) {
for (int i=1;i<10;i++){
MyThread1 myThread1=new MyThread1();
myThread1.setPriority(1);
MyThread2 myThread2=new MyThread2();
myThread2.setPriority(10);
myThread1.start();
myThread2.start();
}
}
}
Thread2:63
Thread2:44
Thread2:66
Thread2:62
Thread2:64
Thread2:47
Thread2:58
Thread1:67
Thread1:64
Thread2:71
Thread1:68
Thread1:65
Thread2:76
Thread1:36
Thread1:83
Thread1:37
Thread1:38
Thread1:86
1.14.3 线程优先级的随机性
不要把优先级和运行结果的顺序衡量
虽然线程的优先级较高的先执行,但不一定是先执行完。
1.15 守护线程
java有两种线程,一种是用户线程,也称非守护线程,另一种是守护线程
什么是守护线程
进程中不存在守护线程时,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程,则垃圾回收线程也就没有存在的必要了,自动销毁
守护Daemon线程的作用是为其他线程的运行提供便利的服务,最典型的GC(垃圾回收器)。
最后一个用户线程销毁了,进程也随之结束了
1.16 并发与并行
并发:一个CPU同时执行多个任务
并行:多个CPU或多核CPU同时执行多个任务
A同学一边打游戏,一边写作业,一边吃饭就是并发
A同学让B同学帮她发游戏,让C同学帮她写作业,自己吃饭就是并行
1.17 同步和异步
同步:需要等待处理的结果才能运行
异步:不需要等待处理的结果还能运行