在并发编程中,有这样的需求:当满足某个条件时线程执行同步块中的代码,条件不满足时,让线程在此等待,直至条件满足再执行同步代码块。

java的Object类即提供了一类这样的方法wait(),notifyAll()/notify(),调用wait()方法后,线程A释放对同步代码块的控制并进入休眠状态,

在条件再次满足时,调用notifyAll()/notify()方法唤醒线程A,线程A将被唤醒并重新试图获得同步代码块的控制,在进入同步代码块成功之后,

再次对条件判断。

典型的应用场景是生产者-消费者模式,有一个固定大小的缓冲区存放消息,一个或者多个生产者线程把消息写入缓冲区;一个或者多个消费者从缓冲区获取消息。

如果缓冲区满了,生产者就不能写入消息,并等待。如果缓冲区为空,消费者就不能获取消息,并等待。

我们使用synchronized关键字来同步代码块,由于java中的类都继承自Object类,因此可以在我们的类中调用wait()让线程进入休眠并等待唤醒。

首先创建一个类MessageStorage来管理消息缓冲区,并使用LinkedList队列来作为消息缓冲区:

public class MessageStorage {
private int maxSize;
private List<String> messages; public MessageStorage(int maxSize) {
this.maxSize = maxSize;
messages = new LinkedList<String>();
}
public void set(String message){
synchronized (this){
while(messages.size() == maxSize){
try {
System.out.print("the message buffer is full now,startinto wait()\n");
wait();//满足条件时,线程休眠并释放锁。当调用notifyAll()时。线程唤醒并重新获得锁
}catch (InterruptedException e){
e.printStackTrace();
}
}
try{
Thread.sleep(100);
}catch (InterruptedExceptione){
e.printStackTrace();
}
messages.add(message);
System.out.print("add message:"+message+" success\n");
notifyAll();//唤醒休眠的线程
}
}
public String get(){
String message = null;
synchronized (this){
while(messages.size() == 0){
try {
System.out.print("the message buffer is empty now,startinto wait()\n");
wait();
}catch (InterruptedExceptione){
e.printStackTrace();
}
}
try{
Thread.sleep(100);
}catch (InterruptedExceptione){
e.printStackTrace();
}
message =((LinkedList<String>)messages).poll();
System.out.print("get message:"+message+" success\n");
notifyAll();
}
return message;
}
}

实现一个生产者,向消息缓冲区写入消息:

public class Producer implements Runnable{
private MessageStorage messageStorage;
private int index;
public Producer(MessageStorage messageStorage,int index) {
this.messageStorage = messageStorage;
this.index = index;
} public void run(){
for(int i=0; i<5; i++){
StringBuffer message = new StringBuffer("thread id:");
message.append(index);
message.append(" id:");
message.append(i);
messageStorage.set(message.toString());
}
}
}

实现一个消费者,从消息缓冲区取数据:

public class Consumer implements Runnable{
private MessageStorage messageStorage;
public Consumer(MessageStorage messageStorage) {
this.messageStorage = messageStorage;
} public void run(){
for(int i=0; i<5; i++){
messageStorage.get();
}
}
}

测试代码:

public class ThreadMain {
public static void main(String[] args){
MessageStorage messageStorage = new MessageStorage(10);
Thread[] threads = new Thread[10];
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Producer(messageStorage,i));//创建多个生产者
threads[i] = thread;
}
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(new Consumer(messageStorage));//创建多个消费者
threads[i+5] = thread;
}
for(int i = 0; i < 10; i++){
Thread thread = threads[i];
try {
thread.start();//启动线程
}catch (Exception e){
e.printStackTrace();
}
}
}
}

总结:

使用wait()和notifyAll()/notify()方法,便于我们在程序中主动的控制的线程的休眠和唤醒 ,实现更为复杂的逻辑控制要求。另外,我们也可以使用Lock锁来进行代码控制,使用锁的条件Condition的await()和signal()/和signalAll()控制线程的休眠、唤醒,来实现上面的生产者-消费者模型。

