多线程(二):Thread 类及常见方法
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
Thread 的常见构造方法
示例如下
public class ThreadDemo11 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("线程名:" +
Thread.currentThread().getName());
}
};
Thread t1 = new Thread(runnable, "thread1");
Thread t2 = new Thread(runnable, "thread2");
Thread t3 = new Thread(runnable, "thread3");
Thread t4 = new Thread(runnable, "thread4");
Thread t5 = new Thread(runnable, "thread5");
Thread t6 = new Thread(runnable, "thread6");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t6.start();
}
}
该代码执行结果如下
ThreadGroup:线程分组
可以将一类线程归为一组,并且进行信息的打印,查看一组线程的具体行为。
以张三和李四百米赛跑为例
/*
* 演示线程分组
*/
public class ThreadDemo12 {
public static void main(String[] args) {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("开始起跑了~" +
Thread.currentThread().getName());
try {
int num = 1 + new Random().nextInt(5);
Thread.sleep(num * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我到终点了~" +
Thread.currentThread().getName());
}
};
//定义分组
ThreadGroup group = new ThreadGroup("百米赛跑一组");
//创建运动员
Thread t1 = new Thread(group, runnable, "张三");
Thread t2 = new Thread(group, runnable, "李四");
t1.start();
t2.start();
// //打印线程分组的详情
// group.list();
//等待所有选手到达终点
while (group.activeCount() != 0) {
}
//宣布成绩
System.out.println("宣布成绩");
}
}
该代码执行结果如下
Thread 的几个常见属性
- ID 是线程的唯一标识,不同线程不会重复
- 名称是各种调试工具用到
- 状态表示线程当前所处的一个情况,下面我们会进一步说明
- 优先级高的线程理论上来说更容易被调度到
- 关于后台线程,需要记住一点:JVM会在一个进程的所有非后台线程结束后,才会结束运行。
- 是否存活,即简单的理解,为 run 方法是否运行结束了
- 线程的中断问题,下面我们进一步说明
public class ThreadDemo13 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
System.out.println("线程启动前状态:" + t1.getState());
t1.start();
System.out.println("线程启动后状态:" + t1.getState());
System.out.println("线程ID:" + t1.getId());
System.out.println("线程的名称:" + t1.getName());
System.out.println("线程优先级:" + t1.getPriority());
System.out.println("线程是否为后台线程:" + t1.isDaemon());
System.out.println("线程是否存活:" + t1.isAlive());
System.out.println("线程是否被中断:" + t1.isInterrupted());
}
}
该代码执行结果如下
线程优先级
从上述代码可以观察到,新创建的线程优先级默认为5。线程优先级最小为1,最大为10。这个值越大那么它的(执行)权重就越高。
/*
* 优先级演示
*/
public class ThreadDemo14 {
public static void main(String[] args) {
// 定义方法
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("线程名:" +
Thread.currentThread().getName());
}
};
for (int i = 0; i < 10; i++) {
Thread t1 = new Thread(runnable, "t1");
Thread t2 = new Thread(runnable, "t2");
Thread t3 = new Thread(runnable, "t3");
t1.setPriority(1); // 优先级最小
// t1.setPriority(Thread.MIN_PRIORITY); // 优先级最小
t3.setPriority(Thread.MAX_PRIORITY); // 最高的优先级
t1.start();
t2.start();
t3.start();
}
}
}
代码执行结果
可见:优先级越高,执行的优先级也越高,执行权也就越大,但是,CPU的调度是非常复杂的,所以不会严格按照优先级的排序去执行。总体来看,还是优先级越高,执行的权重也就越高。
线程分类
后台线程(守护线程)
后台线程(守护线程)用来服务用户线程。
使用场景:Java垃圾回收器
注意事项:
- 守护线程设置必须在调用start();前,如果设置守护线程再线程开始后,那么程序就会报错,并且设置的守护线程的值无法生效。
- 在守护线程里面创建的线程,默认情况下全部都是守护线程。
用户线程【默认为用户线程】
进程退出:没有用户线程运行时,进程就会结束。
public class ThreadDemo15 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
// 在守护线程的内部创建的线程
Thread t2 = new Thread(() -> {
});
System.out.println("t2 是:" +
(t2.isDaemon() == true ? "守护线程" : "用户线程"));
});
// 设置为守护线程
// t1.setDaemon(true);
t1.start();
System.out.println("t1 是否为守护线程:" + t1.isDaemon());
}
}
该代码执行结果如下
start 和 run 的区别
先看两个例子
调用start方法时,代码如下:
public class ThreadDemo16 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("线程名:" + Thread.currentThread().getName());
System.out.println("当前时间 :" + new Date());
}, "t1");
t1.start();
t1.start();
// t1.run();
// t1.run();
}
}
该代码执行结果如下
调用run方法时,代码如下:
public class ThreadDemo16 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println("线程名:" + Thread.currentThread().getName());
System.out.println("当前时间 :" + new Date());
}, "t1");
// t1.start();
// t1.start();
t1.run();
t1.run();
}
}
该代码执行结果如下:
start 和 run 的区别:
- run方法属于普通方法;而start方法属于启动线程的方法。
- run方法可以多次执行;而start方法只能执行一次,执行多次就会报错。
中断线程
线程中断方式有两种:
方式①:使用全局自定义变量来终止线程 。
这个终止的 方法比较温柔,在执行终止指令之后,需要执行完当前的任务才会真正停止。
示例如下:
/*
* 使用全局自定义变量来终止线程
*/
public class ThreadDemo17 {
//声明全局自定义变量
private static boolean flag = false;
public static void main(String[] args) throws InterruptedException {
//转账线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!flag) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("转帐中~~");
}
System.out.println("转账终止~");
}
}, "转账");
t1.start();
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(310);
} catch (InterruptedException e) {
e.printStackTrace();
}
//改变变量的状态来终止线程执行
System.out.println("停止交易~");
flag = true;
}
});
t2.start();
t1.join();
t2.join();
}
}
该代码执行结果如下:
方式②: 使用线程提供的终止方法 interrupt 来终止线程
interrupt()方法的作用就是将线程中的终止状态从默认的 false 改为 true 。
示例如下:
/*
*使用 interrupt 来终止线程
*/
public class ThreadDemo18 {
public static void main(String[] args) throws InterruptedException {
//创建转账线程
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.interrupted()) {
//判断线程中断状态
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// e.printStackTrace();
break;
}
System.out.println("转帐中");
}
System.out.println("转账终止");
}
});
t1.start();
Thread.sleep(310);
//终止线程
System.out.println("停止交易");
t1.interrupt();
//中断线程
}
}
代码执行结果如下:
此方式中真正终止线程的语句为try{ }中的break;语句,示例如下:
public class ThreadDemo19 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// while (!Thread.interrupted()) {
System.out.println("转账中");
}
System.out.println("转账终止");
}
});
t1.start();
TimeUnit.NANOSECONDS.sleep(1);
System.out.println("停止交易");
t1.interrupt();
}
}
该代码执行结果如下:
其中,判断线程中断状态语句:while (!Thread.interrupted())
的效果等价于while (!Thread.currentThread().isInterrupted())
,二者的区别在于,使用while (!Thread.interrupted())
时当终止完了一个线程之后,Thread.interrupted()
的状态会复位,而使用while (!Thread.currentThread().isInterrupted())
当终止完了一个线程之后,Thread.interrupted()
的状态不会复位。
示例一:
public class ThreadDemo20 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.interrupted());
}
}
});
t1.start();
t1.interrupt();
}
}
使用Thread.interrupted()
的运行结果为:
示例二:
public class ThreadDemo20 {
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
// System.out.println(Thread.interrupted());
System.out.println(Thread.currentThread().isInterrupted());
}
}
});
t1.start();
t1.interrupt();
}
}
使用Thread.currentThread().isInterrupted()
的运行结果为:
可见,判断线程中断状态语句:while (!Thread.interrupted())
和while (!Thread.currentThread().isInterrupted())
,二者的区别两个本质上还是有区别的。
附:
方式①与方式②区别:
使用全局自定义标识终止线程执行的时候比较温柔,当它收到了终止指令后,会把当前的任务执行完毕后再终止;使用 interrupt 它会在收到终止指令之后立即终止任务执行。
方式③:使用线程提供的方法 stop 来终止线程 (弃用)原因是使用stop方法时会带来安全隐患,终止线程时不会释放其资源。
等待一个线程 :join( ) 方法
有时,我们需要等待一个线程完成它的工作后,才能进行下一步工作。
我们以张三和李四交替上下班为例,代码如下:
public class ThreadDemo21 {
public static void main(String[] args) throws InterruptedException {
//定义公共任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "上班");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "下班");
}
};
//定义张三
Thread t1 = new Thread(runnable, "张三");
t1.start();
//等待张三下班,李四再上班
t1.join();
//定义李四
Thread t2 = new Thread(runnable, "李四");
t2.start();
}
}
该代码执行结果如下:
如果注释掉 t1.jion()
public class ThreadDemo21 {
public static void main(String[] args) throws InterruptedException {
//定义公共任务
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "上班");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "下班");
}
};
//定义张三
Thread t1 = new Thread(runnable, "张三");
t1.start();
//等待张三下班,李四再上班
//t1.join();
//定义李四
Thread t2 = new Thread(runnable, "李四");
t2.start();
}
}
代码的执行结果就变为:
说明 t1.jion()
可以让程序等待 t1
线程执行完,再执行下一步操作。
附:jion()
方法可以设置参数
获取当前线程引用 :currentThread( ) 方法
这个方法我们一直在用,已经非常熟悉了。
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}