java 线程同步 lock_Java多线程与并发应用-(9)-锁lock+条件阻塞conditon实现线程同步通信...

一.lock可以代替synchronized关键字实现互斥功能。使用方法如下:

Lock l = ...;

l.lock();

try {

// access the resource protected by this lock

} finally {

l.unlock();

}

需要注意的是。

1.需要互斥的一个或多个方法要使用同一个互斥锁。

2.在被锁包含的代码块中,要使用finally块将锁释放。

二.Condition的await方法(注意不是wait方法)可以替换传统通信中的wait方法,对应的signal方法替换notify。

在传统通信的条件判断时,我们会用while而不是if做条件判断,是为了虚假唤醒。那么什么是虚假唤醒呢?

虚假唤醒即:如:我们要求AB方法按顺序执行,有5个线程执行A,5个线程执行B,如果某时刻全部A等待,当其中A1被唤醒,并执行完代码后,会调用notify方法,其本意是唤醒B模块执行线程,但是由于AB公用一个锁,所以可能将A唤醒,即唤醒了不该执行的代码,这就是虚假唤醒。

所以我们使用while条件,即使被唤醒了,我们还会做一次条件判读,这样被虚假唤醒的代码将再一次等待。

这就要求程序员来控制避免虚假唤醒带来的错误。而Lock和Condition的帮我们解决了这个问题。一个锁内部可以有多个Condition.那么同一个锁内可以多个condition实现模块

之间的切换,如上面的例子中A1再执行完之后,通过B对应的Condtion.signal只可以唤醒B对应的线程。我们可以看看Condition的API中的例子,阻塞队列的简单实现:

首先我们看看传统的通信技术实现简单的阻塞队列,有何弊端?

package com.lipeng;

import java.util.Random;

public class BoundedBuffer1 {

private Object[] objs=new Object[100];

private int length;

private int putIndex=0;//存指针

private int getIndex=0;//取指针

/**

* 存放元素,从头到尾,再反复从头到尾

* @param t

*/

public synchronized void put(T t)

{

//如果已经放满了,就等待。

while(length==objs.length)//如果N个线程在这儿等待, 其中一个线程被唤醒后,执行下面的代码,35行this.notify本意是唤醒取线程取数据,但其实可能唤醒存线程。

{

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

objs[putIndex]=t; //在队尾插入元素

length++; //长度加1,

putIndex++;

if(putIndex==objs.length)

{

//注意不是放满了才从起始处放,而是存放指针到队尾了再从头开始。

putIndex=0;

}

this.notify();

}

/**

* 取元素,从头到尾取,在如此反复。

* @return

*/

public synchronized T get()

{

while(length==0)

{

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

T t=(T) objs[getIndex];

length--;

getIndex++;

if(getIndex==objs.length)

{

getIndex=0;

}

this.notify();

return t;

}

public static void main(String[] args) {

final BoundedBuffer1 bb=new BoundedBuffer1();

Runnable getRun=new Runnable() {

@Override

public void run() {

for(int i=0;i<10;i++)

{

synchronized (bb) { //这里加synchronized只是为了让读取数据和打印数据保持完整性,做演示用用

Integer data=bb.get();

System.out.println(Thread.currentThread().getName()+" 读取元素------ "+data);

}

}

}

};

Runnable putRun=new Runnable() {

@Override

public void run() {

for(int i=0;i<10;i++)

{

synchronized (bb) {//这里加synchronized只是为了让存放数据和打印数据保持完整性,做演示用用

Integer data=new Random().nextInt(100);

bb.put(data);

System.out.println(Thread.currentThread().getName()+" 放入--------------------------- "+data);

}

}

}

};

System.out.println("***********************");

for(int i=0;i<10;i++)

{

new Thread(getRun).start();

new Thread(putRun).start();

}

}

}

我们再来看Condition是如何帮我们实现的?

package com.lipeng;

import java.util.Random;

import java.util.concurrent.locks.Condition;

import java.util.concurrent.locks.Lock;

import java.util.concurrent.locks.ReentrantLock;

public class BoundedBuffer2 {

private Object[] objs=new Object[100];

private int length;

private int putIndex=0;//存指针

private int getIndex=0;//取指针

private Lock lock=new ReentrantLock();

private Condition putCon=lock.newCondition();//存放条件

private Condition getCon=lock.newCondition();// 取条件

/**

* 存放元素,从头到尾,再反复从头到尾

* @param t

*/

public void put(T t)

{

try {

lock.lock();

//如果已经放满了,就等待。

while(length==objs.length)//如果N个线程在这儿等待, 其中一个线程被唤醒后,执行下面的代码,35行this.notify本意是唤醒取线程取数据,但其实可能唤醒存线程。

{

try {

putCon.await();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

objs[putIndex]=t; //在队尾插入元素

length++; //长度加1,

putIndex++;

if(putIndex==objs.length)

{

//注意不是放满了才从起始处放,而是存放指针到队尾了再从头开始。

putIndex=0;

}

getCon.signal();

System.out.println(Thread.currentThread().getName()+" 放入--------------------------- "+t);

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}finally{

lock.unlock();

}

}

/**

* 取元素,从头到尾取,在如此反复。

* @return

*/

public T get()

{

try {

lock.lock();

while(length==0)

{

try {

getCon.await();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

T t=(T) objs[getIndex];

length--;

getIndex++;

if(getIndex==objs.length)

{

getIndex=0;

}

putCon.signal();

System.out.println(Thread.currentThread().getName()+" 读取元素------ "+t);

return t;

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

return null;

}finally{

lock.unlock();

}

}

public static void main(String[] args) {

final BoundedBuffer2 bb=new BoundedBuffer2();

Runnable getRun=new Runnable() {

@Override

public void run() {

for(int i=0;i<10;i++)

{

bb.get();

}

}

};

Runnable putRun=new Runnable() {

@Override

public void run() {

for(int i=0;i<10;i++)

{

Integer data=new Random().nextInt(100);

bb.put(data);

}

}

};

System.out.println("***********************");

for(int i=0;i<10;i++)

{

new Thread(getRun).start();

new Thread(putRun).start();

}

}

}

注意事项:

Lock(包括读写锁)+Condition看起来更加面向对象,也似乎提高了性能*(因为我没验证过,哈哈)也更加严谨,但我认为如果传统的通信方法够用,没必要使用它。

原文:http://blog.csdn.net/lp1137917045/article/details/45437429

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值