多线程原理与实战【04 线程的基本操作】
线程的基本操作
线程的名称设置和获取
package com.ryder.code;
public class ThreadNameDemo {
private static final int MAX_COUNT = 3;
static class RunTarget implements Runnable {
@Override
public void run() {
for (int i = 0; i < MAX_COUNT; i++) {
System.out.println("线程执行的轮次:" + i);
}
}
}
public static void main(String[] args) {
RunTarget runTarget = new RunTarget();
Thread thread = new Thread(runTarget);//系统自动设置的线程名称
thread.start();
System.out.println("系统设置的:" + thread.getName());
Thread thread1 = new Thread(runTarget, "自定义的线程——A");
thread1.start();
System.out.println("自定义的:" + thread1.getName());
}
}
系统设置的:Thread-0
自定义的:自定义的线程——A
线程执行的轮次:0
线程执行的轮次:1
线程执行的轮次:2
线程执行的轮次:0
线程执行的轮次:1
线程执行的轮次:2
线程的sleep操作
sleep的作用就是让正在执行的线程休眠,让CPU去执行其他的任务。
package com.ryder.code;
public class SleepDemo {
public static final int SLEEP_GAP = 5000;//睡眠时长5秒
public static final int MAX_COUNT = 50;
static class SleepThread extends Thread {
static int threadSeqNumber = 1;
public SleepThread() {
super("sleepThread-" + threadSeqNumber);
threadSeqNumber++;
}
@Override
public void run() {
for (int i = 1; i < MAX_COUNT; i++) {
System.out.println(getName() + ",睡眠轮次:" + i);
//线程睡眠一会儿
try {
Thread.sleep(SLEEP_GAP);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
for (int i = 1; i < 5; i++) {
Thread thread = new SleepThread();
thread.start();
}
System.out.println(Thread.currentThread().getName() +"运行结束");
}
}
通过jps指令查出JVM进程SleepDemo的进程ID
C:\Users\user>jps
9168 Launcher
11140 Jps
11460
11780 RemoteMavenServer36
6524 SleepDemo
再通过Jstack指令查询线程的状态
C:\Users\use>jstack 6524
2021-05-19 21:28:29
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.111-b14 mixed mode):
"DestroyJavaVM" #15 prio=5 os_prio=0 tid=0x0000000002b5e000 nid=0x2e8c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"sleepThread-4" #14 prio=5 os_prio=0 tid=0x000000001d76c000 nid=0x2874 waiting on condition [0x000000001e63e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.ryder.code.SleepDemo$SleepThread.run(SleepDemo.java:20)
"sleepThread-3" #13 prio=5 os_prio=0 tid=0x000000001d76b800 nid=0x3664 waiting on condition [0x000000001e53f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.ryder.code.SleepDemo$SleepThread.run(SleepDemo.java:20)
"sleepThread-2" #12 prio=5 os_prio=0 tid=0x000000001d76a800 nid=0x2af0 waiting on condition [0x000000001e43f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.ryder.code.SleepDemo$SleepThread.run(SleepDemo.java:20)
"sleepThread-1" #11 prio=5 os_prio=0 tid=0x000000001d768000 nid=0x24d0 waiting on condition [0x000000001e33e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.ryder.code.SleepDemo$SleepThread.run(SleepDemo.java:20)
"Service Thread" #10 daemon prio=9 os_prio=0 tid=0x000000001d71d000 nid=0xa88 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
可以看到所创建的4个线程sleepThread都处于TIMED_WAITING (sleeping)状态
线程的interrupt操作
interrupt的2个作用
- 线程处于阻塞状态(Object.wait()、Thread.join()、Thread.sleep())三种方法之一阻塞,调用的时候会抛出一个InterruptedException异常,提前结束阻塞状态。
- 线程处于运行中,线程不受影响,线程的中断标记为true,在合适的位置会检测是否中断,退出。
package com.ryder.code;
public class InterruptDemo {
public static final int SLEEP_GAP = 5000;//睡眠时长
public static final int MAX_COUNT = 50;//睡眠次数
static class SleepThread extends Thread{
static int threadSeqNumber = 1;
public SleepThread() {
super("sleepThread-" + threadSeqNumber);
threadSeqNumber++;
}
@Override
public void run() {
System.out.println(getName() + "进入睡眠.");
try {
Thread.sleep(SLEEP_GAP);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(getName() + "发生异常被打断");
return;
}
System.out.println(getName() + "运行结束");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new SleepThread();
thread1.start();
Thread thread2 = new SleepThread();
thread2.start();
Thread.sleep(2000);
thread1.interrupt();
Thread.sleep(5000);
thread2.interrupt();
Thread.sleep(1000);
System.out.println("运行结束");
}
}
sleepThread-1进入睡眠.
sleepThread-2进入睡眠.
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.ryder.code.InterruptDemo$SleepThread.run(InterruptDemo.java:19)
sleepThread-1发生异常被打断
sleepThread-2运行结束
运行结束
sleepThread-2在睡眠7秒后,被主线程中断,但是sleepThread-2线程被中断时候,已经执行结束了,所以thread2.interrupt()并没有产生实质效果。
线程的join操作
线程合并操作
package com.ryder.code;
public class JoinDemo {
public static final int MAX_COUNT = 5;
static class SleepThread extends Thread {
static int threadSeqNumber = 1;
public SleepThread() {
super("sleepThread-" + threadSeqNumber);
threadSeqNumber++;
}
@Override
public void run() {
for (int i = 1; i < MAX_COUNT; i++) {
System.out.println(getName() + ",睡眠轮次:" + i);//线程睡眠一会儿
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 =new SleepThread();
System.out.println("启动 thread1");
thread1.start();
thread1.join(); //合并线程1 不限时
Thread thread2 =new SleepThread();
System.out.println("启动 thread2");
thread2.start();
thread2.join(1000); //合并线程2 限时1秒
System.out.println("end...");
}
}
启动 thread1
sleepThread-1,睡眠轮次:1
sleepThread-1,睡眠轮次:2
sleepThread-1,睡眠轮次:3
sleepThread-1,睡眠轮次:4
启动 thread2
sleepThread-2,睡眠轮次:1
sleepThread-2,睡眠轮次:2
sleepThread-2,睡眠轮次:3
sleepThread-2,睡眠轮次:4
end
join线程的WAITING状态
执行以下两个操作,当前线程将处于WAITING状态
1)thread.join()
2) object.wait()
join线程的TIMED_WAITING状态
该状态不会分配CPU的时间片,要等待被唤醒,或者等待的时限到期。
线程的yield操作
线程的yield(让步)操作的作用事让目前正在执行的线程放弃当前的执行,让出CPU的执行权限,使得CPU去执行其他的线程。
package com.ryder.code;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
public class YieldDemo {
public static final int MAX_COUNT = 100;//执行次数
public static AtomicInteger index = new AtomicInteger(0);//执行编号
//记录线程的执行次数
private static Map<String, AtomicInteger> metric = new HashMap<>();
//输出线程的执行次数
private static void printMetric() {
System.out.println("metric" + metric);
}
static class YieldThread extends Thread {
static int threadSeqNumber = 1;
public YieldThread() {
super("yieldThread-" + threadSeqNumber);
threadSeqNumber++;
//将线程加入执行次数统计map
metric.put(this.getName(), new AtomicInteger(0));
}
@Override
public void run() {
for (int i = 1; i < MAX_COUNT && index.get() < MAX_COUNT; i++) {
System.out.println("线程的优先级:" + getPriority());
index.incrementAndGet();
//统计一次
metric.get(this.getName()).incrementAndGet();
if (i % 2 == 0) {
//让步:出让执行的权限
Thread.yield();
}
}
printMetric();
System.out.println(getName() + "运行结束");
}
}
public static void main(String[] args) {
Thread thread = new YieldThread();
//设置为最高的优先级
thread.setPriority(Thread.MAX_PRIORITY);
Thread thread2 = new YieldThread();
//设置为最低的优先级
thread2.setPriority(Thread.MIN_PRIORITY);
System.out.println("启动线程。。。");
thread.start();
thread2.start();
}
}
启动线程。。。
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
...
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:1
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
...
线程的优先级:10
线程的优先级:10
线程的优先级:1
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
线程的优先级:10
metric{yieldThread-2=22, yieldThread-1=78}
sleepThread-1运行结束
metric{yieldThread-2=22, yieldThread-1=78}
sleepThread-2运行结束
可以看出,线程调用yield之后,操作系统在重新进行线程调度时偏向于将执行机会让给优先级更高的线程。
特点:
- yield仅能使一个线程从运行状态转到就绪状态,而不是阻塞状态
- yield不能保证使得当前正在运行的线程迅速转换到就绪状态
- 即使完成了迅速转换,不能保证所有就绪的线程被选中
线程的daemon操作
Java中的线程分为两类: 守护线程(GC线程)与用户线程。
- 实例属性daemon,保存一条Thread线程实例的守护线程,默认false,表示线程为用户线程
- 实例方法setDaemon(…),此方法将线程标记为守护线程(true)或者用户线程(false)
- 实例方法isDaemon(),获取线程的守护状态,用于判断该线程是不是守护线程
package com.ryder.code;
public class DaemonDemo {
public static final int SLEEP_GAP = 500;//每一轮的睡眠时长
public static final int MAX_COUNT = 4;//用户线程执行轮次
//守护线程实现类
static class DaemonThread extends Thread {
public DaemonThread() {
super("daemonThread");
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "--daemon线程开始");
for (int i = 1; ; i++) {
System.out.println(Thread.currentThread().getName() + "--轮次:" + i);
System.out.println(Thread.currentThread().getName() + "--守护状态:" + isDaemon());
//线程睡眠500毫秒
try {
Thread.sleep(SLEEP_GAP);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
Thread daemonThread = new DaemonThread();
daemonThread.setDaemon(Boolean.TRUE);
daemonThread.start();
//创建一条用户线程,执行4轮
Thread userThread = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "用户线程开始...");
for (int i = 1; i <= MAX_COUNT; i++) {
System.out.println(Thread.currentThread().getName() + ">>轮次:" + i);
System.out.println(Thread.currentThread().getName() + ">>守护状态:" + Thread.currentThread().isDaemon());
try {
Thread.sleep(SLEEP_GAP);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ">>用户线程结束");
}, "userThread");
userThread.start();
System.out.println(Thread.currentThread().getName() + "守护状态为:" + Thread.currentThread().isDaemon());
System.out.println(Thread.currentThread().getName() + "运行结束");
}
}
daemonThread--daemon线程开始
daemonThread--轮次:1
daemonThread--守护状态:true
main守护状态为:false
main运行结束
userThread用户线程开始...
userThread>>轮次:1
userThread>>守护状态:false
daemonThread--轮次:2
daemonThread--守护状态:true
userThread>>轮次:2
userThread>>守护状态:false
daemonThread--轮次:3
daemonThread--守护状态:true
userThread>>轮次:3
userThread>>守护状态:false
daemonThread--轮次:4
daemonThread--守护状态:true
userThread>>轮次:4
userThread>>守护状态:false
daemonThread--轮次:5
daemonThread--守护状态:true
userThread>>用户线程结束
要点
- 守护线程必须在启动之前将其设置为true,启动之后再设置,会抛出一个InterruptedException异常
- 守护线程存在被JVM强制终止的风险,所以守护线程尽量不去访问系统资源,数据库连接等
- 守护线程创建的线程也是守护线程