java中Thread常见方法与线程状态对应关系

一个线程通常有6种状态:初始态、等待态、就绪态、运行态、阻塞态、终止态。如图是线程的各个状态之间的关系和方法。今天就介绍一下一些方法都做了什么,对于线程有什么影响。
线程状态转换

wait()函数

注意,wait()方法是Object所拥有的,当一个线程调用一个对象的wait()方法时,此线程就会被挂起,也就是进入了等待态。那什么时候结束等待呢,即从此方法返回?两种情况:
(1)其他线程调用了Object.notify或notifyAll()方法
(2)其他线程调用了此线程的interrupt()方法导致该线程抛出interruptedException异常而返回。
要注意,调用wait()方法的时候,要事先获得该对象的监视器锁,否则会报出IllegalMonitorStateException异常。例如

synchronized(obj) {				//事先获得了对象obj的监视器锁,所以调用wait()不会报错
	obj.wait();
}
obj1.wait();			//没有获得对象obj1的监视器锁,会直接抛出异常

再回到此方法,wait()方法做了些什么,
(1)首先将自己所在的线程挂了起来,此线程处于等待状态。
(2)释放获得的对象的监视器锁
释放锁很重要,因为这样破坏了死锁的必要条件之一:持有并等待。谁调用了wait()方法,就释放谁的锁,不会影响到此线程获得的其他对象的锁。举个例子:

private static volatile Object resourceA = new Object();
private static volatile Object resourceB = new Object();

