Java多线程详解
实现线程的三种方式
1.1继承Thread
public class MyThread {
public static void main(String[] args) {
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
}
}
//继承Thread实现多线程
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
this.name=name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
也可能出现这种运行结果
说明
程序在启动main函数时,Java虚拟机就已经启动了一个主线程来运行main函数,在调用到
mTh1
,mTh2
的start方法时,就相当于有三个线程在同时工作了,这就是多线程的模式,进入了mTh1
子线程,这个线程中的操作,在这个线程中有sleep()
方法,Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。
实际上所有的线程执行顺序都是不确定的,CPU资源的获取完全是看两个线程之间谁先抢占上谁就先运行,当mTh1
抢占上线程后,运行run
方法中的代码,到sleep()
方法进入休眠状态,也就是阻塞状态,然后CPU
资源会被释放,A
,B
再次进行抢占CPU
资源操作,抢占上的继续运行。在运行的结果中你也可以看到这个现象。
注意:
一个实例的start()方法不能重复调用,否则会出现java.lang.IllegalThreadStateException
异常。
1.2实现Runnable接口
package com.hjt.multithreading;
class Thread2 implements Runnable{
private String name;
public Thread2(String name) {
this.name=name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class MyRunnable {
public static void main(String[] args) {
new Thread(new Thread2("C")).start();
new Thread(new Thread2("D")).start();
}
}
结果
说明:
Thread2
类通过实现Runnable
接口,使得该类有了多线程类的特征。run()
方法是多线程程序的一个约定。所有的多线程代码都在run
方法里面。Thread
类实际上也是实现了Runnable
接口的类。在启动的多线程的时候,需要先通过
Thread
类的构造方法Thread(Runnable target)
构造出对象,然后调用Thread对象的start()方法来运行多线程代码。实际上所有的多线程代码都是通过运行
Thread
的start()
方法来运行的。因此,不管是扩展Thread类还是实现Runnable
接口来实现多线程,最终还是通过Thread
的对象的API
来控制线程的,熟悉Thread
类的API
是进行多线程编程的基础。
Thread类和Runnable接口的区别
如果一个类继承Thread
,则不适合资源共享。但是如果实现了Runable
接口的话,则很容易的实现资源共享。
总结:
实现Runnable
接口比继承Thread
类所具有的优势:
1):适合多个相同的程序代码的线程去处理同一个资源
2):可以避免java
中的单继承的限制
3):增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4):线程池只能放入实现Runable
或callable
类线程,不能直接放入继承Thread
的类
提醒一下大家:main方法其实也是一个线程。在java
中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java
中,每次程序运行至少启动2个线程。一个是main
线程,一个是垃圾收集线程。因为每当使用java
命令执行一个类的时候,实际上都会启动一个JVM
,每一个JVM
实习在就是在操作系统中启动了一个进程。
1.3 Callable实现返回值
package com.hjt.multithreading;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class MyCallable {
public static void main(String[] args) throws Exception {
MyThread1 myThread1 = new MyThread1();
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread1());
new Thread(futureTask).start();//开启线程
System.out.println(futureTask.get());//获取返回值
}
}
class MyThread1 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int count = 0;
for (int i = 1;i<100;i++){
if (i%3==0){
count++;
}
}
return count;
}
}
1.4多线程运行过程
1:新建状态(New):new Thread(),新创建了一个线程;
2:就绪状态(Runnable):新建完成后,主线程(main()方法)调用了该线程的start()方法,CPU目前在执行其他任务或者线程,这个创建好的线程就会进入就绪状态,等待CPU资源运行程序,在运行之前的这段时间处于就绪状态;
3:运行状态(Running):字面意思,线程调用了start()方法之后并且抢占到了CPU资源,运行run方法中的程序代码;
4:阻塞状态(Blocked):阻塞状态时线程在运行过程中因为某些操作暂停运行,放弃CPU使用权,进入就绪状态和其他线程一同进行下次CPU资源的抢占。
当发生如下情况时,线程将会进入阻塞状态
① 线程调用sleep()方法主动放弃所占用的处理器资源
② 线程调用了一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
③ 线程试图获得一个同步监视器,但该同步监视器正被其他线程所持有。关于同步监视器的知识、后面将有深入的介绍
④ 线程在等待某个通知(notify)
⑤ 程序调用了线程的suspend()方法将该线程挂起。但这个方法容易导致死锁,所以应该尽量避免使用该方法
当前正在执行的线程被阻塞之后,其他线程就可以获得执行的机会。被阻塞的线程会在合适的时候重新进入就绪状态,注意是就绪状态而不是运行状态。也就是说,被阻塞线程的阻塞解除后,必须重新等待线程调度器再次调度它。
解除阻塞
针对上面几种情况,当发生如下特定的情况时可以解除上面的阻塞,让该线程重新进入就绪状态:
① 调用sleep()方法的线程经过了指定时间。
② 线程调用的阻塞式IO方法已经返回。
③ 线程成功地获得了试图取得的同步监视器。
④ 线程正在等待某个通知时,其他线程发出了个通知。
⑤ 处于挂起状态的线程被调甩了resdme()
恢复方法(会导致死锁,尽量避免使用)。
5:死亡状态(Dead):线程程序执行完成或者因为发生异常跳出了run()方法,线程生命周期结束。
1.5多线程如何正确优雅的中断线程
1、在catch子句中,调用Thread.currentThread.interrupt()来设置中断状态(因为抛出异常后中断标示会被清除)
让外界通过判断Thread.currentThread().isInterrupted()标示来决定是否终止线程还是继续下去,应该这样做
void mySubTask() {
...
try {
sleep(delay);
} catch (InterruptedException e) {
Thread.currentThread().interrupted();
}
...
}
2、或者,更好的做法就是,不使用try来捕获这样的异常,让方法直接抛出
void mySubTask() throws InterruptedException {
...
sleep(delay);
...
}
个人搭建项目代码地址:
https://github.com/hongjiatao/spring-boot-anyDemo
欢迎收藏点赞三连。谢谢!有问题可以留言博主会24小时内无偿回复。