可以解决多线程通信中出现的问题
在API文档中的查找:
-
public interface Lock
Lock
实现提供比使用synchronized
方法和语句可以获得的更广泛的锁定操作。 它们允许更灵活的结构化,可能具有完全不同的属性,并且可以支持多个相关联的对象Condition
。锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁。 但是,一些锁可能允许并发访问共享资源,如
ReadWriteLock
的读锁。使用
synchronized
方法或语句提供对与每个对象相关联的隐式监视器锁的访问,但是强制所有锁获取和释放以块结构的方式发生:当获取多个锁时,它们必须以相反的顺序被释放,并且所有的锁都必须被释放在与它们相同的词汇范围内。虽然
synchronized
方法和语句的范围机制使得使用监视器锁更容易编程,并且有助于避免涉及锁的许多常见编程错误,但是有时您需要以更灵活的方式处理锁。 例如,用于遍历并发访问的数据结构的一些算法需要使用“手动”或“链锁定”:您获取节点A的锁定,然后获取节点B,然后释放A并获取C,然后释放B并获得D等。 所述的实施方式中Lock
接口通过允许获得并在不同的范围释放的锁,并允许获得并以任何顺序释放多个锁使得能够使用这样的技术。
在同步中:synchronized (obj)
{
code......
释放锁
}
锁的获取和释放都是隐式的,在代码中我们是看不见的,只有在class 文件中可以看见。
但是JDK1.5及其以上的版本提供了更加自由的锁——————
LOCK接口:lock(): 获取锁 lock(): 释放锁
升级到JDK1.5,把同步改成 LOCK
此时已经将旧锁改成新锁,那么锁上的监视器方法(wait() notify() notifyall())也就应该被替换成新的锁监视器方法。JDK1.5将这些原有的监视器方法封装到了一个condition对象中。想要获取监视器方法newCondition(0需要先获取condition对象。
API 中的相关解释:
-
Condition
因素出Object
监视器方法(wait
,notify
和notifyAll
)成不同的对象,以得到具有多个等待集的每个对象,通过将它们与使用任意的组合的效果Lock
个实现。Lock
替换synchronized
方法和语句的使用,Condition
取代了对象监视器方法的使用。 -
相关总结(关于新锁和旧锁):
在synchronized() 锁为任意对象,而每一个锁上都有一组监视器方法(对象.wait()..)其实这就是一套,锁的获取和释放也是隐式的也是这一套中的。
在lock 将锁单独的封装成一个对象 Lock lock= new ReetrantLock(); lock.lock();//获取锁 lock.unlock();释放锁。而且将所有的监视器方法封装到Condition 对象中。所以说lock是更加面向对象的的锁,此时锁和监视器方法是被分开的。在旧锁中,锁一出现就拥有一组监视器(出场自带BGM)。有获取监视器方法就获取Condition的对象,sh使用newcondition() 方法,而注意newCondition() 是属于Lock接口的:Condition con=lock.newCondition(); 使用监视器方法 con.await() con.signal()//signalall();
图解:
使用新锁的代码:
import java.util.concurrent.locks.*;
/*
新锁Lock
*/
class Resource{//简化程序-设资源只有一个//多线程之间的通信
private String name;
private int number=1;
private boolean flag;
Lock lock = new ReentrantLock();//获得锁对象
Condition con = lock.newCondition();//获得监视器方法对象
//提供设置方法
public void set(String name ){
lock.lock();
try {
while (flag) {//先判断
try {
con.await();
} catch (InterruptedException e) {
}//存在商品,此线程停止
}
//给成员变量赋值
this.name = name + number;
number++;
//打印生产了那个商品
System.out.println(Thread.currentThread().getName() + " 生产者 " + this.name);
flag = true;//生产完,修改标志
con.signalAll();
}
finally {
lock.unlock();//一定要执行。以防中间有什么一场,锁的释放也一定要执行
}
}
//消费者的
public synchronized void Out(){
lock.lock();
try {
while (!flag) {//先判断
try {
con.await();
} catch (InterruptedException e) {
} //没有商品,消费者停止
}
System.out.println(Thread.currentThread().getName() + " 消费者 " + this.name);
flag = false;//修改标记
con.signalAll
}
finally {
lock.unlock();
}
}
}
//描述生产者
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r = r;
}
public void run(){
for(int x=0;x<5;x++) {
r.set("面包");
}
}
}
//描述消费者
class consumer implements Runnable{
private Resource r;
consumer(Resource r){
this.r=r;
}
public void run(){
for(int x=0;x<5;x++) {
r.Out();
}
}
}
public class producerAndConsumer {
public static void main(String[] args){
//创建资源对象
Resource r = new Resource();
//创建线程任务
Producer pro = new Producer(r);
consumer con = new consumer(r);
//创建线程
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
在这里新锁和旧锁的代码效果完全是一样的,就连出现的问题都是一样的
书写代码时的注意:手动写上锁的获取和释放,一定要用try() finally()结构,因为释放锁是一定要做的
新锁正式解决多线程通信问题
之所以synchronized 无法实现每次唤醒对方——是因为锁与监视器方法是一体的,因为调用方法的对象都是一样的,所有监视器方法等待后都在同一个线程池中,但是如果使线程处于不同的线程池中就要修改锁,此时就破坏了线程的同步。
但是Lock就可以实现唤醒对方——因为 锁和监视器方法是分开的,只要只使用的是一个锁就不会破坏线程的同步性,同时使用多个监视器对象,每次就可以实现唤醒指定的线程。————这就解决了唤醒所有线程的效率损失问题(为解决死锁)
代码:
import java.util.concurrent.locks.*;
/*
新锁Lock//解决因为解决旧锁不能解决的为解决死锁修改后的代码的效率损失问题。每次调用对方
*/
class Resource{//简化程序-设资源只有一个//多线程之间的通信
private String name;
private int number=1;
private boolean flag;
Lock lock = new ReentrantLock();//获得锁对象//保证线程安全——同步性
Condition pro = lock.newCondition();
Condition con = lock.newCondition();//消费者
//获得监视器方法对象,创建两个监视器对象,使得可以唤醒指定的线程
//提供设置方法
public void set(String name ){
lock.lock();
try {
while (flag) {//先判断
try {
pro.await();
} catch (InterruptedException e) {
}//存在商品,此线程停止
}
//给成员变量赋值
this.name = name + number;
number++;
//打印生产了那个商品
System.out.println(Thread.currentThread().getName() + " 生产者 " + this.name);
flag = true;//生产完,修改标志
con.signal() ; //唤醒消费者
}
finally {
lock.unlock();
//一定要执行。以防中间有什么一场,锁的释放也一定要执行
}
}
//消费者的
public synchronized void Out(){
lock.lock();
try {
while (!flag) {//先判断
try {
con.await();
} catch (InterruptedException e) {
} //没有商品,消费者停止
}
System.out.println(Thread.currentThread().getName() + " 消费者 " + this.name);
flag = false;//修改标记
// 唤醒生产者
pro.signal();
}
finally {
lock.unlock();
}
}
}
//描述生产者
class Producer implements Runnable{
private Resource r;
Producer(Resource r){
this.r = r;
}
public void run(){
for(int x=0;x<5;x++) {
r.set("面包");
}
}
}
//描述消费者
class consumer implements Runnable{
private Resource r;
consumer(Resource r){
this.r=r;
}
public void run(){
for(int x=0;x<5;x++) {
r.Out();
}
}
}
public class producerAndConsumer {
public static void main(String[] args){
//创建资源对象
Resource r = new Resource();
//创建线程任务
Producer pro = new Producer(r);
consumer con = new consumer(r);
//创建线程
Thread t1 = new Thread(pro);
Thread t2 = new Thread(con);
t1.start();
t2.start();
}
}
完美解决!!!!!
重点:
Lock Condition newCondition,一定要先加载 import java. until.cunrrent.Locks.*
解决关于多线程间的通信问题
要理解新旧锁之间的联系与区别