倒霉的菜鸟

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理
  • 调用sleep方法将时线程进入休眠状态

public class ThreadTest implements Runnable{

    @Override
    public void run() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  这段代码的意思就是让当前线程休眠1000毫秒,也就是1秒

对sleep方法的调用将抛出 InterruptedException, 因为异常不能跨线程传播回main(),所以必须在本地处理所有在任务内部产生的异常。

我们看下sleep方法的源码

/**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    // BEGIN Android-changed: Implement sleep() methods using a shared native implementation.
    public static void sleep(long millis) throws InterruptedException {
        sleep(millis, 0);
    }

  注释里有这么一句话: 

The thread does not lose ownership of any monitors.

翻译过来就是说,调用sleep方法的线程不会失去对监视器的控制, 什么是监视器呢,我们在之前的文章中说过,就是对象锁。

所以, sleep方法使线程进入休眠状态,停止执行指定的时间, 但不会释放锁。

可以看到源码中直接调用了 sleep(long millis, int nanos)  这个方法的意思就是时线程休眠指定的毫秒数+指定的纳秒数

 

  • 调用wait方法线程进入等待状态,(在当前对象锁上等待),等待和休眠都属于阻塞,但是wait方法将释放锁

我们使用wait方法通常是因为线程需要等待某个外部条件发生变化,而改变这个条件超出了当前方法的控制能力。所以需要释放锁,让其他方法可以被调用,以产生某些条件变化

wait也有两种调用形式(有参和无参)

private synchronized void test() throws InterruptedException {
Thread thread = new Thread();
//使当前线程无限等待
thread.wait();
//使当前线程停止执行1000毫秒
thread.wait(1000);
//使当前线程停止执行1000毫秒 + 500纳秒
thread.wait(1000, 500);
}

对于指定时间的wait方法, 时间到期后线程会恢复执行,这里和sleep相似, 不同的是wait会释放锁

对于不指定时间的wait, 线程将无限等待,直到接收到notify()或者notifyAll()

点开源码

/**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (<condition does not hold>)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @throws  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

  看起来比较奇怪的一点就是,wait(), notify(), notifyAll()这些方法作为线程的功能, 却定义在基类Object中, 而不是在Thread

因为这些方法操作的锁也是所有对象的一部分

因此, wait(), notify(), notifyAll()都只能在同步方法或者同步控制块中调用, 也就是说调用该方法的线程必须已经获取了锁。 否则将得到IllegalMonitorStateException

sleep因为不用操作锁, 因此可以在非同步方法中调用

通常我们在while循环里调用wait()方法, 本质就是需要检查某个特点的条件, 当收到notify/notifyAll时进行判断, 如果不满足, 则返回到wait中继续等待

 

notify()方法将唤醒等待同一个锁的线程中的某一个线程

/**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively competing to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     * Only one thread at a time can own an object's monitor.
     *
     * @throws  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notifyAll()
     * @see        java.lang.Object#wait()
     */
    @FastNative
    public final native void notify();

  

这个方法的源码我看不到, 但可以看到注释,意思是说:

1, 如果当前对象的监视器上有等待的线程,则唤醒其中的一个

2, 唤醒哪一个呢?选择是随机的,取决于你的实现

      因此,如果你想要使用notify, 就必须确保被唤醒的是恰当的任务。 所有任务(如果有)必须等待相同的条件,否则, 你不会知道是否唤醒了恰当的任务。当条件发生变化时, 只能有一个任务能够从中受益。   

3, 唤醒的是调用了wait方法在此对象监视器上等待的线程

4, 被唤醒的线程并不会立即执行,要等到当前线程(调notify的线程)释放锁之后

5, 释放锁之后,被唤醒的线程也不一定会立即执行,如果当前还有其他线程在等待锁, 则该线程要和其他线程竞争

6,调用此方法(notify)的线程必须已经获得了对象监视器,即对象锁 

7,任意时刻下,只有一个线程可以获得对象锁

8,获得对象锁的三种方法: 同步方法,同步块,同步静态方法(前文中说过,synhronized修饰staitc方法时, 锁住的是整个类)

 

notifyAll和notify的区别是: notifyAll()将唤醒在等待这个锁的所有任务

 

小结: sleep和wait的区别

两者都将使当前线程进入阻塞状态, sleep使当前线程休眠指定时间,wait使当前线程等待指定时间或无限等待。 

sleep不释放锁,休眠指定时间后会继续执行, wait 会释放锁, 等待指定时间后需要重新获取锁,不一定会立即执行

wait无限等待时, 必须调用notify/notifyAll才能被唤醒

wait因为要操作锁, 所以必须在同步方法/同步代码块中调用,否则会抛出运行时异常IllegalMonitorStateException, sleep不操作锁,没有限制

这一点上,notify/notifyAll和wait一样,调用这些方法的线程必须自己已经获得了对象锁

 

 

线程相关的一些其他方法:

setPriority()/getPriority()  设置/获取线程的优先级, 通常在run方法的开头去设定

当多个线程竞争一把锁时,调度器倾向于让优先权更高的线程先执行

在绝大多数时间里,所有线程应该以默认优先级(NORM_PRIORITY)运行

 

yield()方法--线程让步

告诉调度器,可以给别的线程使用cpu了,建议有相同优先级的其他线程先运行

但这只是给调度器的建议, 并不一定会被采纳

 

setDaemon()--设置当前线程为后台线程。 必须在线程启动之前调用才有效

后台线程并不属于程序中不可或缺的一部分, 因此, 当所有非后台线程结束时, 程序也就终止了, 同时杀死进程中的所有后台线程

 

cancel()终结任务。isCancled()检查线程的终结状态

 

thread.interrupt()中断线程 

thread.interrupted()检查线程中断状态

调用Executor.shutDownNow()时,executor将发送interrupt()调用给它启动的所有线程

interrupt方法可以中断任何要求抛出InterruptedException的调用(如sleep()),但不能中断正在试图获取锁或者执行io操作的线程。

所以对于io操作的线程, 我们可以关闭底层资源来释放锁。

 

乐观锁的概念:

乐观加锁, 就是说当我们执行某项计算时, 实际上没有使用互斥,但是当计算完成,准备更新资源时, 使用compareAndSet()方法,将旧值和新值一起传过去。 

如果旧值和该对象之前保存的值不一致,就意味着有其他线程在计算期间修改了这个对象, 那么这个操作就失败

 

所以乐观锁就是说,我们是乐观的,保持数据为未锁定状态,并希望我们修改期间没有其他线程来操作它。  这通常是为了性能的提升

 

线程的4个状态:

新建: 线程被创建时, 短暂地处于这种状态

就绪:这种状态下,只要cpu把时间片给线程, 线程就可以运行

阻塞: 线程能够运行,但是有某个条件阻止它运行, 线程处于阻塞状态时, 调度器将忽略线程,直到它重新进入就绪状态

         线程进入阻塞状态的原因可能有: 任务调用了sleep()或者wait(),  任务在等待某个输入/输出完成, 任务试图调用同步控制方法, 但对象锁不可用, 因为已经被另一个任务获取。

死亡:任务执行完run方法不可再运行

 

posted on 2021-09-26 17:05  倒霉的菜鸟  阅读(659)  评论(0编辑  收藏  举报