我们先看一段代码:

/**
* 计算输出其他线程锁计算的数据
*
*/
public class ThreadA {
public static void main(String[] args) throws InterruptedException{
ThreadB b = new ThreadB();
//启动计算线程
b.start();
//线程A拥有b对象上的锁。线程为了调用wait()或notify()方法,该线程必须是那个对象锁的拥有者
synchronized (b) {
System.out.println("等待对象b完成计算。。。");
//当前线程A等待
b.wait();
System.out.println("b对象计算的总和是:" + b.total);
}
}
} /**
* 计算1+2+3 ... +100的和
*
*/
class ThreadB extends Thread {
int total; public void run() {
synchronized (this) {
for (int i = 0; i < 101; i++) {
total += i;
}
//(完成计算了)唤醒在此对象监视器上等待的单个线程,在本例中线程A被唤醒
notify();
System.out.println("计算完成");
}
}
}

执行结果:

等待对象b完成计算。。。
计算完成
b对象计算的总和是:5050

如果我们将b.wait()去掉呢?结果如下:

等待对象b完成计算。。。
b对象计算的总和是:0
计算完成

上述的结果表明,当去掉b.wait()时,新启动的线程ThreadB与主线程ThreadA是各自执行的,没有线程等待的现象。

我们想要的效果是,当线程ThreadB完成计算之后,再去取计算后的结果。所以使用了b.wait()来让主线程等待。

那为什么是使用b.wait(),而不是Thread.currentThread.wait(),或者其他的呢?

如果我们将b.wait()替换成Thread.currentThread.wait(),将会得到如下的结果:

Exception in thread "main" java.lang.IllegalMonitorStateException
at java.lang.Object.wait(Native Method)
at java.lang.Object.wait(Object.java:485)
at pa.com.thread.ThreadA.main(ThreadA.java:18)
等待对象b完成计算。。。
计算完成

替换的代码Thread.currentThread.wait()好像理所当然应该如我们预期的正确啊,让当前线程处于等待状态,让其他线程先执行。

我们忽略了一个很重要的问题:线程与锁是分不开的,线程的同步、等待、唤醒都与对象锁是密不可分的。

线程ThreadA持有对象b的锁,我们要使用这把锁去让线程释放锁,从而让其他的线程能抢到这把锁。

从我们的程序来分析就是:线程ThreadA首先持有锁对象b,然后调用b.wait()将对象锁释放,线程ThreadB争抢到对象锁b,从而执行run()方法中的计算,计算完了之后使用notify()唤醒主线程ThreadA,ThreadA得以继续执行,从而得到了我们预期的效果。

(之所以ThreadB的对象锁也是b,是因为synchronized(this)中的this指向的就是ThreadB的实例b)

Thread.currentThread.wait()调用的是当前线程对象(即主线程ThreadA)的wait()方法,当前线程对象ThreadA是没有被加锁的,它只是获取了对象锁b。我基本没有看到过这样的调用,一般使用的是锁对象的wait(),本例中为b.wait()

顺带讲一下wait()与sleep()的区别。

如果我们将b.wait()换成Thread.sleep(1000),则会出现如下的结果:

等待对象b完成计算。。。
b对象计算的总和是:0
计算完成

从执行结果可以看出,Thread.sleep(1000)只是让主线程ThreadA睡眠了1秒钟,而并没有释放对象锁,所以在主线程ThreadA睡眠的过程中,ThreadB拿不到对象锁,从而不能执行。

所以我们也就得出了如下的结论:

wait()方法是让线程释放对象锁,让其他线程拿到锁之后去优先执行,当其他全程唤醒wait()中的线程 或者 拿到对象锁的线程都执行完释放了对象锁之后,wait()中的线程才会再次拿到对象锁从而执行。

sleep()方法是让线程睡眠,此时并没有释放对象锁,其他想要拿到睡眠线程的对象锁的线程也就拿不到相应的对象锁,从而不能抢在它前面执行。

补:

wait、notify和notifyAll方法是Object类的final native方法。所以这些方法不能被子类重写,Object类是所有类的超类,因此在程序中有以下三种形式调用wait等方法。

wait();//方式1:
this.wait();//方式2:
super.wait();//方式3

void wait()

导致线程进入等待状态,直到它被其他线程通过notify()或者notifyAll唤醒。该方法只能在同步方法中调用。如果当前线程不是锁的持有者,该方法抛出一个IllegalMonitorStateException异常。

