文档章节

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
多线程通信的三大法器,你真的会用吗?

wait, notify, notifyAll 是多线程之间通信最重要的 3 个方法,今天,栈长给大家普及一下它们的知识要点及应用实战。 定义 wait:让持有该对象锁的线程等待; notify: 唤醒任何一个持有该对象...

Java技术栈
11/27
0
0
Java并发编程之线程安全、线程通信

Java多线程开发中最重要的一点就是线程安全的实现了。所谓Java线程安全,可以简单理解为当多个线程访问同一个共享资源时产生的数据不一致问题。为此,Java提供了一系列方法来解决线程安全问题...

leoliu168
11/07
0
0
java中高级大公司多线程面试题

1)在Java中Lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它? lock接口在多线程和并发编...

java成功之路
10/30
0
0
多线程编程学习三(线程间通信).

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

jmcui
2017/09/12
0
0

没有更多内容

加载失败,请刷新页面

加载更多

自定义 Maven 的 repositories

有时,应用中需要一些比较新的依赖,而这些依赖并没有正式发布,还是处于milestone或者是snapshot阶段,并不能从中央仓库或者镜像站上下载到。此时,就需要 自定义Maven的<repositories>。 ...

waylau
4分钟前
0
0
徒手写一个es6代码库

mkdir democd demonpm initnpm install -g babelnpm install -g babel-cli 在项目目录创建两个文件夹 functional-playground 和lib mkdir functional-playgroundmkdir lib...

lilugirl
5分钟前
0
0
linux定位应用问题的一些常用命令,特别针对内存和线程分析的dump命令

1.jps找出进程号,找到对应的进程号后面才好继续操作 2.linux查看进程详细信息 ps -ef | grep 进程ID 3. dump内存信息 Jmap -dump:format=b,file=YYMMddhhmm.dump pid 4.top查看cpu占用信息 ...

noob_chr
5分钟前
0
0
Android TV开发-按键焦点

写在前面 按键焦点过程了解 2.1 dispatchKeyEvent 过程了解 2.2 焦点查找请求过程了解 1.2.1 第一次获取焦点 1.2.3 按键焦点 焦点控制 焦点记忆 应用场景 参考资料 [TOC] 1. 写在前面 工...

冰雪情缘l
5分钟前
0
0
java框架学习日志-3

这章主要是补充一些ioc创建对象的方式,ioc容器在写好<bean></bean>的时候就已经创建对象了。在之前的例子中,一直都是无参的构造方法。下面给出有参的构造方法的对象的创建,没有什么难点重...

白话
7分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部