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

最新文章

  1. nginx自动检测后台服务器健康状态
  2. c语言迷宫游戏的实现
  3. Hive安装
  4. C#复习④
  5. STL库函数 持续更新
  6. 使用main方法调用http请求本地服务器的某个servlet报错问题
  7. Servlet容器如何同时来处理多个请求
  8. PostgreSQL在Ubuntu上安装指南
  9. 使用Dom4j生成xml文件
  10. spring中涉及事务(bean中ref与local)
  11. the introduction of scrapy1.1 tools
  12. Linux学习笔记6-Linux根目录下各个目录的作用
  13. 使用ExecutorCompletionService 管理线程池处理任务的返回结果
  14. text和submit框的border问题
  15. jqgrid 单元格引入时间datepicker控件
  16. 基本数据类型 Symbol
  17. BI基础知识
  18. HDU - 1241 Oil Deposits 经典dfs 格子
  19. 定时任务 spring @Scheduled注解
  20. 【Windows】Dos中的日期的和时间

热门文章

  1. 分数化循环小数C++实现
  2. EternalBlue永恒之蓝渗透测试
  3. uniapp 设置背景图片
  4. vue 导出excel后端返回乱码下载不了的解析问题
  5. xxs攻击
  6. Python - pip 批量更新
  7. 专项测试-App性能分析
  8. C语言实现任务调度与定时器
  9. 将两个byte型拼接成16位二进制,再转化为十进制
  10. js 显示日期时间,时间过一秒加1