JAVA从菜鸟到进阶--面向对象基础(六)——多线程基础四 JDK1.5.0上新特性Lock,condition使用
在以前的博文中,已经讲述了如何解决多消费者多生产者的问题,我们分析问题的关键在于生产者和消费者在唤醒过程中调用的是己方的锁导致这样那样的问题产生。
基于这个问题,java在JDK1.5.0以上版本进行优化改进。新增加了一个接口Lock和一个新的接口condition。我们来看如何用这两个东西去解决多线程问题。
API中描述:
void lock()
获得锁。
void lockInterruptibly()
获取锁定,除非当前线程是 interrupted 。
Condition newCondition()
返回一个新Condition绑定到该实例Lock实例。
boolean tryLock()
只有在调用时才可以获得锁。
boolean tryLock(long time, TimeUnit unit)
如果在给定的等待时间内是空闲的,并且当前的线程尚未得到 interrupted,则获取该锁。
void unlock()
释放锁。
public interface Lock
实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition 。 其中有实现Lock的方法
public class ReentrantLock extends Object implements Lock, Serializable
一个可重入互斥Lock具有与使用synchronized方法和语句访问的隐式监视锁相同的基本行为和语义,但具有扩展功能。
构造方法
Constructor and Description
ReentrantLock()
创建一个 ReentrantLock的实例。
public interface Condition
Condition因素出Object监视器方法( wait , notify和notifyAll )成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock个实现。 Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。 一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。在synchronize中只有一个监视器,监视器的方法对所有线程都适用。
而在Lock中可以存在多个监视器,锁可以被多个监视器所监视。
对之前的代码进行改动。在锁中有con1,con2,conn监视器,每个监视器可以监视一个线程。可以实现生产者消费者进行不同与己方线程的调用。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Resource2
{
Lock lock=new ReentrantLock ();
Condition con1=lock.newCondition();
Condition con2=lock.newCondition();
private String name;
private int count=1;
private boolean flag=false;//flag为假表示还没生产好,flag为真表示生产好了
public void Set(String name) throws InterruptedException
{
lock.lock();
try
{
while(flag==true)
{
con1.await();
}
this.name=name+count;
count++;
System.out.println(Thread.currentThread().getName()+"。。。。生产.."+this.name);
flag=true;
con2.signal();
}
finally
{
lock.unlock();
}
}
public synchronized void out() throws InterruptedException
{
lock.lock();
try
{
while(flag==false)
{
con2.await();
}
System.out.println(Thread.currentThread().getName()+"。。。。消费.."+this.name);
flag=false;
con1.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable
{
Resource2 r;
Producer(Resource2 r)
{
this.r=r;
}
public void run()
{
while(true)
{
try {
r.Set("馒头");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
class ConSumer implements Runnable
{
Resource2 r;
ConSumer(Resource2 r)
{
this.r=r;
}
public void run()
{
while(true)
{
try {
r.out();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public class Thread2
{
public static void main (String []args)
{
Resource2 r=new Resource2();
Producer p=new Producer(r);
ConSumer c=new ConSumer(r);
Thread t0=new Thread(p);
Thread t1=new Thread(p);
Thread t2=new Thread(c);
Thread t3=new Thread(c);
t0.start();
t1.start();
t2.start();
t3.start();
}
}
记住要在finnally代码块释放锁资源。
下面看一段范例代码
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock(); try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally { lock.unlock(); }
}
public Object take() throws InterruptedException {
lock.lock(); try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
item是一个容器,容器未满时,多个线程就行放操作,也可能多个线程进行拿操作。假设一直放放放,放满容器后清零从头再放,放的进程进入挂起状态,拿的进程就拿拿拿,直到拿完后清零后从头再拿,拿的进程挂起,又进行放放放。当然边放的时候可以进行边拿。