java多线程---显示锁Lock&Condition的使用

    显示与隐试:显式就是加锁和释放锁,是用户代码课操控的,而隐式就是一个标记,加索和释锁为jvm来处理。

    lock是1.5推出的,并不是取代synchronized的,而是带来了更多的特性,synchronized是在1.6进行升级的。

一,  基本使用

它为什么推出呢?这里就要涉及到synchronized的缺点

    我们直到synchronized的粒度是对象,当一个线程不放锁,其他线程都需要无限等待,这个时候,假设:这个线程由于Io或者其他原因,被阻塞了,那么其他的线程只有无限等待,很影响程序的效率,这个时候,就需要让其他线程,不能无限的等待,比如说:等待一段时间或者响应中断,Lock就实现了这个功能。

Lock是一个接口,定位java.util.concurrent.locks.Lock。它对显式进行了抽象,

public interface Lock {
    void lock();     //获取锁
    void lockInterruptibly() throws InterruptedException; // 可中断获取锁
    boolean tryLock(); //与if判断配合,未获取到直接返回,非阻塞获取锁
    boolean tryLock(long time, TimeUnit unit) throws  InterruptedException; //超时则返回,阻塞一定时间
    void unlock();  //释放锁
    Condition newCondition(); // 返回一个Condition 绑定lock 实例
}
  

二,实现类

Lock接口的实现类:ReentrantLock

ReentrantLock 的意思是,可重入锁。
方法:
ReentrantLock() //创建一个 ReentrantLock 的实例
ReentrantLock(boolean fair) //创建一个具有给定公平策略的 ReentrantLock

int getHoldCount() //查询当前线程保持此锁的次数
protected Thread getOwner() //返回目前拥有此锁的线程,如果此锁不被任何线程拥有,则返回 null protected Collection getQueuedThreads() //返回一个collection,它包含可能正等待获取此锁的线程 
int getQueueLength() //返回正等待获取此锁的线程估计数 
protected Collection getWaitingThreads(Condition condition) //返回一个 collection,它包含可能正在等待与此锁相关给定条件的那些线程 
int getWaitQueueLength(Condition condition) //返回等待与此锁相关的给定条件的线程估计数
boolean hasQueuedThread(Thread thread) //查询给定线程是否正在等待获取此锁boolean hasQueuedThreads() //查询是否有些线程正在等待获取此锁
boolean hasWaiters(Condition condition) //查询是否有些线程正在等待与此锁有关的给定条件
boolean isFair() //如果此锁的公平设置为 true,则返回true 
boolean isHeldByCurrentThread() //查询当前线程是否保持此锁
boolean isLocked() //查询此锁是否由任意线程保持
void lock() //获取锁
void lockInterruptibly() //如果当前线程未被中断,则获取锁。
Condition newCondition() //返回用来与此 Lock 实例一起使用的 Condition 实例 
boolean tryLock() //仅在调用时锁未被另一个线程保持的情况下,才获取该锁
boolean tryLock(long timeout, TimeUnit unit) //如果锁在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁 
void unlock() //试图释放此锁

具体的使用:三种上锁方式

(1)lock()的使用方法

public class MyPoint {
      private Lock lock=new ReentrantLock();
      static long abb=0;
      public void testMethod() {
            //lock.lock();
            for(int i=0;i<500;i++) {
                  abb++;
            }
            System.out.println("最后的值:"+abb);
            //lock.unlock();
      }
}
//测试
public class MyThread extends Thread{
      private MyPoint service;
      public MyThread(MyPoint service) {
            this.service=service;
      }
      public void run() {
            service.testMethod();
      }
      public static void main(String []args) {
            MyPoint a=new MyPoint();
            MyThread a1=new MyThread(a);
            ..       a4  ..
            a1.start();  ...
            a4.start();
      }
}
//内部锁
public synchronized void testMethod() {...}

        1.不加锁是乱的而且可能达不到2000这个值。

    2.上锁后:    效果是一致的。

(2) tryLock()的使用方法

在上面实验的基础上,只改动第一个类。

public class MyPoint {
      private Lock lock=new ReentrantLock();
      static long abb=0;
      public  void testMethod() {
            if(lock.tryLock()) {
                  
            System.out.println("得到锁,开始计算:abb="+abb);
            for(long i=0;i<50000;i++) {
                  abb++;
            }
            System.out.println("最后的值:"+abb);
            lock.unlock();
            }else {
                  System.out.println("未得到锁,直接返回");
            }
      }
}
//内部锁
public synchronized void testMethod() {...}

显示锁的结果:

