Java多线程.线程状态

线程一共有:新建,可执行(执行中),阻塞,等待(等待时机),时间等待(等多久就不能了),完结。这几种状态。影响状态流转的有Object中的wait/noriy方法,也有Thread类的控制线程的静态方法,也有Unsafe为底层实现的park/unpark,以及封装Unsafe的LockSupport类直接操作线程。同时一些控制同步的逻辑如synchronized,以及直接上锁的Lock,由于需要等待资源的释放,也可以导致状态的变更。

       参考资源:Java语言定义的线程状态分析 - Trust_FreeDom - 博客园

六种状态

对于Java中Thread.State,以下是对doc的翻译:

public enum ThreadState1 {

    /**
     * 该状态标识还没有启动的状态
     * Thread state for a thread which has not yet started.
     */
    NEW,

    /**
     * 该状态标识可运行的线程,一个可运行状态的线程就意味着这个线程已经在虚拟机中等待系统资源,比如处理器
     * Thread state for a runnable thread.  A thread in the runnable
     * state is executing in the Java virtual machine but it may
     * be waiting for other resources from the operating system
     * such as processor.
     */
    RUNNABLE,

    /**
     * 该状态标识一个线程为了一个监控器锁阻塞,该线程等待该监控器锁。在进入或者重新进入同步方法块或者方法时阻塞的。
     * Thread state for a thread blocked waiting for a monitor lock.
     * A thread in the blocked state is waiting for a monitor lock
     * to enter a synchronized block/method or
     * reenter a synchronized block/method after calling
     * {@link Object#wait() Object.wait}.
     */
    BLOCKED,

    /**
     * 该状态标识一个等待的线程
     * 进入该状态通过调用:Object.wait;Thread.join;LockSupport.park。
     * 处于等待状态的是线程是等待其他一个线程来做一个动作,比如:
     * 1. 一个线程中调用了Object.wait()方法在一个对象上,调用这个对象的方法,首先得获取这个对象的monitor。
     * 调用后线程就等待其他线程调用该对象的Object.notify()或者Object.notifyAll(),其他线程调用后调用wait方法的线程才继续
     * 
     * 2. 调用了Thread.join()的,就要等待调用线程完成
     * 
     * Thread state for a waiting thread.
     * A thread is in the waiting state due to calling one of the
     * following methods:
     * <ul>
     *   <li>{@link Object#wait() Object.wait} with no timeout</li>
     *   <li>{@link #join() Thread.join} with no timeout</li>
     *   <li>{@link LockSupport#park() LockSupport.park}</li>
     * </ul>
     *
     * <p>A thread in the waiting state is waiting for another thread to
     * perform a particular action.
     *
     * For example, a thread that has called <tt>Object.wait()</tt>
     * on an object is waiting for another thread to call
     * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
     * that object. A thread that has called <tt>Thread.join()</tt>
     * is waiting for a specified thread to terminate.
     */
    WAITING,

    /**
     * 该线程状态标识该线程在等待特定的时间。
     * 可以通过以下方法使得线程来等待特定时间。
     * Thread state for a waiting thread with a specified waiting time.
     * A thread is in the timed waiting state due to calling one of
     * the following methods with a specified positive waiting time:
     * <ul>
     *   <li>{@link #sleep Thread.sleep}</li>
     *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
     *   <li>{@link #join(long) Thread.join} with timeout</li>
     *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
     *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
     * </ul>
     */
    TIMED_WAITING,

    /**
     * 标识该线程已经完结
     * Thread state for a terminated thread.
     * The thread has completed execution.
     */
    TERMINATED;
}

影响状态的操作

状态流转不仅仅只关注状态流转,还需要关注是否释放锁。

Object

 Wait和notify使用:

  1. 这两个方法是某对象调用的,调用的时候一定得获取到了该对象的monitor,也就是说这两个方法只能在synchronized中调用,否则报:IllegalMonitorStateException异常。
  2. synchronized (obj) { //获取该obj的monitor
        obj.wait();  //该线程开始等待,直到其他线程调用该对象obj.notify,这个方法是释放obj锁的,不释放其他线程怎么获取到,获取不到就没法调用
    }
  3. 获取对象的monitor通过同步方法块,方法,静态方法获得
  4. 可以多个线程在obj上wait,也可以多个线程在obj进行notify
  5. Notify唤醒一个线程,如果多个线程在obj上等待那么只唤醒其中一个,所以一般使用notifyall方法
  6. 带有时间类型的wait,除了其他线程通过obj唤醒外,还可以等到时间到了自动唤醒

示例:生产者和消费者示例

public class ConsumerProduct{
	private Logger log = Logger.getLogger(this.getClass());
	//最大数量,大于这个就得等消费者消费
	private final static int MAX_PRODUCT = 5; 
//最小数量,小于这个就得等待生产者生成
	private final static int MIN_PRODUCT = 1; 
	
