文档章节

Java学习日志(12-1-多线程中锁的等待与唤醒)

Almon
 Almon
发布于 2016/08/15 16:04
字数 1200
阅读 4
收藏 0

多线程间通信

Input------->变量库------->Output

    多个线程操作同一个资源,但是操作的动作不同

eg.同时输入输出会存在控制权转移导致输入一半就输出的问题

class Res{
	String name;
	String sex;
}
class Input implements Runnable{
	private Res r;
	Input(Res r){
		this.r=r;
	}
	public void run(){
		int x=0;
		while(true){
			if(x==0){
				r.name="Mike";
				r.sex="female";
			}
			else{
				r.name="Lily";
				r.sex="male";
			}
			x=(x+1)%2;
		}
	}
}
class Output implements Runnable{
	private Res r;
	Output(Res r){
		this.r=r;
	}
	public void run(){
		while(true){
			System.out.println(r.name+"___"+r.sex);
		}
	}
}
class InputOutputDemo{
	public static void main(String[] args){
		Res r=new Res();
		Input in=new Input(r);
		Output out=new Output(r);
		Thread t1=new Thread(in);
		Thread t2=new Thread(out);
		t1.start();
		t2.start();
	}
}

利用同步synchronized(唯一对象)解决控制权问题

class Res{
	String name;
	String sex;
}
class Input implements Runnable{
	private Res r;
	Input(Res r){
		this.r=r;
	}
	public void run(){
		int x=0;
		while(true){
			synchronized(r){    //共享数据
				if(x==0){
					r.name="Mike";
					r.sex="female";
				}
				else{
					r.name="Lily";
					r.sex="male";
				}
			}
			x=(x+1)%2;
		}
	}
}
class Output implements Runnable{
	private Res r;
	Output(Res r){
		this.r=r;
	}
	public void run(){
		while(true){
			synchronized(r){   //共享数据
				System.out.println(r.name+"___"+r.sex);
			}
		}
	}
}

将运行结果改为输入一条立即输出,依次循环——等待与唤醒

    wait()等待    notify()唤醒    notifyAll唤醒全部

    **因为是对持有锁的线程操作,因此只能用于同步之中,且必须标识锁——r.wait()

      定义在Object类中是因为只有同一个锁上的wait线程,可以被同一个锁notify

      锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中

class Res{
	String name;
	String sex;
	boolean flag=false;
}
class Input implements Runnable{
	private Res r;
	Input(Res r){
		this.r=r;
	}
	public void run(){
		int x=0;
		while(true){
			synchronized(r){
				if(r.flag){
					try{r.wait();}catch(Exception e){}
				}
				if(x==0){
					r.name="Mike";
					r.sex="female";
				}
				else{
					r.name="Lily";
					r.sex="male";
				}
				r.flag=true;
				r.notify();
			}
			x=(x+1)%2;
		}
	}
}
class Output implements Runnable{
	private Res r;
	Output(Res r){
		this.r=r;
	}
	public void run(){
		while(true){
			synchronized(r){
				if(!r.flag){
					try{r.wait();}catch(Exception e){}  //必须抛出异常,且必须标识锁
				}
				System.out.println(r.name+"___"+r.sex);
				r.flag=false;
				r.notify();
			}
		}
	}
}

对代码进行优化

class Res{
	private String name;			//权限私有化
	private String sex;
	private boolean flag=false;
	public synchronized void set(String name,String sex){	//提供对外接口,注意同步数据
		if(flag){
			try{this.wait();}catch(Exception e){}
			}
		this.name=name;
		this.sex=sex;
		flag=true;
		this.notify();
	}
	public synchronized void out(){
		if(!flag){
			try{this.wait();}catch(Exception e){}
		}
		System.out.println(name+"___"+sex);
		flag=false;
		this.notify();
	}
}
class Input implements Runnable{
	private Res r;
	Input(Res r){
		this.r=r;
	}
	public void run(){
		int x=0;
		while(true){
			if(x==0){
				r.set("Mike","Male");
			}
			else{
				r.set("Lily","Female");
			}
			x=(x+1)%2;
		}
	}
}
class Output implements Runnable{
	private Res r;
	Output(Res r){
		this.r=r;
	}
	public void run(){
		while(true){
			r.out();
		}
	}
}
class InputOutputDemo{
	public static void main(String[] args){
		Res r=new Res();
		new Thread(new Input(r)).start();
		new Thread(new Output(r)).start();
	}
}

练习:消费者与生产者通信

Ver 1.0

//存在问题:t1 t2唤醒后不再进行判断,可能生产出统一编号
class ProducerConsumerDemo{
	public static void main(String[] args){
		Resource r=new Resource();
		Producer p=new Producer(r);
		Consumer c=new Consumer(r);
		Thread t1=new Thread(p);
		Thread t2=new Thread(p);
		Thread t3=new Thread(c);
		Thread t4=new Thread(c);
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}
class Resource{
	private String name;
	private int count=1;
	private boolean flag=false;
	public synchronized void set(String name){
		if(flag){	//第一次判断flag为false,不执行wait(),第二次为true,执行wait().
			try{
				this.wait();
			}
			catch(Exception e){
				
			}
		}
		this.name=name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
		flag=true;
		this.notify();
	}
	public synchronized void out(){
		if(!flag){	//第一次执行到这里的时候flag=true,不执行wait().
			try{
				this.wait();
			}
			catch(Exception e){
				
			}
		}
		System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
		flag=false;
		this.notify();
	}
}
class Producer implements Runnable{
	private Resource res;
	Producer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			res.set("+goods+");
		}
	}
}
class Consumer implements Runnable{
	private Resource res;
	Consumer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			res.out();
		}
	}
}