synchronized与条件同步的更多相关文章

  1. Java中的Lock锁

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  2. 线程高级篇-Lock锁实现生产者-消费者模型

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  3. java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解

    synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...

  4. 单例模式中用volatile和synchronized来满足双重检查锁机制

    背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...

  5. Thread 学习记录 <1> -- volatile和synchronized

    恐怕比较一下volatile和synchronized的不同是最容易解释清楚的.volatile是变量修饰符,而synchronized则作用于一段代码或方法:看如下三句get代码: int i1;  ...

  6. synchronized使用说明

    好久没有更新博客了,今天试着用简单的语言把synchronized的使用说清楚. synchronized是什么? synchronized是用来保证在多线程环境下代码同步执行的可重入的互斥锁.所谓互 ...

  7. 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition

    img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...

  8. (转)Lock和synchronized比较详解

    今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问题没有谈到,但这篇文章相当不 ...

  9. Synchronized同步性与可见性

    Synchronized是具有同步性与可见性的,那么什么是同步性与可见性呢? (1)同步性:同步性就是一个事物要么一起成功,要么一起失败,可谓是有福同享有难同当,就像A有10000去银行转5000给身 ...

随机推荐

  1. jacascript DOM节点——元素节点、属性节点、文本节点

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! DOM节点的三个种类:元素节点.文本节点.属性节点: 元素节点 元素节点就是 HTML 标签元素,元素节点 ...

  2. django 开发忘记密码通过邮箱找回功能

    一.流程分析: 1.点击忘记密码====>forget.html页面,输入邮箱和验证码,发送验证链接网址的邮件====>发送成功,跳到send_success.html提示 2.到邮箱里找 ...

  3. 涨薪必备Javascript,快点放进小口袋!

    摘要: 嗨,新一年的招聘季,你找到更好的工作了吗?小姐姐最近刚换的工作,来总结下面试必备小技能,从this来看看javascript,让我们更深入的了解它. 前言 在JavaScript中,被吐槽最多 ...

  4. MyBatis基础学习笔记--自总结

    一.MyBatis和jdbc的区别 jdbc的过程包括: 1.加载数据库驱动. 2.建立数据库连接. 3.编写sql语句. 4.获取Statement:(Statement.PrepareStatem ...

  5. [LeetCode] Count Different Palindromic Subsequences 计数不同的回文子序列的个数

    Given a string S, find the number of different non-empty palindromic subsequences in S, and return t ...

  6. 【Swfit】Swift与OC两种语法写单例的区别

    Swift与OC两种语法写单例的区别 例如写一个NetworkTools的单例 (1)OC写单例 + (instancetype)sharedNetworkTools { static id inst ...

  7. spring data学习

    在Spring Data模块中定义依赖: <dependencies> <dependency> <groupId>org.springframework.data ...

  8. [USACO15OPEN]回文的路径Palindromic Paths 2.0版

    题目描述 农夫FJ的农场是一个N*N的正方形矩阵(2\le N\le 5002≤N≤500),每一块用一个字母作标记.比如说: ABCD BXZX CDXB WCBA 某一天,FJ从农场的左上角走到右 ...

  9. 【bzoj4009 hnoi2015】接水果

    题目描述 风见幽香非常喜欢玩一个叫做 osu!的游戏,其中她最喜欢玩的模式就是接水果.由于她已经DT FC 了The big black, 她觉得这个游戏太简单了,于是发明了一个更加难的版本. 首先有 ...

  10. ●BZOJ 1185 [HNOI2007]最小矩形覆盖

    题链: http://www.lydsy.com/JudgeOnline/problem.php?id=1185 题解: 计算几何,凸包,旋转卡壳 结论:矩形的某一条边在凸包的一条边所在的直线上. ( ...