zoukankan      html  css  js  c++  java
  • 多线程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),需要通知包子铺线程(接触包子铺线程等待状态),此时吃货线程就进入到等待状态.包子铺线程能否进一步执行则取决于锁的获取情况.

     

  • 相关阅读:
    项目有大小,生存各有道
    学习Spark——那些让你精疲力尽的坑
    学习Spark——环境搭建(Mac版)
    小程序新能力-个人开发者尝鲜微信小程序
    如何写出好代码
    华为手机nova2s使用第三方字体库
    std::string与std::wstring互相转换
    Steam安装Google Earth VR
    osgearth2.8关于RectangleNodeEditor编辑点不可见的问题
    Qt生成ui文件对应的.h和.cpp文件
  • 原文地址:https://www.cnblogs.com/rosiness/p/14135569.html
Copyright © 2011-2022 走看看