	private class Lock1{}
	private Lock1 lock = new Lock1(); //锁的对象
	
	private int product = 0  ; //当前产品数量
    
	/**
	 * 生产
	 */
	public void product(){
		//获取lock的monitor
		synchronized (lock) {
			while(this.product >= MAX_PRODUCT){
				try {
					log.info("产品已满,卖的有空位了再生产");
					//该线程就等待,无限制等待
					lock.wait();
				} catch (Exception e) {
					System.err.println(e);
				}
			}
			
			this.product++;
			log.info("生产者生产第" + this.product + "个产品.");
			//通知等待区的消费者可以取出产品了
			lock.notifyAll();
		}
	}
	
	/**
	 * 消费
	 */
	public void consumer(){
		//获取lock的monitor
		synchronized (lock) {
			while(this.product < MIN_PRODUCT ){
				try {
					log.info("缺货,等会再取,数量:"+this.product);
					//最长等待3s后继续查看
					lock.wait(1000L * 3);
					log.info("终于等到了,能抢到不,数量:"+this.product);
				} catch (Exception e) {
				}
			}

			log.info("消费者取走了第" + this.product + "个产品.");
			this.product--;
			//唤醒所有生产者
			lock.notifyAll();
		}
	}
}

调用日志:

2018-07-19 11:21:20,284 [ct1]INFO[***.ConsumerProduct] - 缺货,等会再取,数量:0
2018-07-19 11:21:20,284 [pt2]INFO[***.ConsumerProduct] - 生产者生产第1个产品.
2018-07-19 11:21:20,284 [pt3]INFO[***.ConsumerProduct] - 生产者生产第2个产品.
2018-07-19 11:21:20,284 [pt1]INFO[***.ConsumerProduct] - 生产者生产第3个产品.
2018-07-19 11:21:20,284 [ct3]INFO[***.ConsumerProduct] - 消费者取走了第3个产品.
2018-07-19 11:21:20,284 [ct2]INFO[***.ConsumerProduct] - 消费者取走了第2个产品.
2018-07-19 11:21:20,284 [ct1]INFO[***.ConsumerProduct] - 终于等到了,能抢到不,数量:1
2018-07-19 11:21:20,284 [ct1]INFO[***.ConsumerProduct] - 消费者取走了第1个产品.

Thread. sleep

Thread.sleep(s); 该线程主动进入Timed_Waiting状态s时间,并不释放锁。

​​Thread.Suspend/ Thread.Resume

Thread.join

/**
	 * 测试ThreadObj.join方法
	 * 1. 作用:该方法是使得并行的线程操作变为串行操作,比如在:main方法中调用其他线程的join方法,则mian就在这个调用点等待调用线程执行,然后才main方法后边的代码执行
	 * 2. t1.join();t1.join(0); main方法中调用
	 * 	- main方法会等t1线程执行完后再执行
	 * 3. t1.join(1000l * 5); mian方法中调用
	 * 	- main方法会最长等待t1线程执行5s,然后两个线程并行执行
	 * 4. join和start都是Thread类的非native方法,join 必须在 start之后执行才有效
	 * 5. join的原理
	 * 	- 通过wait方法类实现
	 * 	- 示例一:t2中调用t1.join(); 逻辑如下:
	 * 		1. t2得获取t1对象的锁
	 *		2. t2线程在t1对象上等待(使用t1.对象执行wait方法),状态是Waiting
	 *		3. 直到执行t1.notify后,t2线程的状态变为Blocked,并开始获取t1的锁
	 *		4. 如果在t2线程Waiting的时间里,其他线程获取到了t1对象锁,且一直不释放,t2就会一直阻塞到获取该锁
	 *		5. t2获取该所后继续执行
	 *	- 示例二:t2中调用t1.join(1000l * 2); 逻辑如下:
	 *		1. t2 获取 t1对象的锁
	 *		2. t2 线程在t1对象上等待(因为:t1.join(2s)中执行了t1.wait(2s)方法),此时t2线程的状态为Waiting
	 *		3. 当有手动执行t1.notify或者2s时间到的时候,t2线程状态由Waiting转为Blocked,并开始获取t1锁
	 *		4. 如果有线程或者t1线程run方法有锁t1对象,t2线程也会阻塞,直到获取到t1的锁,才继续执行
	 *  - 综述:在哪跌倒在哪爬起来,我在t1锁上等待,唤醒或者时间到的时候,我也要获取到t1才可以继续。
	 */
	public static void fun5(){
		JoinTestThread2 t2 = new JoinTestThread2();
		t2.start();
	}
