多线程02

线程的几种状态

线程状态概述

当线程被创建并启动之后,它既不是一启动就进入到了执行状态,也不是一直处于执行状态,在线程的声明周期中有6中状态,在java的API帮助文档中java.util.Thread.State这个枚举给出了线程的6种状态.

线程状态导致状态发生条件
NEW(新建) 线程刚被创建,但是还没有启动,还没有调用start方法
Runnable(可运行) 线程可以在java虚拟器中运行的状态,可以是正在运行自己的代码,也可能没有运行,这取决于处理器
Blocked(锁阻塞) 当一个线程试图获取一个对象锁,而对象锁被其他线程所持有,则该线程进入到Blocked(锁阻塞状态),当该线程持有锁时,该线程就进入到Runnable状态.
WAITING(无线等待) 一个线程在等待另一个线程执行一个动作(新建时),该线程进入到Wating状态,进入这个Wating状态后,是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能唤醒
TIMED_WAITING(计时等待) 同Wating状态,有几个方法有超时参数,调用他们将进入Timed Wating状态,这一状态将一直保持到超时期满或者是收到唤醒通知,带有超时参数的常用方法有Thread.sleep().Object.wait().
TERMNATED(被终止) 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡.

六种状态切换原理:

New 新建状态: 需要Therad或者Runable的实现 需要等待start方法开启

RUNNABLE:可运行状态 :被start调用的新建线程

Blocked阻塞状态:多个线程之间互相争夺cpu的使用权的执行时间,得到或者释放了线程切换到其他线程,那么这个线程就会在可运行状态和阻塞状态来回切换,拥有cpu的执行资格,等待cpu空闲时执行

TIMED_WAITING休眠计时等待状态 方法中被sleep(long)或者wait(long)设置线程会被休眠状态,等待计时结束自动唤醒或者被主动唤醒进入运行状态,休眠状态:放弃了cpu的执行资格,cpu空闲也不执行

WAITNG无限(永久等待状态) Obect.wait()计时唤醒方法 Object.notify()唤醒线程方法 此状态只有被唤醒才能进入运行状态

TERMINATED 死亡状态 run方法结束 或者使用stop() 线程被终止.

备注:

  • 休眠计时等待状态和永久等待状态也被称为冻结状态

  • 无限等待状态和 TIMED_WAITING计时等待状态被唤醒但是没有拿到锁,依然会进入阻塞状态.

Timed Wating(计时等待)

Timed Wating在javaAPI中描述为:一个正在限时等待另一个线程执行一个(唤醒)动作的线程处于这一状态

其实当我们调用了sleep方法之后,当前正在执行的线程就进入到了计时等待状态.

public void sleep(long a): 线程暂停方法

练习: 实现一个计时器,计数到100,在每个数字之间暂停一秒,每搁10个数组输出一个字符串

public class MyThread extends Thread{
   Override
       public void run(){
       for(int i=1;i<100;i++){
           if(i/10==0){
               System.out.println("==============================");
          }
           System.out.println(i);
           //在每个数字之间我要暂停一秒
           try{
           Thread.sleep(1000);
          }catch(Exception e){
               e.printStachTrace();
          }
      }
  }
   //准备一个main函数
   public static void main(String [] args){
       new MyThread().start();
  }
}

备注:

1.进入到Timed Waiting状态的一种常见的操作是调用sleep方法,单独的线程也可以调用,不一定非要有协作关系

2.为了让其他线程有机会执行到,一般建议将Thread.sleep调用放到线程run方法内,这样才能保证该线程执行过程中会睡眠.

3.sleep与锁无关,线程睡眠到期会自动苏醒,并返回到Runnable状态.sleep里面的参数指定的时间是线程不会运行的最短时间.,因此sleep()方法不能保证该线程睡眠到期后就会立刻开始执行,

Blocked锁阻塞状态

blocked状态在javaAPI中描述为:一个正在阻塞等待一个监视器锁(锁对象)的线程处于这一状态.

比如:线程A与线程B代码中使用了同一把锁,如果线程A获取到了锁对象,线程A就进入Runnable状态,反之线程B就进入了Blocked锁阻塞状态.

Waiting 无限等待状态

waiting状态在JavaAPI中的描述为:一个正在无线等待另一个线程执行一个特别的(唤醒)动作的线程处于这一状态.

