目录
3. 匿名内部类(继承 Thread 类和实现 Runnable 接口)
2. 调用 interrupt() 方法通知:isinterrupted
一、线程的创建
线程的创建方法有 5 种:
- ①继承Thread 类;
- ②实现 Runnable 接口;
- ③匿名内部类,继承Thread 类;
- ④ 匿名内部类,实现 Runnable 接口;
- ⑤ lambda 表达式。
1. 继承 Thread 类
创建子类,继承自 Thread 类,并且重写 run 方法。
run 方法:描述了这个线程内部要执行哪些代码。
注:需要调用start方法,才真正在系统中创建了线程,在真正开始执行上面的 run 中的操作。
class MyThread extends Thread {
@Override
public void run() {
System.out.println("111");
}
}
public class Demo1 {
public static void main(String[] args) {
MyThread t1 = new MyThread();
t1.start();
}
}
输出结果:
2. 实现 Runnable 接口
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("222");
}
}
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(new MyRunnable());
t1.start();
}
}
输出结果:
3. 匿名内部类(继承 Thread 类和实现 Runnable 接口)
public class Demo3 {
public static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("1111");
}
};
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("2222");
}
});
t1.start();
t2.start();
}
}
输出结果:
也可能会出现如下情况,因为这里用到了多线程,t1 线程和 t2 线程是并发运行的,所以谁先输出是不确定的。

