新建线程
新建一个线程,只要使用 new 关键字创建一个线程对象,并调用这个线程对象的 start() 方法启动这个线程,启动线程后,线程会去调用 run() 方法,run() 方法就是线程具体执行的内容。
创建线程方式一
继承 Thread 类,重写 run() 方法
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) {
// 创建一个线程对象
Thread thread = new MyThread();
// 启动线程
thread.start();
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("当前线程名为: " + Thread.currentThread().getName());
}
}
说明:
- Thread.currentThread() 返回当前线程
- 线程对象直接调用 run() 方法,不会开启新线程,只会在当前线程中串行执行
创建线程方式二
实现 Runnable 接口,实现 run() 方法
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) {
// 创建一个线程对象
Thread thread = new Thread(new MyRunnable());
// 启动线程
thread.start();
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("实现Runnable接口,当前线程名为: " + Thread.currentThread().getName());
}
}
注意:
- 在创建 Thread 对象时,可以传入一个字符串参数,该字符串可以用来定义该线程的名字
public Thread(Runnable target, String name)
终止线程
一般来说,线程执行完毕就会结束,无需手工关闭。但我们也可以调用 stop() 方法来手动关闭线程。
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) {
// 创建一个线程对象
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
try {
// 线程睡眠1秒钟
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + threadName + " 结束执行");
});
// 启动线程
thread.start();
try {
// 线程睡眠0.5秒钟
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 终止线程
thread.stop();
System.out.println("线程:" + Thread.currentThread().getName() + " 结束执行");
}
}
注意:
- stop() 已经被废弃,不推荐使用,原因是 stop() 方法过于暴力,强行把执行到一半的线程终止,可能会引起一些数据不一致的问题。若想终止线程,可以使用线程中断。
- 线程的 run() 方法不允许抛出异常,若调用的方法抛出异常,需要捕获。
- sleep() 方法是 Thread 类的静态方法,使当前线程睡眠 n 毫秒。
线程中断
线程中断并不会使线程立即退出,而是给线程发送一个通知,告诉目标线程,希望它退出执行,至于目标线程接到通知后如何处理,则完全由目标线程自行决定。
线程中断涉及 Thread 类的三个方法:
public void interrupt() // 中断线程
public static boolean interrupted() // 判断是否被中断,并清除当前中断状态
public boolean isInterrupted() // 判断是否被中断
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) {
// 创建一个线程对象
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
while (true){
// 判断线程是否被中断
if(Thread.currentThread().isInterrupted()){
System.out.println("线程:" + threadName + " 被中断");
break;
}
}
System.out.println("线程:" + threadName + " 结束执行");
});
// 启动线程
thread.start();
try {
// 线程睡眠2秒钟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 中断线程
thread.interrupt();
}
}
说明:
- 线程 sleep() 方法进入睡眠或者 wait() 方法进入等待时,中断该线程,线程会抛出 InterruptedException 异常,并且清除中断状态,所以在捕获这个异常后,可以结束该线程执行,若不想在此处中断,则需要再次设置中断标志。
等待和通知
等待 wait() 方法和通知 notify() 方法,主要是为了多线程之间的协作,这两个方法并不是在 Thread 类中,而是定义在 Object 类中。 ```java package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
// 创建一个线程对象
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
synchronized (o){
try {
System.out.println("线程:" + threadName + " 将进入等待状态");
// 线程等待
o.wait();
System.out.println("线程:" + threadName + " 被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程:" + threadName + " 结束执行");
});
// 启动线程
thread.start();
Thread.sleep(2000);
synchronized (o){
// 唤醒线程
o.notify();
Thread.sleep(2000);
System.out.println("线程:" + Thread.currentThread().getName() + " 退出同步块");
}
}
}
注意:
- 调用等待 wait() 方法或通知 notify() 方法,都需要先获取锁,并在该锁对象中调用。
- 调用等待 wait() 方法,该线程会进入等待状态,并释放锁。
- notify() 方法只会**随机**唤醒**一个**等待在**该锁对象**上线程,若想唤醒**该锁对象**上所以等待的线程,可以使用 notifyAll() 方法。
- 线程被唤醒后,需要重新获取锁才能继续执行。
- wait() 方法有两个重载的方法 wait(long timeout) 和 wait(long timeout, int nanos),这两个重载方法是线程等待指定时间后,若还没被唤醒,则自动唤醒。
- notify()方法必须在wait()方法之后被调用,不然线程不会被唤醒。
<a name="GIiBa"></a>
#### wait() 方法和 sleep() 的区别
调用wait()会使线程释放锁,等线程被唤醒之后,会去竞争锁。而调用sleep()则不会释放锁。
<a name="iKKbV"></a>
## 挂起和继续执行线程
线程挂起 suspend() 和继续执行 resume() 都是定义在 Thread 类中的实例方法,这两个操作是一对相反的操作,当线程挂起后,必须要等到 resume() 方法才能继续执行。
```java
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
Object o = new Object();
// 创建一个线程对象
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
synchronized (o) {
System.out.println("线程:" + threadName + " 将挂起");
// 线程挂起
Thread.currentThread().suspend();
System.out.println("线程:" + threadName + " 继续执行");
}
System.out.println("线程:" + threadName + " 结束执行");
});
// 启动线程
thread.start();
Thread.sleep(1000);
new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
synchronized (o) {
System.out.println("线程:" + threadName + " 获取到锁");
}
System.out.println("线程:" + threadName + " 结束执行");
}).start();
Thread.sleep(2000);
System.out.println("线程:" + thread.getName() + " 的状态: " + thread.getState().name());
// 线程继续执行
thread.resume();
}
}
输出结果
线程:Thread-0 开始执行。。。
线程:Thread-0 将挂起
线程:Thread-1 开始执行。。。
线程:Thread-0 的状态: RUNNABLE
线程:Thread-0 继续执行
线程:Thread-0 结束执行
线程:Thread-1 获取到锁
线程:Thread-1 结束执行
注意:
- 线程挂起 suspend() 和继续执行 resume() 都已经被标注为废弃方法。原因是 suspend() 方法导致线程被挂起后,不会释放锁,其他线程想访问被它占用的锁时,无法正常执行,并且如果 resume() 方法在 suspend() 方法之前执行或者没有执行,则被挂起的线程很难再继续执行,被它占用的锁也不会被释放,并且线程被挂起之后,线程的状态还为 RUNNABLE,严重影响我们对系统的判断。
等待线程结束和谦让
等待线程结束 jion() 方法是当前线程在指定线程结束后,再执行。
package com.demo.base;
public class ThreadDemo {
public static void main(String[] args) throws InterruptedException {
// 创建一个线程对象
Thread thread = new Thread(() -> {
String threadName = Thread.currentThread().getName();
System.out.println("线程:" + threadName + " 开始执行。。。");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程:" + threadName + " 结束执行");
});
// 启动线程
thread.start();
// 等待线程结束
thread.join();
System.out.println("线程:" + Thread.currentThread().getName() + " 结束执行");
}
}
注意:
- join() 方法在哪个线程中调用,则该线程为等待线程。在哪个线程对象上调用,则该线程为被等待线程。等待线程要等到被等待线程执行完毕才会继续执行。
- join() 方法有两个重载的方法 join(long millis) 和 join(long millis, int nanos),都是等待指定时间后,就不再等待。
谦让 yield() 方法,是一个静态方法,它会使当前线程让出CPU,当前线程让出CPU后,还会进行CPU资源的争夺。