一个调用了某个对象的Object,wait()方法的线程,会等待另一个线程调用此对象Objectnotify()或者Object.notifyAll()方法

其实waiting状态并不是一个线程的操作,它体现的是多个线程之间的通信*.可以理解为东哥线程之间的协作关系,多个线程会争取锁,同时相互之间有存在协作关系

等待唤醒机制

线程间通信

概念:多个线程在处理同一资源,但是处理的动作(线程的任务)却又不相同.

比如说,线程A用来生存一个娃哈哈,线程B用来消费娃哈哈饮料.娃哈哈饮料可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消费.那么线程A与线程B之间就存在线程通信问题.

为什么要处理线程之间的通信:

多个线程并发在执行时,在默认情况下CPU随机切换线程的,当我们需要多个线程共同来完成一件任务时,并且我们希望他们有规律的执行,那么多线程之间就需要一些协调通信,以此类帮助我们达到多线程共同操作一份数据.

如何保证线程之间有效利用资源:

多个线程在处理同一个资源的时候,并且任务还不相同,需要线程通信来帮助我们解决线程之间对同一变量的操作

就是多个线程在操作同一份数据时,为了避免对同一共享变量的争夺,也就是我们需要通过一定的逻辑来使各个线程有效的利用资源.而这些逻辑 就是利用等待唤醒机制(wait()和notify())

等待唤醒机制

这是多个线程之间的一种协作机制.

就是一个线程进行了规定操作后,就进入了等待操作.(wait()),等待其他线程执行完他们的指定代码后,再将其唤醒(notify);

在有多个线程进行等待时,如果需要唤醒所有线程,可以使用notifyAll()来唤醒所有的等待线程

wait//notify就是线程间的一种协作机制.

等待唤醒中的方法:

等待唤醒机制就是用来解决线程间通信问题的.可以使用到的方法有三个如下:

  • wait():线程不再活动,不再参与CPU的调度,进入到wait set中,因此不会浪费CPU资源,也不会去竞争锁,这时的线程的线程状态就是WAITING状态.他还要等着别的线程执行一个特别的动作,就是唤醒通知(notify)在这个对象上等待的线程从wait set中释放出来,重新进入到调度队列(ready queue)中.

  • notify():选取所通知对象的 wait set中的一个线程释放.例如:餐厅有空位置后,等候就餐最久的顾客最先入座.

  • notifyAII():释放所通知对象的wait set中的正在等待的全部线程.

备注:

哪怕只是通知了一格等待线程,被通知的线程也不会立即恢复执行,因为他当初中断的地方是在同步块内,而此刻他已经不持有锁了,所以他需要再次尝试着去获取锁(很可能面临着其他线程的竞争),成功后才能当初调用wait方法之后的地方恢复执行.总结下:

如果能获取到锁,线程就从WAITING状态转变成RUNNABLE状态

否则从,wait set 中,又进入set中,线程就从WAITING状态转变成BLOCKED状态.

调用wait和notify的注意细节:

1.wait方法与notify方法必须有同一个锁对象.因为对应的锁对象才能通过notify唤醒使用同一个锁对象调用的wait方法后的线程.

2.wait方法与notify方法时属于Object类的方法的,因为锁对象可以是任意对象.因为锁对象可以是任意对象,而任意对象的所属类都是继承了Object类

3.wait方法与motify方法必须要在同步代码块或者同步方法中使用..因为,必须通过锁对象调用这两个方法来实现等待与唤醒.

生产者与消费者问题

等待唤醒机制案例:

举一个例子:生产包子与消费包子

包子铺线程生产包子,吃货线程消费包子.当没有包子的时候(包子的状态位false),吃货线程需要等待,包子铺线程生产包子(包子的状态为true),并通知吃货线程(接触吃货等待的状态),因为已经有了包子,那么包子铺线程就需要进入到等待状态
   接下来,吃货线程能够记你一步执行则取决于锁的获取情况,如果吃货线程获取到锁,那么就执行吃包子的动作,包子吃完了(包子的状态位false),需要通知包子铺线程(接触包子铺线程等待状态),此时吃货线程就进入到等待状态.包子铺线程能否进一步执行则取决于锁的获取情况.

 

原文地址:https://www.cnblogs.com/rosiness/p/14135569.html