public class JoinTestThread2 extends Thread{
	private Logger log = Logger.getLogger(this.getClass());
	public void run() {
		log.info("t2 start ...");
		
		JoinTestThread1 t1 = new JoinTestThread1();
		t1.setT2(this);
		t1.start();
		
		try {
			log.info("t2 join t1 before ...");
			t1.join(1000l * 2);
			log.info("t2 join t1 end ...");
		} catch (InterruptedException e) {}
		
		log.info("t2 end ...");
	}
}
public class JoinTestThread1 extends Thread{
	private Logger log = Logger.getLogger(this.getClass());
	private Thread t2;
	public void setT2(Thread t2) {
		this.t2 = t2;
	}
	public void run() {
		try {
			log.info("t1 before .."+t2.getState());
			synchronized(this){
				Thread.sleep(1000l * 5);
				log.info("t1 after1 .." + t2.getState());
				this.notifyAll();
				Thread.sleep(1000l * 5);
				log.info("t1 after2 .." + t2.getState());
			}
		} catch (InterruptedException e) {}
	}
}

ThreadObj.interrupt/isInterrupted

Interrupt()方法只是设置了一个标记位,而正常执行的方法就不判断这个标记位,只有状态变化的时候才进行检查。

1.正常运行的线程不影响,但运行中又sleep或者wait等调用,那么该线程就直接挂掉。

2.处于waiting或者timed_waiting状态的线程,调用过程interruput方法可以直接终止线程。

3.正常运行的线程调用interrupt方法后,可以使用isInterrupted返回为true,从而从代码维度进行终止。

示例1:interrupt调用后正常运行的线程不影响,变化状态时挂掉

public class TestInterrupt {
	public static void main(String[] args) throws Exception {
		InterruptThread1 t1 = new InterruptThread1();
		t1.start();
		Thread.sleep(1000l * 2);
		t1.interrupt();//终止
	}
	public static class InterruptThread1 extends Thread{
		public void run() {
			try {
				int i = 0 ;
				while(i++ < 500000){
					System.out.println(i);
				}
				System.out.println("before ..." + this.getState());
				//1. Thread.sleep(1000l * 1);
				//2.wait
				synchronized(this){
					wait(1000l);
				}
				System.out.println("end ..."+ this.getState());
			} catch (Exception e) {}
		}
	}
}
结果:
499998
499999
500000
before...RUNNABLE

示例2.调用interrupt方法直接终止waiting和timed_waiting状态线程

public class TestInterrupt2 {
	public static void main(String[] args) throws Exception {
		InterruptThread2 t2 = new InterruptThread2();
		t2.start();
		Thread.sleep(1000l * 2);
		t2.interrupt();  //终止
		
		Thread.sleep(1000l * 2);
		
		System.out.println(t2.getState());
	}
	public static class InterruptThread2 extends Thread{
		public void run() {
			try {
				synchronized(this){
					System.out.println("before wait ..");
					Thread.sleep(1000l * 3);//或者 wait(); 
					System.out.println("wait end ..");
				}
			} catch (Exception e) {}
		}
	}
} 
打印结果:
before wait ..
TERMINATED

使用isInterrupted获取是否有调用过interrupt方法来终止代码

public class TestInterrupt3 {

	public static void main(String[] args) throws Exception {
		InterruptThread1 t1 = new InterruptThread1();
		t1.start();
		Thread.sleep(1000l * 2);
		t1.interrupt();//终止
	}

	public static class InterruptThread1 extends Thread{
		public void run() {
			try {
				int i = 0 ;
				while(!this.isInterrupted() && i++ < 500000){
					System.out.println(i);
				}
			} catch (Exception e) {}
		}
	}
}

结果:
只要执行了终止方法,就不在打印了。

状态流转

状态流转不仅仅只关注状态流转,还需要关注是否释放锁。

跟锁有关系的,只有wait、notify、sleep。只有wait失去锁。

其他跟锁没关系,比如:join,unsafe.park/unpark,interrupt都是线程间通信。


系列内容:

Java多线程.线程状态_闲猫的博客-CSDN博客_java线程的状态

Java多线程.Volatile,transient,Monitor,goto_闲猫的博客-CSDN博客_java monitor

Java多线程.Unsafe_闲猫的博客-CSDN博客

Java多线程.三种实现方式_闲猫的博客-CSDN博客_java多线程实现的代码

Java多线程.ReentrantLock_闲猫的博客-CSDN博客

Java多线程.Synchronized_闲猫的博客-CSDN博客

Java多线程.LockSupport_闲猫的博客-CSDN博客

Java多线程.线程检测_闲猫的博客-CSDN博客_java 多线程测试

Java多线程.CAS_闲猫的博客-CSDN博客_java多线程cas

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闲猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值