java多线程---显示锁Lock&Condition的使用_第1张图片
可以看到,四个线程,只有一个线程获得了锁,而且返回了,但是其他三个线程就没有执行,没有等待,没有再去竞争锁。
内部锁的结果:
java多线程---显示锁Lock&Condition的使用_第2张图片
很明显,四个线程都参与了计算,这里面发生了阻塞,等待,唤醒等等操作。
3)lockInterruptibly()的使用
public class MyPoint {
      private Lock lock=new ReentrantLock();
      static long abb=0;
      public  void testMethod() throws InterruptedException {
            lock.lockInterruptibly();
            System.out.println(Thread.currentThread().getName()+" 得到锁,开始计算:abb="+abb);
            for(long i=0;i<500000000;i++) {
                  abb++;
                  abb%=54635135;
            }
            System.out.println(Thread.currentThread().getName()+" 最后的值:"+abb);
            lock.unlock();
      }
}
//---------
public class MyThread extends Thread{
      private MyPoint service;
      public MyThread(MyPoint service) {
            this.service=service;
      }
      public void run() {
            try {
                  service.testMethod();
            } catch (InterruptedException e) {
                  // TODO Auto-generated catch block
                  System.out.println("被中断!。。");
            }
      }
      public static void main(String []args) {
            MyPoint a=new MyPoint();
            MyThread a1=new MyThread(a);
            MyThread a2=new MyThread(a);
            MyThread a3=new MyThread(a);
            a1.start();
            a2.start();
            a3.start();
            try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
            a2.interrupt();
      }
}

结果:java多线程---显示锁Lock&Condition的使用_第3张图片

很明显,第二个线程,也就是a2没有执行,被中断了。

三,condition 接口

    Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

在使用notify/notifyAll()方法进行通知时,被通知的线程是有JVM选择的,使用ReentrantLock类结合Condition实例可以实现“选择性通知”,这个功能非常重要,而且是Condition接口默认提供的。

    而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程

    第一个 demo 实现 生产者/消费者模式:一对一交替打印
public class MyService {
      private Lock lock=new ReentrantLock();
      private Condition condition=lock.newCondition();
      private boolean hasValue=false;
      public void set() {
            try {
                  lock.lock();
                  while(hasValue) {
                        condition.await();
                  }
                  System.out.println("打印 ★");
                  hasValue=true;//有货了
                  condition.signal();//唤醒
            }catch(InterruptedException e) {
                  e.printStackTrace();
            }finally {
                  lock.unlock();
            }
      }
      public void get() {
            try {
                  lock.lock();
                  while(!hasValue)
                        condition.await();
                  System.out.println("打印 ☆");
                  hasValue=false; //消耗了
                  condition.signal();
            }catch(InterruptedException e) {
                  e.printStackTrace();
            }finally {
                  lock.unlock();
            }
      }
}
//------------线程
public class ThreadA extends Thread{
      private MyService service;
      public ThreadA(MyService service) {
            this.service=service;
      }
      public void run()  {
            while(true) {
                  try {
                        Thread.sleep(1000);
                  } catch (InterruptedException e) {
                        e.printStackTrace();
                  }
                  service.set();
            }
      }
      public static void main(String []args) {
            MyService service=new MyService();
            ThreadA a=new ThreadA(service);
            ThreadB b=new ThreadB(service);
            a.start();
            b.start();
      }
}
//------b线程很简单,就是把set改为get就可以了,这里就不冗余了。

结果:

小结: 这里的使用和传统的线程通信wait/notify感觉 没什么区别,只是语句换了一下。
有一个改变的地方就就是:Object.notify()==>Condition.signal();   notifyAll()->signalAll()
第二个demo 唤醒指定线程来体会condition的优越性
public class MyService {
      private Lock lock=new ReentrantLock();
      private Condition conditionA=lock.newCondition();
      private Condition conditionB=lock.newCondition();
      public void waitA() {
            try {
                  lock.lock();
                  System.out.println(Thread.currentThread().getName()+"  start");
                  conditionA.await();
                  System.out.println(Thread.currentThread().getName()+"  end");
            }catch(InterruptedException e) {
                  e.printStackTrace();
            }finally {
                  lock.unlock();
            }
      }
      public void waitB() {
           ...
           conditionB.await();
           ...
      }
      public void signal_A() {
            try {
                  lock.lock();
                  System.out.println(Thread.currentThread().getName()+"  唤醒 Thread-0");
                  conditionA.signalAll();
            }finally {
                  lock.unlock();
            }
      }
}
public class ThreadA extends Thread{
      private MyService service;
      public ThreadA(MyService service) {
            this.service=service;
      }
      public void run()  {
            service.waitA();
      }
      public static void main(String []args) throws InterruptedException  {
            MyService service=new MyService();
            ThreadA a=new ThreadA(service);
            ThreadB b=new ThreadB(service);
            a.start();
            b.start();
            Thread.sleep(2000);
            service.signal_A();
      }
}

java多线程---显示锁Lock&Condition的使用_第4张图片

可见,用什么condition执行等待,那么唤醒的时候,就是唤醒使用对应condition类的线程。

你可能感兴趣的:(java,多线程,java,并发编程)