Ver 2.0

存在多个消费者与生产者时

将if语句改为while,单次判断变为多次判断。同时改为notifyAll避免全部wait

class Resource{
	private String name;
	private int count=1;
	private boolean flag=false;
	public synchronized void set(String name){
		while(flag){	
			try{
				this.wait();
			}
			catch(Exception e){
				
			}
		}
		this.name=name+"--"+count++;
		System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
		flag=true;
		this.notifyAll();
	}
	public synchronized void out(){
		while(!flag){	
			try{
				this.wait();
			}
			catch(Exception e){
				
			}
		}
		System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
		flag=false;
		this.notifyAll();
	}
}

总结

    对于多个生产者和消费者,使用while进行判断,让被唤醒的线程再次判断flag

    只用notify容易出现只唤醒本方线程的情况,导致所有线程都wait。

    notifyAll能够唤醒对方线程。

Java 5.0以后将同步synchronized替换为Lock操作

    将wait    notify等操作替换为Condition对象

    该对象可以对Lock锁进行操作

import java.util.concurrent.locks.*;

class Resource{
	private String name;
	private int count=1;
	private boolean flag=false;
	private Lock lock=new ReentrantLock();
	private Condition con_pro=lock.newCondition();
	private Condition con_con=lock.newCondition();
	public void set(String name)throws InterruptedException{
		lock.lock();
		try{
			while(flag){
				con_pro.await(); //只唤醒对方
			}
			this.name=name+"--"+count++;
			System.out.println(Thread.currentThread().getName()+"...生产者..."+this.name);
			flag=true;
			con_con.signal();
		}
		finally{ //释放锁的动作一定要执行
			lock.unlock();
		}
	}
	public void out()throws InterruptedException{
		lock.lock();
		try{
			while(!flag){
				con_con.await();	
			}
			System.out.println(Thread.currentThread().getName()+"...消费者..."+this.name);
			flag=false;
			con_pro.signal();
		}
		finally{
			lock.unlock();
		}
	}
}
class Producer implements Runnable{
	private Resource res;
	Producer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			try{
				res.set("+goods+");
			}
			catch(InterruptedException e){
				
			}
		}
	}
}
class Consumer implements Runnable{
	private Resource res;
	Consumer(Resource res){
		this.res=res;
	}
	public void run(){
		while(true){
			try{
				res.out();
			}
			catch(InterruptedException e){
				
			}
		}
	}
}

 

© 著作权归作者所有

共有 人打赏支持
Almon
粉丝 2
博文 64
码字总数 44346
作品 0
江北
Java多线程学习(四)等待/通知(wait/notify)机制

系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Ja...

一只蜗牛呀
04/16
0
0
杂乱笔记-android多线程

首先看一下java下的多线程的几个关键方法 Object obj = new Object() ;while(!condition) {  obj.wait();  }  obj.doSomething();  } 在另一线程B中,如果B更改了某些条件,使得线程...

广阔
2012/06/24
0
0
java基础thread——java5之后的多线程(浅尝辄止)

承上启下 虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象L...

潇潇漓燃
06/03
0
0
多线程编程学习三(线程间通信).

一、概要 线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就是成为整体的必用方案之一。可以说,使线程进行通信后,系统之间的交互性会更强大...

jmcui
2017/09/12
0
0
Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

原文:https://blog.csdn.net/xionghan01/article/details/52840358 一、线程5种状态 新建状态(New) 新创建了一个线程对象。 就绪状态(Runnable) 线程对象创建后,其他线程调用了该对象的...

Tenderrain
06/22
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

jquery创建类似于java的map

var map = {}; // Map map = new HashMap(); map[key] = value; // map.put(key, value); var value = map[key]; // Object value = map.get(key); var has = key in map; // boolean has = ......

SuperDabai
39分钟前
0
0
java大数据转换16进制转10进制

public static void main(String[] args) {String hex = "0xdbf3accc683297cf0000";BigInteger amount = new BigInteger(hex.substring(2), 16);System.out.println(amount);......

任梁荣
昨天
2
0
OSChina 周六乱弹 —— 目测我们程序员丁克的几率不大

Osc乱弹歌单(2018)请戳(这里) 【今日歌曲】 @真Skr小机灵鬼儿:8.13分享Jocelyn Pook/Russian Red的单曲《Loving Strangers》 《Loving Strangers》- Jocelyn Pook/Russian Red 手机党少...

小小编辑
昨天
9
3
TypeScript基础入门 - 函数 - 剩余参数

转载 TypeScript基础入门 - 函数 - 剩余参数 项目实践仓库 https://github.com/durban89/typescript_demo.gittag: 1.2.1 为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能...

durban
昨天
1
0
OpenCV边缘检测算子原理总结及实现

1. 拉普拉斯算子 原理:是一种基于图像导数运算的高通线性滤波器。它通过二阶导数来度量图像函数的曲率。 拉普拉斯算子是最简单的各向同性微分算子,它具有旋转不变性。一个二维图像函数的拉...

漫步当下
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部