二、Thread常见的方法
Thread 类是 JVM ⽤来管理线程的⼀个类,换句话说,每个线程都有⼀个唯⼀的 Thread 对象与之关 联。
2.1 Thread 的常⻅构造⽅法
方法 | 说明 |
---|---|
Thread() | 创建线程 |
Thread(Runnable target) | 使用 Runnable 实现多线程 |
Thread(String name) | 创建线程 并命名 |
Thread(Runnable target, String name) | 使用 Runnable 和自定义名称实现多线程 |
【了解】Thread(ThreadGroup group, Runnable target) | 线程可以属于一个线程组,这个组由 ThreadGroup 类来表示,该构造器允许我们将线程添加到指定的线程组中。 |
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
2.2 Thread 的⼏个常⻅属性
属性 | 获取方法 |
---|---|
ID | getId() |
名称 | getName() |
状态 | getState() |
优先级 | getPriority() |
是否后台线程 | isDaemon() |
是否存活 | isAlive() |
是否被中断 | isInterrupted() |
• ID 是线程的唯⼀标识,不同线程不会重复
• 名称是各种调试⼯具⽤到
• 状态表⽰线程当前所处的⼀个情况,下⾯我们会进⼀步说明
• 优先级⾼的线程理论上来说更容易被调度到
• 关于后台线程,需要记住⼀点:JVM会在⼀个进程的所有⾮后台线程结束后,才会结束运⾏。
• 是否存活,即简单的理解,为 run ⽅法是否运⾏结束了
package Demo03;
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName() + ": 我还没死");
Thread.sleep(1 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我即将死去");
});
System.out.println(Thread.currentThread().getName()
+ ": ID: " + thread.getId());
System.out.println(Thread.currentThread().getName()
+ ": 名称: " + thread.getName());
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
System.out.println(Thread.currentThread().getName()
+ ": 优先级: " + thread.getPriority());
System.out.println(Thread.currentThread().getName()
+ ": 后台线程: " + thread.isDaemon());
System.out.println(Thread.currentThread().getName()
+ ": 活着: " + thread.isAlive());
System.out.println(Thread.currentThread().getName()
+ ": 被中断: " + thread.isInterrupted());
thread.start();
while (thread.isAlive()) {}
System.out.println(Thread.currentThread().getName()
+ ": 状态: " + thread.getState());
}
}
2.3 启动⼀个线程 - start()
但线程对象被创建出来并不意味着线 程就开始运⾏了。
• 覆写 run ⽅法是提供给线程要做的事情的指令清单
• 线程对象可以认为是把 李四、王五叫过来了
• ⽽调⽤ start() ⽅法,就是喊⼀声:”⾏动起来!“,线程才真正独⽴去执⾏
注意:
调⽤ start ⽅法, 才真的在操作系统的底层创建出⼀个线程.
package Demo03;
public class Test {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("Thread 1");
});
t.start();
}
}
2.4 中断⼀个线程
停⽌线程的⽅式:
1. 通过共享的标记来进⾏沟通
2. 调⽤ interrupt() ⽅法来通知
⽰例-1: 使⽤⾃定义的变量来作为标志位.
package Demo03;
public class ThreadDemo {
private static class MyRunnable implements Runnable {
public volatile boolean isQuit = false;
@Override
public void run() {
while (!isQuit) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()
+ ": 啊!险些误了⼤事");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
System.out.println(Thread.currentThread().getName() + ": 让李四开始转账。");
thread.start();
//主线程睡10秒
Thread.sleep(10 * 1000);
System.out.println(Thread.currentThread().getName() + ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
target.isQuit = true;
}
}
⽰例-2: 使⽤ Thread.interrupted() 或者 Thread.currentThread().isInterrupted() 代替⾃定义标志位.
方法 | 说明 |
---|---|
public void interrupt() | 中断与对象关联的线程,如果线程正在阻塞(如等待锁或处于sleep()状态),则会抛出InterruptedException,并将线程的状态设置为“已中断”。 |
public static boolean interrupted() | 判断当前线程的中断标志位是否已经设置。如果设置了,则清除该标志位并返回true;否则返回false。注意,这个方法会清除中断状态,所以连续两次调用不会立即返回true。 |
public boolean isInterrupted() | 检查与对象关联的线程的中断标志位是否已经设置。它不会清除中断状态,所以即使在连续多次调用时也可以检测到中断状态。 |
使⽤ thread 对象的 interrupted() ⽅法通知线程结束.
package test;
public class ThreadDemo {
private static class MyRunnable implements Runnable {
@Override
public void run() {
// 两种⽅法均可以
while (!Thread.interrupted()) {
//while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName()
+ ": 别管我,我忙着转账呢!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println(Thread.currentThread().getName()
+ ": 有内⻤,终⽌交易!");
// 注意此处的 break
break;
}
}
System.out.println(Thread.currentThread().getName()
+ ": 啊!险些误了⼤事");
}
}
public static void main(String[] args) throws InterruptedException {
MyRunnable target = new MyRunnable();
Thread thread = new Thread(target, "李四");
System.out.println(Thread.currentThread().getName()
+ ": 让李四开始转账。");
thread.start();
Thread.sleep(10 * 1000);
System.out.println(Thread.currentThread().getName()
+ ": ⽼板来电话了,得赶紧通知李四对⽅是个骗⼦!");
thread.interrupt();
}
}
2.5 等待⼀个线程 - join()
有时,我们需要等待⼀个线程完成它的⼯作后,才能进⾏⾃⼰的下⼀步⼯作。例如,张三只有等李四 转账成功,才决定是否存钱,这时我们需要⼀个⽅法明确等待线程的结束。
package test;
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Runnable target = () -> {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName()
+ ": 我还在⼯作!");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + ": 我结束了!");
};
Thread thread1 = new Thread(target, "李四");
Thread thread2 = new Thread(target, "王五");
System.out.println("先让李四开始⼯作");
thread1.start();
thread1.join();
System.out.println("李四⼯作结束了,让王五开始⼯作");
thread2.start();
thread2.join();
System.out.println("王五⼯作结束了");
}
}
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 筦待线程结束,最多等 millis 毫秒 |
public void join(long millis, int nanos) | 同理,但可以更高精度 |
2.6 获取当前线程引⽤
方法 | 说明 |
---|---|
public static Thread currentThread(); | 返回当前线程对象的引用 |
public class ThreadDemo {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
2.7 休眠当前线程
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 使当前正在执行的线程休眠(暂停执行)指定的毫秒数 |
public static void sleep(long millis, int nanos) throws InterruptedException | 使当前正在执行的线程休眠(暂停执行)指定的毫秒数加上纳秒数 |
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}