Thread
上一篇我们已经知道了线程的调度,Java的多线程Thread---API文档深入研究1.0
接下来我们就可以使用如下方法对线程进行控制。
目录
线程休眠public static void sleep(long millis)
线程礼让public static void yield()
后台线程public final void setDaemon(boolean on)
线程控制
线程休眠public static void sleep(long millis)
查看API文档我们知道:
public static void sleep(long millis) throws InterruptedException使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 线程不会丢失任何显示器的所有权。
参数
millis
- 以毫秒为单位的睡眠时间长度异常
IllegalArgumentException
- 如果millis
值为负数
InterruptedException
- 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。
创建MySleepThread类:
public class MySleepThread extends Thread {
@Override
public void run() {
for (int i = 0;i < 100;i++){
System.out.println(getName() + ":" + i);
//加入一个休眠方法
//睡2秒钟,1秒=1000毫秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
参考代码:
public class ThreadSleepDemo {
public static void main(String[] args) {
MySleepThread t1 = new MySleepThread();
MySleepThread t2 = new MySleepThread();
MySleepThread t3 = new MySleepThread();
t1.setName("刘德华");
t2.setName("张学友");
t3.setName("郭富城");
t1.start();
t2.start();
t3.start();
}
}
运行结果
线程加入public final void join()
查看API文档我们知道:
public final void join()throws InterruptedException等待这个线程死亡。
调用此方法的行为方式与调用完全相同
join(0)
异常
InterruptedException
- 如果任何线程中断当前线程。 当抛出此异常时,当前线程的中断状态将被清除。
创建MyJoinThread类:
public class MyJoinThread extends Thread {
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println(getName() + "+" + i);
}
}
}
参考代码:
public class ThreadJoinDemo {
public static void main(String[] args) {
MyJoinThread t1 = new MyJoinThread();
MyJoinThread t2 = new MyJoinThread();
MyJoinThread t3 = new MyJoinThread();
t1.setName("刘德华");
t2.setName("张学友");
t3.setName("郭富城");
t3.start();
try {
t3.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
t1.start();
t2.start();
}
}
运行结果:
从运行结果上可以看出:先将t3线程郭富城执行完毕,t1、t2线程等待线程t3执行完毕。
线程礼让public static void yield()
查看API文档我们知道:
public static void yield()对调度程序的一个暗示,即当前线程愿意产生当前使用的处理器。 调度程序可以自由地忽略这个提示。
产量是一种启发式尝试,以改善否则会过度利用CPU的线程之间的相对进度。 其使用应与详细的分析和基准相结合,以确保其具有预期的效果。
很少使用这种方法。 它可能对调试或测试有用,可能有助于根据种族条件重现错误。 在设计并发控制结构(例如
java.util.concurrent.locks
包中的并行控制结构)时也可能有用。
创建MyYieldThread类:
public class MyYieldThread extends Thread{
@Override
public void run() {
for (int i = 0;i < 100;i++){
System.out.println(getName() + ":" + i);
Thread.yield();
}
}
}
参考代码:
public class ThreadYieldDemo {
public static void main(String[] args) {
MyYieldThread t1 = new MyYieldThread();
MyYieldThread t2 = new MyYieldThread();
t1.setName("彭于晏");
t2.setName("周杰伦");
t1.start();
t2.start();
}
}
运行结果:
从运行结果上来看两个线程有交替执行的现象,这就是线程的礼让
后台线程public final void setDaemon(boolean on)
查看API文档我们知道:
public final void setDaemon(boolean on)将此线程标记为daemon线程或用户线程。 当运行的唯一线程都是守护进程线程时,Java虚拟机将退出。
线程启动前必须调用此方法。
参数
on
- 如果true
,将此线程标记为守护线程异常
IllegalThreadStateException
- 如果这个线程是 alive
SecurityException
- 如果checkAccess()
确定当前线程不能修改此线程
类似于植物大战僵尸,后面一排车就是一个后台线程也称守护线程,一排绿色植物守护后面的向日葵这也是守护线程,南瓜头保护者火桩也是守护线程。
如果一个程序都是守护线程的话,程序就停止了。
java中有两类线程:用户线程,守护线程
- 用户线程:我们再学习多线程之前的所有代码,运行起来的时候都是一个个的用户线程
- 守护线程:所谓的守护线程,指的就是程序运行的时候再后台提供一个通用的服务线程比如说垃圾回收线程,它就是一个称职的守护线程,并且这个线程是程序不可或缺一部分或者说只要程序存在非守护线程,程序就不会停止,如果一个程序都是守护程序,程序就停止了。
设置守护线程必须在启动之前设置
创建MyDaemonThread类:
public class MyDaemonThread extends Thread {
@Override
public void run() {
for (int i = 0;i < 1000;i++){
System.out.println(getName() + ":" + i);
}
}
}
参考代码1:
public class ThreadDaemonDemo {
public static void main(String[] args) {
MyDaemonThread t1 = new MyDaemonThread();
MyDaemonThread t2 = new MyDaemonThread();
MyDaemonThread t3 = new MyDaemonThread();
t1.setName("刘德华");
// t1.setDaemon(true);
t2.setName("张学友");
t2.setDaemon(true);
t3.setName("郭富城");
t3.setDaemon(true);
t1.start();
t2.start();
t3.start();
}
}
运行结果:
参考代码2:
public class ThreadDaemonDemo {
public static void main(String[] args) {
MyDaemonThread t1 = new MyDaemonThread();
MyDaemonThread t2 = new MyDaemonThread();
MyDaemonThread t3 = new MyDaemonThread();
t1.setName("刘德华");
t1.setDaemon(true);
t2.setName("张学友");
t2.setDaemon(true);
t3.setName("郭富城");
t3.setDaemon(true);
t1.start();
t2.start();
t3.start();
}
}
运行结果:
可以看出当线程都是守护线程的时候程序就停止运行了。
中断线程
创建MyStopThread类:
import java.util.Date;
public class MyStopThread extends Thread{
@Override
public void run() {
System.out.println("开始执行时间:" + new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束时间:" + new Date());
System.out.println("hello");
}
}
public final void stop()
查看API文档我们知道:
public final void stop()已弃用 这种方法本质上是不安全的。 使用Thread.stop停止线程可以解锁所有已锁定的监视器(由于未
ThreadDeath
ThreadDeath异常在堆栈中ThreadDeath
的自然结果)。 如果先前受这些监视器保护的任何对象处于不一致的状态,则损坏的对象将变得对其他线程可见,可能导致任意行为。stop
许多用途应该被代替,只需修改一些变量来指示目标线程应该停止运行。 目标线程应该定期检查此变量,如果变量表示要停止运行,则以有序方式从其运行方法返回。 如果目标线程长时间等待(例如,在interrupt
变量上),则应该使用interrupt
方法来中断等待。 有关详细信息,请参阅Why are Thread.stop, Thread.suspend and Thread.resume Deprecated? 。强制线程停止执行。
如果安装了一个安全管理器,它的
checkAccess
方法this
作为参数。 这可能导致SecurityException
被提升(在当前线程中)。如果此线程与当前线程不同(即当前线程正试图停止除本身线程之外的线程),则另外还调用安全管理器的
checkPermission
方法(具有RuntimePermission("stopThread")
参数)。 再次,这可能会导致抛出SecurityException
(在当前线程中)。由该线程表示的线程被强制停止,它正在异常进行,并抛出一个新创建的
ThreadDeath
对象作为例外。允许停止尚未启动的线程。 如果线程最终启动,它将立即终止。
一个应用程序通常不应该尝试捕获
ThreadDeath
,除非它必须做一些非凡的清理操作(请注意,抛出ThreadDeath
导致finally
语句try
语句在线程正式死亡之前执行)。 如果一个catch
子句捕获一个ThreadDeath
对象,重要的是重新抛出该对象,使线程实际上死亡。该反应否则捕获的异常不打印出消息,或者如果未捕获的异常是一个实例,否则通知应用程序的顶级错误处理程序
ThreadDeath
。异常
SecurityException
- 如果当前线程不能修改此线程。
参考代码:
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopThread myStopThread = new MyStopThread();
myStopThread.start();
try {
Thread.sleep(2000);
myStopThread.stop();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
开始执行时间:Wed Dec 29 13:56:09 CST 2021
通过运行结果可以看出stop()直接将线程终止了,没有输出hello和结束时间。
public void interrupt()
查看API文档我们知道:
public void interrupt()中断这个线程。
除非当前线程中断自身,这是始终允许的,所以调用此线程的
checkAccess
方法,这可能会导致抛出SecurityException
。如果该线程阻塞的调用
wait()
,wait(long)
,或wait(long, int)
的方法Object
类,或者在join()
,join(long)
,join(long, int)
,sleep(long)
,或sleep(long, int)
,这个类的方法,那么它的中断状态将被清除,并且将收到一个InterruptedException
。如果该线程在可阻止在I / O操作
InterruptibleChannel
则信道将被关闭,该线程的中断状态将被设置,并且螺纹将收到一个ClosedByInterruptException
。如果该线程在
Selector
中被阻塞,则线程的中断状态将被设置,并且它将从选择操作立即返回,可能具有非零值,就像调用了选择器的wakeup
方法一样。如果以前的条件都不成立,则该线程的中断状态将被设置。
中断不存在的线程不需要任何效果。
异常
SecurityException
- 如果当前线程不能修改此线程
参考代码:
public class ThreadStopDemo {
public static void main(String[] args) {
MyStopThread myStopThread = new MyStopThread();
myStopThread.start();
try {
Thread.sleep(2000);
myStopThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果
开始执行时间:Wed Dec 29 13:58:48 CST 2021
结束时间:Wed Dec 29 13:58:50 CST 2021
hello
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.hjl.test.day29.MyStopThread.run(MyStopThread.java:11)
通过运行结果可以看出interrupt()是中断这个线程,对其它线程并不影响,但是将会抛出异常。
线程的生命周期图
多线程火车票售票案例
创建TicketWindow类:
public class TicketWindow extends Thread {
private int tickets = 1000;
Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){
if (tickets > 0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票");
}
}
}
}
}
参考代码:
public class TicketWindowDemo {
public static void main(String[] args) {
TicketWindow ticketWindowDemo = new TicketWindow();
//创建三个线程对象,模拟三个窗口售票
Thread t1 = new Thread(ticketWindowDemo);
Thread t2 = new Thread(ticketWindowDemo);
Thread t3 = new Thread(ticketWindowDemo);
t1.setName("窗口1");
t1.setName("窗口2");
t1.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
运行结果:
到底啦!给靓仔一个关注吧!