前言

  在Java并发编程实战,会经常遇到多个线程访问同一个资源的情况,这个时候就需要维护数据的一致性,否则会出现各种数据错误,其中一种同步方式就是利用Synchronized关键字执行锁机制,锁机制是先给共享资源上锁,只有拿到锁的线程才可以访问共享资源,其他线程进入等待状态。下面将以实例代码讲解一下

一、wait()、nofity()、nofityAll()讲解

  示例代码

package thread;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit; /**
* Created by StoneGeek on 2018/5/19.
* 博客地址:http://www.cnblogs.com/sxkgeek
* 当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。
* 当线程执行notify()/notifyAll()方法时,会唤醒一个处于等待状态该对象锁的线程,然后继续往下执行,直到执行完退出对象锁锁住的区域(synchronized修饰的代码块)后再释放锁
* 个人认为synachronized(){}执行完后会释放锁
*/
public class WaitNotify {
static boolean flag = true;
static Object lock = new Object(); public static void main(String[] args) throws Exception {
Thread waitThread = new Thread(new Wait(), "WaitThread");
waitThread.start();
TimeUnit.SECONDS.sleep();
Thread notifyThread = new Thread(new Notify(), "NotifyThread");
notifyThread.start();
} static class Wait implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 当条件不满足时,继续wait,同时释放了lock的锁
while (flag) {
System.out.println(Thread.currentThread().getName()
+ " flag is true. wait@ "
+ new SimpleDateFormat("HH:mm:ss")
.format(new Date()));
try {
lock.wait();
System.out.println("此处继续执行"+Thread.currentThread().getName());
// flag=true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 条件满足时,完成工作
System.out.println(Thread.currentThread().getName()
+ " flag is false. running@ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
} // wait()会立刻释放synchronized(obj)中的obj锁,以便其他线程可以执行obj.notify()
// 但是notify()不会立刻立刻释放sycronized(obj)中的obj锁,必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁.
// yield(),sleep()不会释放锁
static class Notify implements Runnable {
public void run() {
// 加锁,拥有lock的Monitor
synchronized (lock) {
// 获取lock的锁,然后进行通知,通知时不会释放lock的锁,
// 直到当前线程释放了lock后,WaitThread才能从wait方法中返回
System.out.println(Thread.currentThread().getName()
+ " hold lock. notify @ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
lock.notifyAll();
flag = false;
}
// 再次加锁
synchronized (lock) {
System.out.println(Thread.currentThread().getName()
+ " hold lock again. sleep@ "
+ new SimpleDateFormat("HH:mm:ss").format(new Date()));
}
synchronized (lock){
System.out.println(Thread.currentThread().getName()+"执行结束");
}
}
}
}

  执行结果如下

1  WaitThread flag is true. wait@ ::
2 NotifyThread hold lock. notify @ ::
3 NotifyThread hold lock again. sleep@ ::
4 NotifyThread执行结束
5 此处继续执行WaitThread
6 WaitThread flag is false. running@ ::
7 WaitThread执行结束

  解释:

  首先创建一个lock对象,然后给这个lock上锁来对多个进程同步,flag是一个标志,用来跳出while循环。

  当线程执行wait()时,会把当前的锁释放,然后让出CPU,进入等待状态。

  此时轮到notifythread线程,并且执行notifyAll(),这个意思是能够唤醒所有正在等待这个lock对象的monitor的线程,但是

  必须要等notify()所在线程执行完synchronized(obj)块中的所有代码才会释放这把锁,

  此时接着waitthread被唤醒,继续执行while循环,执行完之后,由于flag在notifythread中置为false,所以跳出while循环(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,此时会一直陷入wait,大家可以试试),

  执行console打印5 6 7

  notify()与notifyAll()的区别 

  notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象

  的monitor的话,则只能唤醒其中一个线程

  而调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程

  当时的疑惑

  (1)既然notify或者notifyAll需要执行完synchronized块中的内容,那么他还有什么存在的价值的

    后来执行完之后,才发现要是没有这个方法,那么synchronized块执行完之后,waitthread还是在等待状态,无法被唤醒。

  (2)wait被notify唤醒之后,是接着执行,所以console打印5,并不是从头执行(如果在实例代码的wail()后加flag=true结果是截然不同,由于notirythread进程执行完,waitthread进程重新执行wait方法,此时会一直陷入wait,无其他进程唤醒此进程)

二、wait()/wait(long)和sleep(long)方法的区别

  将示例代码中的lock.wait()改为Thread.sleep(1000),console打印

WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread
WaitThread flag is true. wait@ ::
此处继续执行WaitThread

  由此说明sleep并没有释放锁。

  区别:

    1、Sleep(long)是Thread的方法,而wait()/wait(long)是Object的方法

    2、Sleep(long)可以放在sychnoized块内也可以不放在里面,但是wait()/wait(long)必须放在语句块内

    3、Sleep(long)不释放锁,只是让当前线程暂停一段时间,而wait()/wait(long)是释放锁

    4、wait()将当前线程放到阻塞队列,只有调用notify()/notifyAll()方法后,才将其从阻塞队列中移动到就绪队列,等待被CPU调度,而wait(long)方法执行后就是放到阻塞队列,等待时间到期或者被wait()/wait(long)唤醒后就可以放到就绪队列被CPU调度

  目前还有一个疑惑,就是线程中的run方法有两个同样的synchroized(lock),是不是跟一个synchroized(lock)效果是一样的,目前就运行结果来看是这样子的!

synchronized块中的wait()、nofity()、nofityAll()方法的更多相关文章

  1. 九、dbms_ddl(提供了在PL/SQL块中执行DDL语句的方法)

    1.概述 作用:提供了在PL/SQL块中执行DDL语句的方法,并且也提供了一些DDL的特殊管理方法. 2.包的组成 1).alter_compile说明:用于重新编译过程.函数和包语法:dbms_dd ...

  2. 调用notify()后,当前线程执行完synchronized块中的所有代码才会释放锁

    package com.pinnet.test; public class Demo { public static void main(String[] args) { Demo demo = ne ...

  3. java 为什么wait(),notify(),notifyAll()必须在同步(Synchronized)方法/代码块中调用?

    wait()作用:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止.条件:在调用wait()之前,线程必须要获得该对象的对象级别锁,即只能在同步方法或同步块中调用wait()方法.进入wai ...

  4. Java多线程初学者指南(11):使用Synchronized块同步方法

    synchronized关键字有两种用法.第一种就是在<使用Synchronized关键字同步类方法>一文中所介绍的直接用在方法的定义中.另外一种就是synchronized块.我们不仅可 ...

  5. 使用Synchronized块同步方法

    synchronized关键字有两种用法.第一种就是在<使用Synchronized关键字同步类方法>一文中所介绍的直接用在方法的定义中.另外一种就是synchronized块.我们不仅可 ...

  6. 为什么WAIT必须在同步块中

    我们知道java的Object有wait和notify方法,如果要使用wait和notify的话,那么必须在synchronized块中,否则会抛出IllegalMonitorStateExcepti ...

  7. -1-5 java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁 sleep()和wait()方法的区别 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

     本文关键词: java 多线程 概念 进程 线程区别联系 java创建线程方式 线程组 线程池概念 线程安全 同步 同步代码块 Lock锁  sleep()和wait()方法的区别 为什么wait( ...

  8. Java中wait()方法为什么要放在同步块中?(lost wake-up 问题)

    问题起源 事情得从一个多线程编程里面臭名昭著的问题"Lost wake-up problem"说起. 这个问题并不是说只在Java语言中会出现,而是会在所有的多线程环境下出现. 假 ...

  9. 阿里面试题,为什么wait()方法要放在同步块中?

    某天我在***的时候,突然有个小伙伴微信上说:“哥,阿里面试又又挂了,被问到为什么wait()方法要放在同步块中,没答出来!” 我顿时觉得**一紧,仔细回顾一下,如果wait()方法不在同步块中,代码 ...

随机推荐

  1. Junit测试Controller(MockMVC使用),以及传输@RequestBody数据解决办法

    转自:http://www.importnew.com/21153.html 一.单元测试的目的 简单来说就是在我们增加或者改动一些代码以后对所有逻辑的一个检测,尤其是在我们后期修改后(不论是增加新功 ...

  2. Spring系列__04AOP

    AOP简介 今天来介绍一下AOP.AOP,中文常被翻译为"面向切面编程",其作为OOP的扩展,其思想除了在Spring中得到了应用,也是不错的设计方法.通常情况下,一个软件系统,除 ...

  3. Winform中使用zxing实现二维码生成(附dll下载)

    场景 zxing.dll下载 https://download.csdn.net/download/badao_liumang_qizhi/11623214 效果 实现 新建Winform程序,将上面 ...

  4. maven的使用解说

    maven周期及项目中的应用: 周期如下: 1.default生命周期,部署项目(jar包的依赖管理) 2.clear生命周期,项目清理工作 3.site生命周期,处理项目中产生的文档信息 应用: 1 ...

  5. 疑难杂症----Windows10

    现在大多数个人电脑所用的操作系统都是win10,而我们使用win10时总是会碰上各种各样的问题,所以专门写一篇博客来记录我碰上的各种问题,便于以后更快的解决问题. 一.小娜搜索不到应用问题解决方案 小 ...

  6. DirectX12 3D 游戏开发与实战第三章内容

    变换 学习目标 理解如何使用矩阵表示线性变换和仿射变换 学习对几何体进行缩放.旋转和平移的坐标变换 根据矩阵之间的乘法运算性质,将多个变换矩阵合并为一个单独的净变换矩阵 找寻不同坐标系之间的坐标转换方 ...

  7. Java并发synchronized详解

    今天和大家一起学习下并发编程,先举一个简单的生活例子,我们去医院或者银行排队叫号,那每个工作人员之间如何保证不会叫重号呢? public class TicketDemo extends Thread ...

  8. Spring Cloud 负载均衡初体验

    目录 服务搭建 1.注册中心--Eureka Server 2.服务提供方--Service Provider 3.服务消费方--Service Consumer 服务消费 Feign 与断路器 Hy ...

  9. ssh免密码登陆(集群多台机器之间免密码登陆)

    1. 首先在配置hosts文件(每台机器都要) 进入root权限 vi /etc/hosts 添加每台机器的ip + 主机名,例如: 172.18.23.201 hadoop1 172.18.23.1 ...

  10. Android手机QQ文件夹解析

    注:切勿修改手机QQ文件夹,以免造成不必要的使用问题及无法修复的数据丢失] 安卓手机QQ tencent文件夹解析 QQ下载的聊天背景:tencent→MobileQQ→system_backgrou ...