• Thread之二:sleep、wait、yield、join


     《Thread之一:线程生命周期及五种状态

    Thread之二:sleep、wait、yield、join

     《juc线程池原理(四): 线程池状态介绍

    一、回顾下概念

    Java中的多线程是一种抢占式的机制而不是分时机制。线程主要有以下几种状态:新建,就绪,运行,阻塞,死亡。抢占式机制指的是有多个线程处于就绪状态,但是只有一个线程在运行。
     

    1.sleep()方法

      在指定时间内让当前正在执行的线程暂停执行,但不会释放“锁标志”。不推荐使用。

      sleep()使当前线程进入阻塞状态,在指定时间内不会执行。

    2.wait()方法

      在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。

      当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。

      唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。

      waite() 和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生 IllegalMonitorStateException的异常。

    3.yield方法

      暂停当前正在执行的线程对象。

      yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。

      yield()只能使同优先级或更高优先级的线程有执行的机会。

    4.join方法

      等待该线程终止。

      等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。

    二、线程同步

    一个线程结束的标志是:run()方法结束。
    一个机锁被释放的标志是:synchronized块或方法结束。
    当有多个线程访问共享数据的时候,就需要对线程进行同步。线程同步相关的方法中的几个主要方法的按照所属可以分成:
    • Thread类的方法:sleep(),yield()等
    • Object的方法:wait()和notify()、notifyAll()等

    Object中的对象头存放的锁信息在控制同步访问时使用。见《java对象在内存中的结构(HotSpot虚拟机)》和《Synchronized之二:synchronized的实现原理

    Wait()方法和notify()方法:当一个线程执行到wait()方法时,它就进入到一个和该对象相关的等待池中,同时失去了对象的机锁。当它被一个notify()方法唤醒时,等待池中的线程就被放到了锁池中。该线程从锁池中获得机锁,然后回到wait()前的中断现场。
     
    Thread类中的方法:
    由于sleep()方法是Thread类的方法,因此它不能改变对象的机锁。所以当在一个Synchronized方法中调用sleep()时,线程虽然休眠了,但是对象的机锁没有被释放其他线程仍然无法访问这个对象。而wait()方法则会在线程休眠的同时释放掉机锁,其他线程可以访问该对象。
    Yield()方法:是停止当前线程,让同等优先权的线程运行。如果没有同等优先权的线程,那么Yield()方法将不会起作用。
    join()方法:是由一个线程调用另一个线程,调用线程等待被调用线程终止。
     

    sleep()与wait()的共同点及不同点:

    共同点: 他们都是在多线程的环境下,都可以在程序的调用处阻塞指定的毫秒数,并返回。
    不同点: Thread.sleep(long)可以不在synchronized的块下调用,而且使用Thread.sleep()不会丢失当前线程对任何对象的同步锁(monitor);
                  object.wait(long)必须在synchronized的块下来使用,调用了之后失去对object的monitor, 这样做的好处是它不影响其它的线程对object进行操作。

    举个java.util.Timer的例子来说明。

    复制代码
    private void main Loop() {
            while (true) {
            ....
            synchronized(queue) {
            .....
            if (!taskFired) // Task hasn't yet fired; wait
                   queue.wait(executionTime - currentTime);
            }
    }
    复制代码

    在这里为什么要使用queue.wait(),而不是Thread.sleep(), 是因为暂时放弃queue的对象锁,可以让允许其它的线程执行一些同步操作。如:

    private void sched(TimerTask task, long time, long period) {
              synchronized(queue) {
                  ...
                  queue.add(task);
              }
    }

    但是正如上篇文章讲到的,使用queue.wait(long)的前提条件是sched()动作执行的时间很短,否则如果很长,那么queue.wait()不能够按时醒来。

  • 相关阅读:
    java判断字符串是否为数字
    门萨高智商者的集中营
    Android全局变量是用public&nbsp…
    oracle 关闭查询的进程
    oracle 常用参考
    oracle创建临时表
    透明网关设置
    透明网关diy
    又一个下拉菜单导航按钮
    数据库备份或导出时丢失主键的相关知识
  • 原文地址:https://www.cnblogs.com/duanxz/p/3737186.html
Copyright © 2020-2023  润新知