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

     

  • 相关阅读:
    86. Partition List
    2. Add Two Numbers
    55. Jump Game
    70. Climbing Stairs
    53. Maximum Subarray
    64. Minimum Path Sum
    122. Best Time to Buy and Sell Stock II
    以场景为中心的产品设计方法
    那些产品经理犯过最大的错
    Axure教程:如何使用动态面板?动态面板功能详解
  • 原文地址:https://www.cnblogs.com/rosiness/p/14135569.html
Copyright © 2011-2022 走看看