线程中的wait() 与 锁的关系的更多相关文章

  1. C++线程中的几种锁

    线程之间的锁有:互斥锁.条件锁.自旋锁.读写锁.递归锁.一般而言,锁的功能越强大,性能就会越低. 1.互斥锁 互斥锁用于控制多个线程对他们之间共享资源互斥访问的一个信号量.也就是说是为了避免多个线程在 ...

  2. [多线程] 线程中的synchronized关键字锁

    为什么要用锁? 在多线程中,难免会出现在多个线程中对同一个对象的实例变量或者全局静态变量进行并发访问的情况,如果不做正确的同步处理,那么产生的后果就是"脏读",也就是取到的数据其实 ...

  3. iOS: 线程中那些常见的锁

    一.介绍 在多线程开发中,锁的使用基本必不可少,主要是为了解决资源共享时出现争夺而导致数据不一致的问题,也就是线程安全问题.锁的种类很多,在实际开发中,需要根据情况选择性的选取使用,毕竟使用锁也是消耗 ...

  4. {Python之线程} 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Threading模块 九 锁 十 信号量 十一 事件Event 十二 条件Condition(了解) 十三 定时器

    Python之线程 线程 本节目录 一 背景知识 二 线程与进程的关系 三 线程的特点 四 线程的实际应用场景 五 内存中的线程 六 用户级线程和内核级线程(了解) 七 python与线程 八 Thr ...

  5. 操作系统/应用程序、操作中的“并发”、线程和进程,python中线程和进程(GIL锁),python线程编写+锁

    并发编程前言: 1.网络应用 1)爬虫 直接应用并发编程: 2)网络框架 django flask tornado 源码-并发编程 3)socketserver 源码-并发编程 2.运维领域 1)自动 ...

  6. Java核心知识点学习----线程中如何创建锁和使用锁 Lock,设计一个缓存系统

    理论知识很枯燥,但这些都是基本功,学完可能会忘,但等用的时候,会发觉之前的学习是非常有意义的,学习线程就是这样子的. 1.如何创建锁? Lock lock = new ReentrantLock(); ...

  7. JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制

    JAVA之旅(十四)--静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制 JAVA之旅,一路有你,加油! 一.静态同步函数的锁是clas ...

  8. Python并发编程之谈谈线程中的“锁机制”(三)

    大家好,并发编程 进入第三篇. 今天我们来讲讲,线程里的锁机制. 本文目录 何为Lock( 锁 )?如何使用Lock( 锁 )?为何要使用锁?可重入锁(RLock)防止死锁的加锁机制饱受争议的GIL( ...

  9. Erlang运行时中的无锁队列及其在异步线程中的应用

    本文首先介绍 Erlang 运行时中需要使用无锁队列的场合,然后介绍无锁队列的基本原理及会遇到的问题,接下来介绍 Erlang 运行时中如何通过“线程进度”机制解决无锁队列的问题,并介绍 Erlang ...

随机推荐

  1. linux包之procps之sysctl命令

    概述 [root@localhost ~]# rpm -qf /sbin/sysctlprocps-3.2.8-25.el6.x86_64 我们常常在 Linux 的 /proc/sys 目录下,手动 ...

  2. Linux复制指定目录及子目录下特定类型的文件

    首先建立一个用于测试的目录,用'tree'命令查看其结构如下所示: 可见,目录中主要包含用于测试的*.txt文件和用于充当炮灰的*.tes文件 目标是保持当前的目录结构,只把txt文件复制出来 方法一 ...

  3. JQuery Pagenation 知识点整理——$.extend(),与$.fn.extend()应用(20150517)

    jQuery为开发插件提拱了两个方法,分别是: jQuery.fn.extend(); jQuery.extend(); 一. $.extend()方法 $.extend()方法在JQuery中有两个 ...

  4. AngularJS初始化普通数组和对象数组

    普通数组: ng-init="persons=['john','jack']" 对象数组: ng-init="persons=[{name:'john',age:20}, ...

  5. CentOs 设置静态IP 方法 dhcp的配置

    0.自动获取配置 DEVICE=eth0 HWADDR=00:0C:39:AD:11:48 TYPE=Ethernet UUID=c230a1e5-a535-487a-aab5-2fad7cd5583 ...

  6. Android Bitmap和Canvas学习笔记 [转]

    原文:http://www.cnblogs.com/feisky/archive/2010/01/10/1643460.html 位图是我们开发中最常用的资源,毕竟一个漂亮的界面对用户是最有吸引力的. ...

  7. Python(迭代器 生成器 装饰器 递归 斐波那契数列)

    1.迭代器 迭代器是访问集合元素的一种方式.迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束.迭代器只能往前不会后退,不过这也没什么,因为人们很少在迭代途中往后退.另外,迭代器的一大优 ...

  8. IsolatedStorageException for Silverlight Application

    if you are using the IsolatedStorage for application configuration storage, and when you get the Iso ...

  9. LintCode "Max Tree"

    Something new I learnt from it: what is Treap and a O(n) construction https://en.wikipedia.org/wiki/ ...

  10. 51nod 1211 数独

    数独游戏规则如下:在9 * 9的盘面上有些已知的数字及未知的数字,推理出所有未知的数字,并满足每一行.每一列.每一个粗线宫内的数字均含1-9,不重复.       有些局面存在多个解或无解,这属于不标 ...