目录
一、线程的状态
网上对线程状态的描述很多,有5种,6种,7种,都可以接受
比较常见的说法是5种,5种状态一般是针对传统的线程状态来说(操作系统层面)
按Java线程区分有6种状态
NEW:Thread对象被创建出来了,但是还没有执行start方法。
RUNNABLE:Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
BLOCKED、WAITING、TIME_WAITING:都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程
BLOCKED:synchronized没有拿到同步锁,被阻塞的情况
WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒
TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒
TERMINATED:run方法执行完毕,线程生命周期到头了
线程状态打印测试:
NEW:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
});
System.out.println(t1.getState());
}
RUNNABLE:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true){
}
});
t1.start();
Thread.sleep(500);
System.out.println(t1.getState());
}
BLOCKED:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(() -> {
// t1线程拿不到锁资源,导致变为BLOCKED状态
synchronized (obj){
}
});
// main线程拿到obj的锁资源
synchronized (obj) {
t1.start();
Thread.sleep(500);
System.out.println(t1.getState());
}
}
WAITING:
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
Thread t1 = new Thread(() -> {
synchronized (obj){
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
Thread.sleep(500);
System.out.println(t1.getState());
}
TIMED_WAITING:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(500);
System.out.println(t1.getState());
}
TERMINATED:
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());
}
二、线程的常用方法
1 获取当前线程
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 获取当前线程的方法
Thread main = Thread.currentThread();
System.out.println(main);
}
2 线程的名字
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println(Thread.currentThread().getName());
});
t1.setName("模块-功能-计数器");
t1.start();
}
3 线程的优先级
线程的优先级就是CPU调度线程的优先级。
Java线程的有10个优先级可以设置,从1~10任取一整数。
超过这个区间,会抛出参数异常错误。
先执行的线程级别低,也是需要高级别执行完之后,才执行。
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("t1:" + i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("t2:" + i);
}
});
t1.setPriority(1);
t2.setPriority(10);
t2.start();
t1.start();
}
4 线程的让步
Thread的静态方法yield,可以让当前线程从运行状态转变为就绪状态。
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
if(i == 50){
Thread.yield();
}
System.out.println("t1:" + i);
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 100; i++) {
System.out.println("t2:" + i);
}
});
t2.start();
t1.start();
}
5 线程的休眠
Thread的静态方法sleep,让线程从运行状态转变为等待状态
sleep有两个方法重载:
- 第一个就是native修饰的,让线程转为等待状态的效果
- 第二个是可以传入毫秒和一个纳秒的方法(如果纳秒值大于等于0.5毫秒,就给休眠的毫秒值+1。如果传入的毫秒值是0,纳秒值不为0,就休眠1毫秒)
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(1000);
System.out.println(System.currentTimeMillis());
}
6 线程的强占
Thread的非静态方法join方法,在main线程中调用了t1.join(),那么main线程会进入到等待状态,需要等待t1线程全部执行完毕,在恢复到就绪状态等待CPU调度。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("t1:" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.start();
for (int i = 0; i < 10; i++) {
System.out.println("main:" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 1){
try {
// 调用join方法,main线程会进入等待状态,
// 需要等待t1执行2s后,再恢复就绪状态等待CPU调度。
// 如果t1结束了,main线程就自动变为就绪状态等待调度。
t1.join(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
7 守护线程
创建的线程,默认都不是守护线程。JVM在程序中没有非守护线程,就会结束当前JVM,主线程默认是非守护线程。Daemon守护线程设置,默认false。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
System.out.println("t1:" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t1.setDaemon(true);
t1.start();
}
8 线程的等待和唤醒
wait方法将进入等待池,并释放锁资源。
notify或者notifyAll方法,将等待池中的线程唤醒,添加到锁池中。
notify随机的唤醒等待池中的一个线程到锁池
notifyAll将等待池中的全部线程都唤醒,并且添加到锁池
wait方法和notify以及norifyAll方法,必须在synchronized修饰的代码块或者方法内部使用,因为要操作基于某个对象的锁的信息维护。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
sync();
},"t1");
Thread t2 = new Thread(() -> {
sync();
},"t2");
t1.start();
t2.start();
Thread.sleep(12000);
synchronized (MiTest.class) {
MiTest.class.notifyAll();
}
}
public static synchronized void sync() {
try {
for (int i = 0; i < 10; i++) {
if(i == 5) {
MiTest.class.wait();
}
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
三、线程的结束方式
1 stop方法(不建议)
stop方法是进行强制结束,极不建议使用,可能会有问题。
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
Thread.sleep(500);
t1.stop();
System.out.println(t1.getState());
}
2 使用共享变量(很少会用)
有些线程可以使用死循环保证线程一直运行,可以通过变更共享变量,破坏死循环,使线程退出线程,结束run方法。
static volatile boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(flag){
// 处理任务
}
System.out.println("任务结束");
});
t1.start();
Thread.sleep(500);
flag = false;
}
3 interrupt方式
public static void main(String[] args) throws InterruptedException {
// 线程默认情况下, interrupt标记位:false
System.out.println(Thread.currentThread().isInterrupted());
// 执行interrupt之后,再次查看打断信息
Thread.currentThread().interrupt();
// interrupt标记位:ture
System.out.println(Thread.currentThread().isInterrupted());
// 返回当前线程,并归位为false interrupt标记位:ture
System.out.println(Thread.interrupted());
// 已经归位了
System.out.println(Thread.interrupted());
// =====================================================
Thread t1 = new Thread(() -> {
while(!Thread.currentThread().isInterrupted()){
// 处理业务
}
System.out.println("t1结束");
});
t1.start();
Thread.sleep(500);
t1.interrupt();
}
打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
while(true){
// 获取任务
// 拿到任务,执行任务
// 没有任务了,让线程休眠
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("基于打断形式结束当前线程");
return;
}
}
});
t1.start();
Thread.sleep(500);
t1.interrupt();
}
wait和sleep的区别?
- sleep属于Thread类中的static方法、wait属于Object类的方法
- sleep属于TIMED_WAITING,自动被唤醒、wait属于WAITING,需要手动唤醒。
- sleep方法在持有锁时,执行,不会释放锁资源、wait在执行后,会释放锁资源。
- sleep可以在持有锁或者不持有锁时,执行。 wait方法必须在只有锁时才可以执行。
wait方法会将持有锁的线程从owner移到WaitSet集合中,这个操作是在修改ObjectMonitor对象,需要持有synchronized锁的,才能操作ObjectMonitor对象的。