- 调用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方法不可再运行