一、线程核心执行流程方法 -- run()
run方法是Runnable接口中定义的。所有实现Runnable的接口的子类都需要覆写run方法。
run方法是线程的核心执行流程方法,也就是说,run方法决定了线程启动后要干什么,当run方法执行完毕(JVM启动run方法),线程会进入销毁状态。
二、启动线程-- start()
start方法是Thread类定义的。start方法是启动线程调用的方法,只有当线程对象调用start方法之后才会被系统调度,进入运行状态。
当线程启动之后,若再次调用start()方法,就会抛出IllegalThreadStateException(非法线程状态异常),说明该线程已经启动过了。
三、中断线程
(一)通过共享变量中断线程
public class ThreadInterruptByVariable {
private static class MyThread implements Runnable{
volatile boolean isQuit = false;
@Override
public void run() {
while (!isQuit){
System.out.println(Thread.currentThread().getName() + " 别烦我,正在转账呢~~");
try {
// 每次转账休眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
Thread thread = new Thread(mt,"小A线程");
System.out.println("小A可以开始转账了~");
thread.start();
// 主线程休眠3秒
Thread.sleep(3000);
System.out.println("钱转完了,让小A停止!");
mt.isQuit = true;
}
}
(二)使用Thread.interrupted()(静态方法)或Thread对象的(成员方法)isInterrupted()
public class ThreadInterruptByMethod {
private static class MyThread implements Runnable{
@Override
public void run() {
// 1. 静态方法,判断当前线程是否被中断了
// while (Thread.interrupted()){
//2.成员方法,判断当前线程是否被中断了
while (Thread.currentThread().isInterrupted()){
System.out.println(Thread.currentThread().getName() + " 别烦我,正在转账呢~~");
try {
// 每次转账休眠1s
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println("转账中断");
// 当线程被中断时,会抛出中断异常
// 抛出中断异常之后,中断状态就会还原!--》因为线程调用了sleep方法
break;
}
}
System.out.println(Thread.currentThread().getName()+"被中断了");
}
}
public static void main(String[] args) throws InterruptedException {
MyThread mt = new MyThread();
Thread thread = new Thread(mt,"小A线程");
System.out.println("小A可以开始转账了~");
thread.start();
// 主线程休眠3秒
Thread.sleep(3000);
System.out.println("钱转完了,让小A停止!");
// 中断子线程
// 调用此方法就会将子线程的状态置为中断状态
thread.interrupted();
}
}
thread 收到通知的方式有两种:
1. 如果线程因为调用 wait/join/sleep 等方法而阻塞挂起,则以 InterruptedException 异常的形式通 知,清除中断标志(无论是使用Thread.interrupted() 还是使用Thread.currentThread().isInterrupted(),状态都会从TRUE 重新置为 FALSE)
当出现 InterruptedException 的时候, 要不要结束线程取决于 catch 中代码的写法. 可以选择 忽略这个异常, 也可以跳出循环结束线程.
2.线程没有调用以上方法,处在正常运行状态时收到中断通知thread.interrupt()
Thread.interrupted()判断当前线程是否被中断,若中断状态为true 清除中断标志 TRUE -->FALSE
Thread.currentThread.isInterrupted(); 判断指定线程对象是否状态为中断状态,若状态为true,不会清除中断标志。TRUE-->TRUE
四、等待线程--join()
join方法---Thread类中定义的成员方法。
线程对象. join():在哪个线程(A)中调用别的线程对象的join方法(B线程对象.join()),意思就是A线程要等待B线程执行完毕后再继续执行本线程的后续代码。
Eg:若在主线程中调用thread1.join(),主线程就会进入阻塞状态,直到thread1执行结束,主线程才会继续向后执行
public class ThreadJoin {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "还在学习JavaSE阶段...");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"JavaSE线程");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + "进入数据结构的学习部分");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"数据结构线程");
System.out.println("先学习JavaSE");
t1.start();
// 主线程死等t1,直到t1执行结束主线程再恢复执行
t1.join();
// 此时走到这里,t1线程已经执行结束,再启动t2线程
t2.start();
// 在哪调用别的线程的join方法,阻塞的是调用join的线程
// main -> 调用t2.join() 阻塞主线程,直到t2完全执行结束再恢复主线程的执行
// 主线程只等t2 2000ms - 2s,若t2在2s之内还没结束,主线程就会恢复执行
t2.join(2000);
// t2线程也执行结束了,继续执行主线程
System.out.println("开始学习JavaEE");
System.out.println(Thread.currentThread().getName());
}
}
五、休眠线程--sleep()
sleep():Thread类的静态方法,在哪个线程中调用,就休眠哪个线程
调用sleep方法的线程会让出CPU资源,从运行状态转为就绪状态,等待CPU继续调度。
六、 yeild()
线程调用yeild() ==》会从运行状态转为就绪状态!
调用yeild 方法的线程也会主动让出CPU资源,从运行状态转为就绪状态,等待CPU继续调度。
到底什么时候让出CPU ,什么时候又重新被CPU再次调度,都是操作系统OS调度的,我们无权选择。
可能会出现以下情况:
- 让出后立马又被调度了
- 让出之后,很久都不调度
七、wait() 和 notify()
线程间的等待与唤醒机制。wait()和 notify()是Object类的方法,用于线程的等待与唤醒,必须搭配synchronized锁来使用。
(一)等待方法
死等,线程进入waiting状态,直到有其他线程调用notify()将其唤醒
等待一段时间,进入timed_waiting状态。若在该时间段内线程被唤醒,则继续执行,若超过相应时间还没有其它线程唤醒此线程,此线程也不再等待,恢复执行。
(二)唤醒方法
随机唤醒一个正处于等待状态的线程
唤醒所有处于等待状态的线程
(三)代码示例
调用wait方法的前提是首先要获取到该对象的锁(synchronized对象锁),调用wait方法会释放锁,该线程进入等待队列等待被唤醒,被唤醒后不是立即恢复执行,而是进入阻塞状态,竞争锁。
public class WaitAndNotify {
private static class WaitTask implements Runnable {
private Object lock;
public WaitTask(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + "准备进入等待状态");
// 此线程在等待lock对象的notify方法唤醒
try {
//调用 wait()的线程会进入waiting状态,等待被其他线程唤醒(lock.notify())
lock.wait();
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//被唤醒结束后,线程从wait()之后开始继续执行
System.out.println(Thread.currentThread().getName() + "等待结束,本线程继续执行");
}
}
}
private static class NotifyTask implements Runnable {
private Object lock;
public NotifyTask(Object lock) {
this.lock = lock;
}
@Override
public void run() {
synchronized (lock) {
System.out.println("准备唤醒");
// 随机唤醒一个处在lock对象上等待的线程
// lock.notify();
lock.notifyAll();
System.out.println("唤醒结束");
}
}
}
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
// 创建三个等待线程
Thread t1 = new Thread(new WaitTask(lock),"t1");
Thread t2 = new Thread(new WaitTask(lock),"t2");
Thread t3 = new Thread(new WaitTask(lock),"t3");
// 创建一个唤醒线程
Thread notify = new Thread(new NotifyTask(lock),"notify线程");
t1.start();
t2.start();
t3.start();
Thread.sleep(100);
notify.start();
}
}
()非法监视器状态异常
如果调用wait()和notify()没有获取锁[没有搭配synchronized锁来使用],就会排除非法的监视器状态异常
八、线程的六种状态
public class ThreadState {
public static void main(String[] args) {
for (Thread.State state:Thread.State.values()) {
System.out.println(state);
}
}
}
- new :新建线程对象就是new状态
- Runnable : 新建状态的下一个状态,可执行状态(真正在运行的(Running)和即将开始执行的(Ready),都是这个状态)
- Blocked:锁等待,需要等待其他线程对象释放锁对象
- WAITTING:等待被另一个线程唤醒(notify方法)
- TIMED_WAITTING: 超时等待,需要等待一段时间后自动唤醒
3,4,5 都是等待状态,都属于线程的阻塞状态(该线程需要暂缓执行,这三个造成暂缓执行的原因不同),当前线程暂停执行,等待其他任务或者资源
6.Terminated: 终止状态,表示当前线程已经执行结束了,可以被销毁
线程的run方法执行完毕后,或者抛出异常不正常执行完毕都会进入终止状态。