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

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. #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型。其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数)。

    #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型.其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数). def get_num(num): i ...

  2. Excel和Word 简易工具类,JEasyPoi 2.1.5 版本发布

    Excel和Word 简易工具类,JEasyPoi 2.1.5 版本发布 摘要: jeasypoi 功能如同名字easy,主打的功能就是容易,让一个没见接触过poi的人员 就可以方便的写出Excel导 ...

  3. CSS3 3D transform变换

    .实际应用-图片的旋转木马效果 您可以狠狠地点击这里:图片的旋转木马效果demo 建议在足够新版本的FireFox浏览器或Safari浏览器下观看,Chrome可能需要居中定位查看,下图为效果缩略图: ...

  4. Python系列之 - python运算符

    废话不多说,上节说的是数据类型,本篇讲讲数据运算. 在算式"1+2"中,"1"和"2"被称为操作数,"+"被称为运算符 ...

  5. 重载运算符“ <<” 和“>>” 运算符

    :" <<  "   "  >>  " 的重载作为友元函数重载,有两种方法:1,把变量作为public,就可以不用友元声明:2,先友元声 ...

  6. [SHOI 2011]双倍回文

    Description 题库链接 记一个字符串为 \(X\) ,它的倒置为 \(X^R\) .现在给你一个长度为 \(n\) 的字符串 \(S\) ,询问其最长的形同 \(XX^RXX^R\) 的子串 ...

  7. [AH/HNOI2017]抛硬币

    题目描述 小 A 和小 B 是一对好朋友,他们经常一起愉快的玩耍.最近小 B 沉迷于**师手游,天天刷本,根本无心搞学习.但是已经入坑了几个月,却一次都没有抽到 SSR,让他非常怀疑人生.勤勉的小 A ...

  8. bzoj 1272: [BeiJingWc2008]Gate Of Babylon

    Description Solution 如果没有限制,答案就是 \(\sum_{i=0}^{m}C(n+i-1,i)\) 表示枚举每一次取的个数,且不超过 \(m\),方案数为可重组合 发现这个东西 ...

  9. 【Ural1277】 Cops and Thieves 无向图点连通度问题

    1277. Cops and Thieves Time limit: 1.0 secondMemory limit: 64 MB The Galaxy Police (Galaxpol) found ...

  10. hdu 5430(几何)

    题意:求光在圆内反射n次后第一次返回原点的方案数 如果k和n-1可约分,则表明是循环多次反射方案才返回原点. #include <iostream> #include <cstrin ...