4. lambda 表达式
public class Demo4 {
public static void main(String[] args) {
Thread t1 = new Thread(()-> {
System.out.println("111");
});
t1.start();
}
}
输出结果:
5. 整体小结
public class Demo1 {
//1.继承 Thread 类,重写 run 方法
class MyThread extends Thread{
public void run(){
System.out.println("111");
}
}
//2.实现 Runnable 接口,重写 run 方法
class MyRunnacble implements Runnable{
@Override
public void run() {
System.out.println("222");
}
}
//3.匿名内部类,继承 Thread 类
public static void Demo3() {
Thread thread = new Thread(){
public void run() {
System.out.println("333");
}
};
}
//4.匿名内部类,实现 Runnable 接口
public static void Demo4() {
Thread thread = new Thread(new Runnable(){
public void run() {
System.out.println("444");
}
});
}
//5.lambda 表达式
public static void Demo5() {
Thread thread = new Thread(() -> {
System.out.println("555");
});
}
}
strat 和 run 的区别:
run 方法只是一个普通的方法,描述了任务的内容。在main线程里调用 run,并没有创建新的线程。
start 方法是一个特殊的方法,其内部会在系统中创建线程。
二、Thread 类
1. Thread 的常见构造方法
| 方法 | 说明 |
| Thread() | 创建线程对象 |
| Thread( Runnable target ) | 使用 Runnable 对象创建线程对象 |
| Thread( String name ) | 创建线程对象,并命名 |
| Thread( Runnable target, String name) | 使用 Runnable 对象创建线程对象,并命名 |
2. Thread 的常见属性
| 属性 | 获取方法 |
| ID | getID ( ) |
| 名称 | getName ( ) |
| 状态 | getState ( ) |
| 优先级 | getPriority ( ) |
| 是否有后台线程 | isDaemon ( ) |
| 是否存活 | isAlive ( ) |
| 是否被中断 | isInterrupted ( ) |
三、线程中断
核心思路是让线程的 run 方法执行完。特殊情况:main 线程来说,main 方法执行完,线程就完了。
有两种方法:
1. 通过共享的标记来进行沟通中断(手动设置标志位)
自己手动设置一个标志位,注意需要给标志位加上 volatile 关键字(确保线程安全)。
public class Demo3 {
//自己设置一个标志位:isQuit 是否退出,false 不退出, true 退出
//因为 main 线程修改的 isQuit 和 t 线程判定的 isQuit 是同一个值。
private static volatile boolean isQuit = false; //加上 volatile 确保线程安全
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!isQuit) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//当标志位 isQuit 为 true 时,就退出循环,进一步 run 结束,再进一步线程t 结束
//先让 t 线程执行一会
Thread.sleep(5000);
isQuit = true;
System.out.println("终止 t 线程");
}
}
2. 调用 interrupt() 方法通知:isinterrupted
使用 Thread 中内置的标志位来进行判定。可以通过以下两种方法来进行获取标志位,进行判定。
- Thread.interrupted() :静态的方法
- Thread.currentThread().isinterrupted() :实例方法,currentThread 能够获取当先线程的实例。
注意: interrupted() 和 isnterrupted() 的区别:
① interrupted() 这个方法判定的标志位是 Thread 的 static 成员。(一个程序中只有一个标志位)
② isinterrupted() 这个方法判定的标志位是 Thread 的普通成员。每个线程实例都有自己的标志位。 经常使用。
如要让线程中断:
- t.interrupt() :将线程 t 中断。
注意:使用该方法后,可能产生两种情况:
① 如果 t 线程处于 就绪状态,就是设置线程的标志位为 true;
② 如果 t 线程处于 阻塞状态(sleep休眠了),就会触发一个InterruptException异常,从而导致 线程 从阻塞状态 被唤醒。
因此,在 sleep 中,不能单纯在 catch 中打印日志。还要加上 break。
public class Demo4 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
//判断线程中内置的标志位(是否中断位)为 true 还是 false
//为 true : 说明 是中断,循环应该结束
//为 false:说明 没有中断,循环应该继续
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
//当触发异常后,立即退出循环。
break;
}
}
});
t.start();
//在主线程中,调用interrupt方法,来中断这个线程
Thread.sleep(5000); //休眠 5 s
//t.interrupt 的意思是 让 t 线程被中断
t.interrupt();
}
}
四、线程等待:join
多个线程之间的调度顺序是不确定的,有时需要控制线程的执行顺序,要控制线程之间的顺序,其中一种方法就是线程等待。
线程等待,主要是控制线程结束的先后顺序。
- join 方法:哪个线程调用的 join ,哪个线程就会阻塞等待,等到对应线程执行完毕为止(对应线程的 run 执行完)。
在如下代码中,调用 join 方法的线程是 main 线程,是针对 t 线程对象调用的,此时就是让 main 等待 t 。 调用 join 后,main 线程就会进入阻塞状态(暂时无法在cpu上执行),即不往下继续执行了。main 线程必须等到 t 线程执行完毕才能恢复成 就绪状态。即 控制 t 线程先结束,main 线程后结束。
注意:
join 操作默认情况下,是死等,不合理。因此还可以执行等待时间,最长等待多久,等不到就不等了。
- t.join(1000):main 线程等待 t 线程 1000 ms(1s)。
public class Demo5 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//在主线程中进行等待,使用 join 方法,等待 t 线程执行完。
//t.join();
//如果 10s 之内,t 线程结束了,此时 join 直接返回
//如果 10s 之后,t 线程还没结束,此时 join 也直接返回,不等了
t.join(10000);
for (int i = 0; i < 5; i ++) {
System.out.println("hello main");
Thread.sleep(1000);
}
}
}
五、获取线程的实例:currentThread
- Thrad.currentThread() :获取当先线程的引用(实例)
哪个线程调用的 currentThread ,就获取到的是哪个线程的实例。
public class Demo6 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread() {
public void run() {
//调用 currentThread() 的是线程 t,因此返回的 name 是 t 线程的 name:Thread-0
//特殊情况:这里也可以写成 this.getName()。
System.out.println(Thread.currentThread().getName());
}
};
t.start();
t.join();
//调用 currentThrad() 的是 main 线程,因此返回的是 main 线程的 name:main
System.out.println(Thread.currentThread().getName());
}
}
六、线程休眠:sleep
进程是通过 PCB 进行描述(狭义描述),通过双向链表组织起来的。当进程中有多个线程时,此时每个线程都有一个PCB(在Linux 中,进程和线程也没有明显区分,线程被称为轻量级进程),一个进程就对应了一组 PCB。PCB 上有一个字段 tgroupId,同一个进程中的 tgroupId 是相同的。
每个线程的 PCB 中状态 有 就绪状态 和 阻塞状态。因此在进程中,有两个队列(均由双向链表将每个线程的 PCB 连接起来):就绪队列 和 阻塞队列。
当某线程调用 sleep 时,这个线程对应的 PCB 就会进入 阻塞队列。而操作系统调度线程时,只从就绪队列中挑选 PCB 到 CPU 上运行,而阻塞队列中的 PCB 就只能干等。
当睡眠时间到了,系统才会把该 PCB 从 阻塞队列 中拿到 就绪队列 中去。
文章详细介绍了Java中创建线程的五种方式,包括继承Thread类、实现Runnable接口、匿名内部类以及使用lambda表达式。同时,讨论了线程中断的两种策略,通过共享标志和调用interrupt()方法,以及join方法用于线程同步和Thread.currentThread()获取当前线程实例。最后提到了线程休眠的sleep方法。
4万+

被折叠的 条评论
为什么被折叠?



