1、解释一:并行是指两个或者多个事件在同一时刻发生;而并发是指两个或多个事件在同一时间间隔发生;
2、解释二:并行是在不同实体上的多个事件,并发是在同一实体上的多个事件;
3、解释三:并行是在一台处理器上"同时"处理多个任务,并发是在多台处理器上同时处理多个任务。如 hadoop 分布式集群;
所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能;
1、并行在
多处理器系统
中存在,而并发可以在单处理器和多处理器系统中
都存在;
2、并发能够在单处理器系统中存在是因为并发是并行的假象;
3、并行要求程序能够同时执行多个操作,而并发只是要求程序假装同时执行多个操作(每个小时间片执行一个操作,多个操作快速切换执行);
当有多个线程在操作时,如果系统只有一个 CPU,则它根本不可能真正同时进行一个以上的线程,它只能把 CPU 运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状态,这种方式我们称之为并发(Concurrent);
当系统有一个以上 CPU 时,则线程的操作有可能非并发。当一个 CPU 执行一个线程时,另一个 CPU 可以执行另一个线程,两个线程互不抢占 CPU 资源,可以同时进行,这种方式我们称之为并行(Parallel);
- 进程:正在运行的应用程序(QQ、微信、IDEA、WPS),一个进程中最少需要有一个线程,如果线程数为0了,进程被迫也就停止了;;
- 线程:它依赖于进程存在,没有进程,谈不上线程;
进程是拥有资源的基本单位,进程间的资源是不共享的;
线程是程序使用CPU的基本单位,也就是CPU调度的基本单位;
进程:每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销,一个进程包含1–n个线程;(进程是资源分配的最小单位)
线程:同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC),线程切换开销小;
进程和线程之间的关系:
比如QQ音乐运行了,这时候这个应用程序属于一个
进程
,QQ音乐里面可能需要执行很多不同的任务,比如歌曲下载、歌曲的播放……,这些任务都属于线程
;
现在的计算机,Windows电脑支持多进程,可以同时多个进程运行;我在电脑上开启了WPS和QQ音乐,我感觉我一边在编辑文字,一边在听歌,是同时进行的,那么 计算机在同一个时间点上,是同时进行多个进程的吗?
- 当然不是,因为计算机在同一时间点上只能运行一个进程,你的感觉是多个进程在同时执行,这是因为CPU在多个进程之间高速的切换,我们指的是 单核CPU(
用来处理任务的核心数量
),你的人耳和眼睛根本感觉不到;当然,4核或者多核CPU可以实现真正意义上的同时运行多个进程;
多进程的意义在哪?
- 多进程存在的意义不是提高了执行速度,而是 提高了CPU的使用率;
- 我们程序在运行时的使用,都是在抢CPU的时间片(执行权),如果是多线程的程序,那么在抢到CPU的执行权的概率应该比较单线程程序抢到的概率要大;也就是说,CPU在多线程程序中执行的时间要比单线程多,所以就提高了CPU的使用率;
- 因为CPU本身作为硬件,一直在不断的提高自己的性能,因此我们支持多线程环境,以便于CPU的资源不被浪费;
- 但是即使是多线程程序,那么他们中的哪个线程能抢占到CPU的资源呢,这个是不确定的,所以 多线程具有随机性;
1、在程序开发当中,将一个对象从实例化完成,到这个对象使用结束,并被销毁,这样的过程称之为对象的一生
,这就类似于人的一生;
2、线程的生命周期:一个线程被实例化完成,(线程在Java中也是一个对象),到这个线程销毁的过程;
3、线程的状态:
一个线程被实例化完成,但是还没有做任何操作;
一个线程已经被开启,已经开始去争抢CPU时间片;
一个线程抢到了CPU时间片,开始运行这个线程的逻辑;
一个线程在运行的过程中,受到某些操作的影响,放弃了已经获取到的CPU时间片,并且不再去参与CPU时间片的争抢,此时线程处于挂起状态(暂停);
一个线程对象需要被销毁;
Java命令会启动JVM,等于启动了一个应用程序,也就是启动了一个进程;
该进程会自动启动一个 “主线程” ,然后主线程去调用某个类的 main 方法,
所以 main方法运行在主线程中;
JVM的启动至少启动了
垃圾回收线程和主线程
,所以是多线程的;
自定义类继承Thread,重写该类里面的run();一般来说,我们
把一些耗时的代码写在run()
,而不会写到主线程中,阻塞主线程;
run()里面本质上就是需要并发执行的任务;
---测试类:
public class MyTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.run();
//这是一些耗时的代码
}
}
---自定义线程类:
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("这是一些耗时的代码");
}
}
这里注意,你没有发现吗?其实调用自定义线程类里面的run(),只是做了简单的创建自定义类,new对象,调方法,线程并没有真正意义上的开启,或者说线程不会进入就绪态;
正确开启线程的方法:
new对象,使对象调用start()方法,是线程开启的正确方法
;- 当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。但是这并不意味着线程就会立即运行。只有当cpu分配时间片时,这个线程获得时间片时,才开始执行run()方法;
- start()来启动线程,无需等待run()里面的代码执行完毕,可以直接继续执行下面的代码;这里把run()叫做线程体,它包含了要执行的这个线程的内容,run()运行结束,此线程终止,然后CPU再调度其他线程;
- run()当做普通方法的方式调用,程序还是要顺序执行,要等待run()方法体执行完毕之后,才可以继续执行下面的代码;程序只有主线程这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的;
- 需要注意的是,线程不能重复开启;;
public class MyTest1 {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
System.out.println("这是后面的代码");
/*这是后面的代码
这是一些耗时的代码*/
}
}
- 实现Runnable接口,这种方式
扩展性强,实现一个接口 还可以再去继承其他类
;
a:如何获取线程名称
b:如何给线程设置名称
c:实现接口方式的好处- 可以避免由于Java单继承带来的局限性;
---测试类:
public class MyTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
//Thread-0这是一些耗时代码
}
}
----自定义类:
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "这是一些耗时代码");
}
}
Runnable本质上就是一个任务,它是一个接口,里面只有一个run();
注意:这个类里面不能调用this.getName()来获取线程名字,因为他本质不是一个Thread;
- 实现 Callable 接口。 相较于实现 Runnable 接口的方式,方法可以有返回值,并且可以抛出异常。
- 执行 Callable 方式,需要 FutureTask 实现类的支持,用于
接收运算结果
,( FutureTask 是 Future 接口的实现类);- 实现步骤:
1.创建一个类实现Callable 接口
2.创建一个FutureTask类将Callable接口的子类对象作为参数传进去
3.创建Thread类,将FutureTask对象作为参数传进去
4.开启线程
---测试类:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyTest {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyCallable myCallable = new MyCallable(5);
FutureTask<Integer> task = new FutureTask<Integer>(myCallable);
Thread thread = new Thread(task);
thread.start();
System.out.println("这个任务返回的结果是:" + task.get());
// 这是一个任务Thread-0
// 这个任务返回的结果是:10
}
}
---自定义类:
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
private int num = 0;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
System.out.println("这是一个任务" + Thread.currentThread().getName());
int sum = 0;
for (int i = 0; i < num; i++) {
sum += i;
}
return sum;
}
}
- Runnable 任务 让线程来执行 run() 没有返回值,这个方法不能抛出异常,只能抓;
- Callable 任务 让线程来执行 call() 有返回值,而且可以抛出异常。
public final String getName()
返回该线程的名称;
public final void setName(String name)
改变线程名称,使之与参数 name 相同;
public Thread(String name)
线程的有参构造需要一个线程名字,可以直接给线程设置名字;当然,自定义线程类可以添加一个有参构造,将线程名字传递给父类构造super();
---测试类:
public class MyTest2 {
public static void main(String[] args) {
MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
myThread1.setName("1线程");
myThread2.setName("2线程");
myThread1.start();
myThread2.start();
System.out.println("主线程的代码");
/*主线程的代码
这是一些耗时的代码2线程
这是一些耗时的代码1线程*/
/*主线程的代码
这是一些耗时的代码1线程
这是一些耗时的代码2线程*/
}
}
---自定义线程类:
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("这是一些耗时的代码" + this.getName());
}
}
两个线程交替执行,高速的切换,没有规律,线程的调度是随机性的;
多线程并发执行,抢占CPU执行权,哪个线程抢到,在某一时刻就执行哪个线程;
Thread.currentThread().getName():
静态方法,可以获取当前正在执行的线程名称;
线程对象.getName()
成员方法,只能使用对象调用,获取当前线程名称;
public class MyTest3 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
//获取当前线程
System.out.println(thread.getName());
//main
Thread thread1 = new Thread();
System.out.println(thread1.getName());
//Thread-0
}
}
---测试类:
public class MyTest4 {
public static void main(String[] args) {
CopyText copyText = new CopyText();
CopyVideo copyVideo = new CopyVideo();
copyText.setName("text");
copyVideo.setName("video");
copyText.start();
copyVideo.start();
System.out.println("主线程后面的代码");
/*主线程后面的代码
这是复制文本文件的耗时操作text
这是复制视频文件的耗时操作video*/
}
}
----线程类:
public class CopyText extends Thread {
@Override
public void run() {
System.out.println("这是复制文本文件的耗时操作" + this.getName());
}
}
public class CopyVideo extends Thread {
@Override
public void run() {
System.out.println("这是复制视频文件的耗时操作" + this.getName());
}
}
需求:同一个种类的3个不同线程复制一个文件;
平均复制,分为3份,使用随机访问流;
代码实现:
---测试类:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
public class MyTest {
public static void main(String[] args) throws FileNotFoundException {
//封装源文件
File file = new File("D:\\桌面图标\\文件程序练习打包\\demo\\亲爱的旅人啊.mp3");
//获取文件总大小
long totalLenth = file.length();
//定义线程数量
int threadNum = 3;
//计算平均每个线程要复制的文件的大小
long aveSize = totalLenth / threadNum;
long start = 0;
long end = 0;
for (int i = 0; i < threadNum; i++) {
//定义复制文件的起始位置和结束位置
start = aveSize * i;
end = aveSize * (i + 1) - 1;
new copyThread(start, end, file, new File("D:\\桌面图标\\copy.mp3")).start();
}
//如果不够平均分,剩余的字节使用另外一个线程完成
if (totalLenth % threadNum != 0) {
start = aveSize * threadNum;
end = totalLenth;
new copyThread(start, end, file, new File("D:\\桌面图标\\copy.mp3")).start();
}
System.out.println("复制完成了!");
}
}
---复制文件的线程类
class copyThread extends Thread {
private long end;
private RandomAccessFile in = null;
private RandomAccessFile out = null;
private long start;
/***
*
* @param start 要复制文件的起始位置
* @param end 要复制文件的结束位置
* @param srcFile 原文件夹
* @param target 目标文件夹
* @throws FileNotFoundException
*/
public copyThread(long start, long end, File srcFile, File target) throws FileNotFoundException {
this.start = start;
this.end = end;
in = new RandomAccessFile(srcFile, "rw");
out = new RandomAccessFile(target, "rw");
}
@Override
public void run() {
//设置要复制文件与目标文件的起始指针位置
try {
in.seek(start);
out.seek(start);
int len = 0;
byte[] bytes = new byte[1024 * 8];
while (start < end && (len = in.read(bytes)) != -1) {
start += len;
out.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
in.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
假如我们的计算机只有一个 CPU,那么 CPU 在某一个时刻只能执行一条指令,
线程只有得到 CPU时间片,也就是使用权,才可以执行指令。那么Java是如何对线程进行调用的呢?
分时调度:UNIX;
抢占式调度:Windows、Java;
在上面的程序执行当中,有这样一个现象,无论子线程如何争抢CPU资源,随机调度,主线程的代码总会在子线程之前执行,这是为什么?
- 其实这是由于线程的优先级;
- 在一个线程中开启另外一个新线程,则新开线程称为该线程的子线程,子线程初始优先级与父线程相同,不过主线程先启动占用了cpu资源,这就造成了主线程不会被阻塞;
- 如果存在主线程和子线程争抢cpu执行权的话,看运气,谁抢到就让谁执行;
public final int getPriority()
返回线程的优先级;
public final void setPriority(int newPriority)
更改线程的优先级;
- 线程的优先级用1-10之间的整数表示,数值越大优先级越高,默认的优先级为5;
- 其实设置了优先级,也无法保障线程的执行次序。只不过,优先级高的线程获取CPU资源的概率较大,优先级低的并非没机会执行;
- 因为线程的优先级的大小仅仅表示这个线程被CPU执行的概率增大了,但是我们都知道多线程具有随机性,所以有的时候一两次的运行说明不了问题;
---测试类:
public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
MyThread th3 = new MyThread();
//设置线程的优先级 范围1--10
th1.setPriority(Thread.MIN_PRIORITY);
th3.setPriority(Thread.MAX_PRIORITY);
//多个线程并发执行()多个抢占CPU的执行权,哪个线程抢到,在某一个时刻,就会执行哪个线程。线程的执行具有随机性。
//获取线程的优先级
int priority1 = th1.getPriority();
int priority2 = th2.getPriority();
int priority3 = th3.getPriority();
//线程默认的优先级是5
//Thread.MAX_PRIORITY;
//Thread.MIN_PRIORITY;
//Thread.NORM_PRIORITY
System.out.println("线程的优先级" + priority1);
//线程的优先级1
System.out.println("线程的优先级" + priority2);
//线程的优先级5
System.out.println("线程的优先级" + priority3);
//线程的优先级10
th2.setPriority(2);
th1.setName("zhangsan");
th2.setName("lisi");
th3.setName("wangwu");
th1.start();
th2.start();
th3.start();
// 这是一些耗时的代码wangwu
// 这是一些耗时的代码zhangsan
// 这是一些耗时的代码lisi
}
}
----自定义线程类:
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("这是一些耗时的代码" + this.getName());
}
}
让当前正在运行的线程休眠,睡一会;
public static void sleep(long millis) throws InterruptedException
在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响,该线程不丢失任何监视器的所属权。sleep()平台移植性好
。
public class MyTest1 {
public static void main(String[] args) throws InterruptedException {
//单位是毫秒
//让当前的主线程睡眠2秒
Thread.sleep(2000);
System.out.println("下面的代码");
}
}
需求:我们在浏览一些网页或者打开APP的时候。总是会出现3s或者5s的广告,这个就可以通过线程休眠来做;
----测试类:
public class MyTest2 {
public static void main(String[] args) throws InterruptedException {
//线程休眠:可以让当前正在执行的线程, 睡一会。
//Thread(String name) 分配新的 Thread 对象。
//通过有参构造,可以给线程起个名字
MyThread th1 = new MyThread("广告线程");
MyThread th2 = new MyThread("主页线程");
th1.setPriority(Thread.MAX_PRIORITY);
th2.setPriority(Thread.MIN_PRIORITY);
th1.start();
th2.start();
// 广告线程执行了
// 主页线程执行了
}
}
---线程类:
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
try {
if(this.getName().equals("广告线程")){
Thread.sleep(3000); //单位是毫秒
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName() + "执行了");
}
}
加入线程:
public final void join()
意思就是: 等待该线程执行完毕了以后,其他线程才能再次执行;
注意事项: 在线程启动之后,再调用方法;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
MyThread th3 = new MyThread();
th1.setName("刘备");
th2.setName("关羽");
th3.setName("张飞");
th1.start();
//注意:在线程启动之后, 在调用join()方法
th1.join();
th2.start();
th2.join();
th3.start();
th3.join();
// 这是一些耗时的代码刘备
// 这是一些耗时的代码关羽
// 这是一些耗时的代码张飞
}
}
public class MyThread extends Thread {
@Override
public void run() {
System.out.println("这是一些耗时的代码" + this.getName());
}
}
在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到join()方法了;
没有加Join()
public class MyTest {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
MyThread th1 = new MyThread("A");
MyThread th2 = new MyThread("B");
th1.start();
th2.start();
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
}
}
class MyThread extends Thread {
private String name;
public MyThread(String name) {
super(name);
this.name = name;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 线程运行开始!");
for (int i = 0; i < 5; i++) {
System.out.println("子线程" + name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 线程运行结束!");
}
}
main主线程运行开始!
main主线程运行结束!
A 线程运行开始!
子线程A运行 : 0
B 线程运行开始!
子线程B运行 : 0
子线程A运行 : 1
子线程B运行 : 1
子线程A运行 : 2
子线程B运行 : 2
子线程A运行 : 3
子线程B运行 : 3
子线程A运行 : 4
子线程B运行 : 4
A 线程运行结束!
B 线程运行结束!
会发现主线程比子线程结束的要早;
加入Join()
public class MyTest1 {
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + "主线程运行开始!");
MyThread th1 = new MyThread("A");
MyThread th2 = new MyThread("B");
th1.start();
th2.start();
try {
th1.join();
th2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "主线程运行结束!");
}
}
main主线程运行开始!
A 线程运行开始!
B 线程运行开始!
子线程B运行 : 0
子线程A运行 : 0
子线程B运行 : 1
子线程A运行 : 1
子线程B运行 : 2
子线程A运行 : 2
子线程B运行 : 3
子线程A运行 : 3
子线程B运行 : 4
子线程A运行 : 4
B 线程运行结束!
A 线程运行结束!
main主线程运行结束!
主线程一定会等子线程都结束了才结束;
礼让线程:
public static void yield():
- 暂停当前正在执行的线程对象,并执行其他线程;
- yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。
public class MyTest {
public static void main(String[] args) {
threadYield();
}
//线程的礼让:指的就是让当前的运行状态的线程释放自己的CPU资源,由运行状态回到就绪状态
public static void threadYield() {
Runnable r = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=======" + i);
if (i == 3) {
Thread.yield();
}
}
}
};
Thread thread1 = new Thread(r, "thread-1");
Thread thread2 = new Thread(r, "thread-2");
thread1.start();
thread2.start();
}
}
某一次运行结果:
thread-1=======0
thread-2=======0
thread-2=======1
thread-2=======2
thread-1=======1
thread-2=======3
thread-1=======2
thread-1=======3
thread-2=======4
thread-2=======5
thread-2=======6
thread-2=======7
thread-2=======8
thread-2=======9
thread-1=======4
thread-1=======5
thread-1=======6
thread-1=======7
thread-1=======8
thread-1=======9
另外一次运行结果:
thread-1=======0
thread-1=======1
thread-1=======2
thread-1=======3
thread-1=======4
thread-1=======5
thread-2=======0
thread-2=======1
thread-2=======2
thread-2=======3
thread-1=======6
thread-1=======7
thread-1=======8
thread-1=======9
thread-2=======4
thread-2=======5
thread-2=======6
thread-2=======7
thread-2=======8
thread-2=======9
- 按照我们的想法,这个礼让应该是一个线程执行一次,但是通过我们的测试,效果好像不太明显;那是为什么呢?
这个礼让是要暂停当前正在执行的线程,放弃了当前抢到的CPU时间片,这个暂停的时间是相当短的,如果在这个线程暂停完毕以后,其他的线程还没有抢占到CPU的执行权,那么这个时候这个线程应该再次和其他线程抢占CPU的执行权;因此不是说A礼让了就一定要执行B;
①:sleep()使当前线程进入停滞状态,所以执行sleep()的线程在指定的时间内肯定不会被执行;yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
②:sleep 方法使当前运行中的线程睡眼一段时间,进入不可运行状态,这段时间的长短是由程序设定的,yield 方法使当前线程让出 CPU 占有权,但让出的时间是不可设定的。实际上,yield()方法对应了如下操作:先检测当前是否有相同优先级的线程处于同可运行状态,如有,则把 CPU 的占有权交给此线程,否则,继续运行原来的线程。所以yield()方法称为“退让”,它把运行机会让给了同等优先级的其他线程;
③:另外,sleep 方法允许较低优先级的线程获得运行机会,但 yield() 方法执行时,当前线程仍处在可运行状态,所以,不可能让出较低优先级的线程些时获得 CPU 占有权。在一个运行系统中,如果较高优先级的线程没有调用 sleep 方法,又没有受到 I/O 阻塞,那么,较低优先级线程只能等待所有较高优先级的线程运行结束,才有机会运行;
守护线程:
public final void setDaemon(boolean on):
- true代表为守护线程,默认为false;
- 将该线程标记为守护线程或用户线程,当正在运行的线程都是守护线程时,Java 虚拟机退出;
- 该方法必须在启动线程前调用;
- 一般子线程是守护线程,主线程死亡,守护线程立马挂掉;
---测试程序:
public class MyTest {
public static void main(String[] args) {
MyThread th1 = new MyThread();
MyThread th2 = new MyThread();
th1.setName("张飞");
th2.setName("关羽");
//设置为守护线程 当主线程死亡后,守护线程要立马死亡掉。
//注意:setDaemon(true)该方法必须在启动线程前调用。
th1.setDaemon(true);
th2.setDaemon(true);
th1.start();
th2.start();
Thread.currentThread().setName("刘备:主线程");
for (int i = 0; i < 2; i++) {
System.out.println(Thread.currentThread().getName() + "==" + i);
}
System.out.println(Thread.currentThread().getName() + "退出了");
// 刘备:主线程==0
// 刘备:主线程==1
// 张飞0
// 刘备:主线程退出了
// 张飞1
// 张飞2
// 张飞3
// 张飞4
}
}
----自定义线程类:
public class MyThread extends Thread {
public MyThread() {
}
public MyThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + i);
}
}
}
用户线程和守护线程都是线程,区别是Java虚拟机在所有用户线程dead后,程序就会结束。而不管是否还有守护线程还在运行,若守护线程还在运行,则会马上结束。很好理解,守护线程是用来辅助用户线程的,如公司的保安和员工,各司其职,当员工都离开后,保安自然下班了。
- 用户线程和守护线程的适用场景
由两者的区别及dead时间点可知,守护线程不适合用于输入输出或计算等操作,因为用户线程执行完毕,程序就dead了,适用于辅助用户线程的场景,如JVM的垃圾回收,内存管理都是守护线程,还有就是在做数据库应用的时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监听连接个数、超时时间、状态等。- 创建守护线程
调用线程对象的方法setDaemon(true),设置线程为守护线程。
1、thread.setDaemon(true)必须在thread.start()之前设置;
2、在Daemon线程中产生的新线程也是Daemon的;
3、不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑;因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。- Java守护线程和Linux守护进程
两者不是一个概念,Linux守护进程是后台服务进程,没有控制台。
在Windows中,你可以运行javaw来达到释放控制台的目的,在Unix下你加&在命令的最后就行了,所以守护进程并非一定需要的;
public final void stop():
停止线程的运行
public void interrupt():
中断线程,查看API可得当线程调用wait()、sleep(long time)方法的时候处于阻塞状态,可以通过这个方法清除阻塞;
public class MyTest {
public static void main(String[] args) throws InterruptedException {
MyThread th1 = new MyThread();
th1.setName("张飞");
th1.start();
Thread.sleep(2000);
//让线程死亡
th1.stop();
//清除线程的阻塞状态
//th1.interrupt();
}
}
interrupt()
:不要以为它是中断某个线程,它只是给线程发送一个中断信号,让线程在无限等待时(如死锁时)能抛出,从而结束线程,但是如果你吃掉了这个异常,那么这个线程还是不会中断的;