public static void main(String[] args) throws Exception {
	Thread threadA = new Thread(new Runnable() {
		public void run() {
			try {
				synchronized (resourceA) {
					System.out.println("threadA get resourceA lock");
					System.out.println("threadA try to get resourceB...");
					Thread.sleep(1000);
					//System.out.println("threadA release resourceA lock");		//(1)
					//resourceA.wait();		//(2)
					synchronized(resourceB) {
						System.out.println("threadA get resourceB lock");
					}
				}
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
	});

	Thread threadB = new Thread(new Runnable() {
		public void run() {
			try {
				synchronized (resourceB) {
					System.out.println("threadB get resourceB lock");
					System.out.println("threadB try to get resourceA...");
					Thread.sleep(1000);
					synchronized(resourceA) {
						System.out.println("threadB get resourceA lock");
						//resourceA.notify();			(3)
					}
				}
			} catch(Exception e) {
				e.printStackTrace();
			}
		}
	});

	threadA.start();
	threadB.start();
	threadA.join();
	threadB.join();
	System.out.println("main over");
}

运行结果如下:可以看到两个线程发生了死锁,线程A在获得了resourceA的锁,去尝试获得resourceB的锁,而线程B恰好相反。所以主进程一直没结束在等两个线程返回。

threadA get resourceA lock
threadA try to get resourceB...
threadB get resourceB lock
threadB try to get resourceA

若把代码(1)(2)(3)三行的注释去掉运行结果如何呢:

threadA get resourceA lock
threadB get resourceB lock
threadB try to get resourceA...
threadA try to get resourceB...
threadA release resourceA lock
threadB get resourceA lock
threadA get resourceB lock
main over

代码(2)就是当线程A获取不到resourceB时暂时释放自己所持有的resourceA,而此时线程B获取到了resourceA继续执行,执行完成后要释放所持有的resourceA,注意此处代码(3)要用notify()方法,若一个对象调用了wait(),则另一个线程释放该对象的锁时一定要用notify()来通知已经释放。若没有用wait()方法,则就不需要notify(),可以看到线程B执行完成后自动释放了resourceB的锁且没有调用notify()方法,而线程A也自动获取了此对象的锁,并执行结束。

notify()函数、notifyAll()函数

和wait()函数类似,notify()函数也是Object拥有的,它会==唤醒一个调用在该共享变量上调用wait系列方法后被挂起的线程。==但被唤醒的进程也不能马上从wait方法返回,还需要获得该对象的监视器锁才可以继续执行,因为该线程即使被唤醒后也会和其他线程竞争该变量的监视器锁。
同样只有当前线程获得共享变量的监视器所后,才可以调用共享变量的notify()方法。
对应的notifyAll()方法会唤醒所有在该共享变量上调用wait()方法的线程,这些线程公平竞争此对象的监视器锁,其实两者类似,只是notifyAll()唤醒了所有线程,但最终都只会有一个线程获得该对象的监视器锁。

join()方法

join方法是Thread直接提供,当在某一个线程A中调用另一个线程B的join方法是,线程A会阻塞,等待线程B执行完毕后返回再继续执行,相当于线程A要确保线程B执行完毕。例如:

public static void main(String[] args) throws InterruptedException {
	Thread threadB = new Thread(new Runnable() {
		public void run() {
			try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
			for (int i = 0; i < 5; i++) {
				System.out.println("threadB is running - " + i);
			}
		}
	});
	System.out.println("main thread is running before threadB");
	threadB.start();
	//threadB.join();			(1)
	System.out.println("main thread end");
}

若对于代码(1)进行注释,结果如下。主线程没有等待线程B执行完便独自继续执行完毕,而线程B则单独进行。

main thread is running before threadB
main thread end
threadB is running - 0
threadB is running - 1
threadB is running - 2
threadB is running - 3
threadB is running - 4

若对于代码(1)不进行注释,结果如下。主线程等待线程B执行完毕后,才继续执行。

main thread is running before threadB
threadB is running - 0
threadB is running - 1
threadB is running - 2
threadB is running - 3
threadB is running - 4
main thread end

sleep()方法

sleep()方法是Thread的静态方法,调用这个方法的含义为:这期间此线程不参与CPU的调度会让出指定时间的执行权,但是该线程所拥有的监视器资源,比如锁还是持有不让出的。例如:

public class SleepTest2 {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    System.out.println("child threadA is in sleep");
                    Thread.sleep(10000);
                    System.out.println("child threadA is in awaked");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                lock.lock();
                try {
                    System.out.println("child threadB is in sleep");
                    Thread.sleep(10000);
                    System.out.println("child threadB is in awaked");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

执行结果如下。说明即使threadA在“休眠”,但是还是没有释放锁,threadB必须等待。

child threadA is in sleep
child threadA is in awaked
child threadB is in sleep
child threadB is in awaked

yield()方法

yield()方法也是Thread的一个静态方法,当一个线程调用Thread.yield()时是在暗示线程调度器当前线程请求让出自己的CPU使用,但线程调度器可以无条件忽略这个暗示。所以调用后,此线程会让出CPU使用权并处于就绪状态,线程调度器会从就绪队列中选一个线程来执行,有可能会调度刚刚让出CPU的那个线程

yield()与sleep()方法联系:

1.两者调用后都不会释放当前线程所拥有的资源,如锁。
2.线程调用sleep()后处于堵塞状态,此期间线程调度器不会去调度该线程;而yield()方法只是让出当前线程的CPU资源,并处于就绪状态,下次有可能继续被调度。

线程中断interrupt()方法

注意Interrupt()方法只是设置了线程的中断标志,并不能直接停止该线程的运行。事实上当某一个线程A调用线程B的interrupt()方法时,往往B还会继续运行。若在线程B因为调用了wait()系列函数、join()方法或者sleep方法而被阻塞挂起,那线程A调用线程B的Interrupt()方法时会抛出异常。利用这个特性可以做一些更加高效的事,例如当线程为等待某些条件时往往需要调用sleep()方法或其他来阻塞挂起自己,那等条件满足时直接就可以调用此线程的interrupt()方法,使其内部抛出异常并返回,这样就不必必须等待sleep()中的时间才能返回。

isInterrupted()方法检查当前线程是否被中断。interrupted()方法检查当前线程是否被中断,该方法是一个静态方法,而且检查的是当前线程的中断标志而不是调用interrupted()方法的实例对象的中断标志。其实现如下:

public static boolean interrupted() {
	return currentThread().inInterrupted(true);
}

看一个例子,了解一下interrupted()与isInterrupted()方法不同之处:

public static void main(String[] args) throws InterruptedException {
        Thread threadOne= new Thread(new Runnable() {
            @Override
            public void run() {
                for (;;) {}
            }
        });

        threadOne.start();
        //设置中断标志
        threadOne.interrupt();

        //获取中断标志
        System.out.println("isInterrupted:" + threadOne.isInterrupted());
        //获取中断标志并重置
        System.out.println("isInterrupted:" + threadOne.interrupted());
        //获取中断标志并重置
        System.out.println("isInterrupted:" + Thread.interrupted());
        //获取中断标志
        System.out.println("isInterrupted:" + threadOne.isInterrupted());

        threadOne.join();
        System.out.println("main thread is over");
    }

运行结果如下:第一个true和最后一个true可以理解,因为前面刚设置,而中间的两个false表明调用interrupted()方法时获得的是当前线程的中断标志,此处即是主线程的中断标志位,当然是false。

isInterrupted:true
isInterrupted:false
isInterrupted:false
isInterrupted:true
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值