(转载)多线程编程学习四(Lock 的使用)

一、前言

    本文要介绍使用Java5中 Lock 对象,同样也能实现同步的效果,而且在使用上更加方便、灵活,主要包括 ReentrantLock 类的使用和ReentrantReadWriteLock 类的使用。

二、使用ReentrantLock 类

1、在java多线程中,可以使用synchronized关键字来实现线程之间同步互斥,但在JDK1.5中新增加的ReentrantLock也能达到同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定、多路分支通知等功能,而且在使用上也比synchronized更加的灵活。

2、调用lock.lock()代码的线程就持有了“对象监视器”,即lock 持有的是对象锁,依赖于该类的实例存在。

复制代码
public class MyService {
    private Lock lock=new ReentrantLock();
    public void testMethod(){
        lock.lock();
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+(i+1));
        }
        lock.unlock();
    }
}
复制代码

3、关键字synchronized 与wait() 和 notify()/notifyAll() 方法相结合可以实现等待/通知模式,类ReentrantLock 也可以实现同样的功能,但需要借助于Condition对象。

Object类中的wait()方法相当于Condition类中的await()方法
Object类中的wait(long timeout)方法相当于Condition类中的await(long time,TimeUnit unit)方法
Object类中的notify()方法相当于Condition类中的signal()方法
Object类中的notifyAll()方法相当于Condition类中的signalAll()方法
复制代码
public class Myservice {
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    //等待
    public void waitMethod(){
        try {
            lock.lock();
            System.out.println("A");
            condition.await();//调用的Condition的await等待方法也需要在同步方法中,否则会报错
            System.out.println("B");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
    //唤醒
    public void signal(){
        try {
            lock.lock();
            System.out.println("现在开始唤醒...");
            condition.signal();
        }finally {
            lock.unlock();
        }
    }
}
复制代码

4、使用多个Condition对象 实现线程之间的选择性通知。

复制代码
public class MyService {
    private Lock lock=new ReentrantLock();
    //通过定义多个Condition实现选择性通知,可以唤醒指定种类的线程,这是
    //控制部分线程行为的方便形式
    private Condition conditionA=lock.newCondition();
    private Condition conditionB=lock.newCondition();

    public void awaitA(){
        try {
            lock.lock();
            System.out.println("awaitA begin");
            conditionA.await();
            System.out.println("awaitA end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void awaitB(){
        try {
            lock.lock();
            System.out.println("awaitB begin");
            conditionB.await();
            System.out.println("awaitB end");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }

    public void signalA(){
        try {
            lock.lock();
            System.out.println("现在开始唤醒awaitA");
            conditionA.signalAll();
        }finally {
            lock.unlock();
        }
    }

    public void signalB(){
        try {
            lock.lock();
            System.out.println("现在开始唤醒awaitB");
            conditionB.signalAll();
        }finally {
            lock.unlock();
        }
    }
}
复制代码
复制代码
public class Run
{
    public static void main(String[] args) throws InterruptedException
    {
        MyService myService=new MyService();
        Thread threadA=new Thread(){
            @Override
            public void run()
            {
                super.run();
                myService.awaitA();
            }
        };

        Thread threadB=new Thread(){
            @Override
            public void run()
            {
                super.run();
                myService.awaitB();
            }
        };

        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        myService.signalA();
        Thread.sleep(1000);
        myService.signalB();

    }
}
复制代码

5、公平锁和非公平锁

公平锁:表示线程获得锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序。
非公平锁:一种获得锁的抢占机制,是随机获取锁的,和公平锁不一样的就是先来的不一定先得到锁,这种方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。
复制代码
public class Service {
    private Lock lock;

    public Service(boolean isFair)
    {
        //通过这种方式创建公平锁(true)和非公平锁(false)
        lock=new ReentrantLock(isFair);
    }

    public void methodA(){
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName()+"正在运行");
        }finally {
            lock.unlock();
        }
    }
}
复制代码
复制代码
public class Run {
    public static void main(String[] args)
    {
        final Service service=new Service(true);
        Runnable runnable=new Runnable() {
            @Override
            public void run()
            {
             service.methodA();
            }
        };

        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].setName("线程"+(i+1));
            threads[i].start();
        }
    }
}
复制代码

 6、ReentrantLock 常用方法介绍

(1) int getHoldCount() 查询当前线程保持此锁定的个数,也就是线程中调用lock方法的次数。

(2) int getQueueLength() 返回正等待此锁定的线程估计数,比如有5个线程,1个线程正占用了这个Lock锁在执行,则调用此方法返回的就是4。该值仅是估计的数字,因为在此方法遍历内部数据结构的同时,线程的数目可能动态地变化。此方法用于监视系统状态,不用于同步控制。

(3) int getWaitQueueLength(Condition condition) 返回等待与此锁定相关的给定条件Condition的线程估计数,比如有五个线程,每个线程都执行了同一个condition对象的await()方法,则调用此方法返回的值就是5。

复制代码
public class Service {
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    public void methodA(){
        try {
            lock.lock();
            System.out.println("A getHoldCount 调用lock的次数=>"+lock.getHoldCount());
            Thread.sleep(2000);
            System.out.println("A getQueueLength 正在等待的线程数=>"+lock.getQueueLength());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    //测试getWaitQueueLength方法
    public Integer methodC(){
        try {
            lock.lock();
            return lock.getWaitQueueLength(condition);
        }finally {
            lock.unlock();
        }

    }
}
复制代码
复制代码
public class Run{

    public static void main(String[] args) throws InterruptedException {
        Service service=new Service();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
               service.methodA();
            }
        };

        Thread[] threads=new Thread[5];
        for (int i=0;i<5;i++){
            threads[i]=new Thread(runnable);
            threads[i].start();
        }

        Thread.sleep(1000);
        System.out.println("执行了同一个Condition对象的的await()的线程有:"+service.methodC());
    }
}
复制代码

(4) boolean hasQueuedThread(Thread thread) 查询指定的线程是否正在等待获取此锁定。

(5) boolean hasQueuedThreads() 查询是否有线程正在等待获取此锁定。

(6) boolean hasWaiters(Condition condition) 查询是否有线程正在等待与此锁定有关的condition条件

(7) boolean isFair() 判断是不是公平锁。

(8) boolean isHelpByCurrentThread() 查询当前线程是否保持此锁定。

(9) boolean isLocked() 查询此锁定是否由任意线程保持。

 

(10) void lockInterruptibly() 如果当前线程未被中断,则获取锁定,如果已经被中断,则出现异常。

(11) boolean tryLock() 仅在调用时锁定未被另一个线程锁定的情况下,才获得此锁定。

(12) boolean tryLock(long timeout,TimeUnit unit) 如果锁定在给定等待时间内没有被另一个线程保持,且当前线程未被中断,则获取该锁定。

复制代码
public class Service
{
    private ReentrantLock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();

    //测试lockInterruptibly
    public void methodA(){
        try {
            lock.lockInterruptibly();
            System.out.println("methodA=》"+Thread.currentThread().getName());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            if (lock.isHeldByCurrentThread()){//如果当前线程依旧保持对此锁的锁定,则释放
                lock.unlock();
            }
        }
    }
    //测试tryLock
    public void methodB(){
          if (lock.tryLock()){
              System.out.println(Thread.currentThread().getName()+"获得锁");
          }else{
              System.out.println(Thread.currentThread().getName()+"未获得锁");
          }
    }
}
复制代码
复制代码
public class Run
{
    public static void main(String[] args) throws InterruptedException
    {
        Service service=new Service();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                service.methodA();
                service.methodB();
            }
        };

        Thread threadA=new Thread(runnable);
        threadA.setName("A");
        threadA.start();

        Thread.sleep(1000);
        Thread threadB=new Thread(runnable);
        threadB.setName("B");
        threadB.start();
        threadB.interrupt();
    }
}
复制代码

 

(13) lock.awaitUninterruptibly():这个线程将不会被中断,一直睡眠直到其他线程调用signal()或signalAll()方法。

(14) lock.awaitUntil(Date date):这个线程将会一直睡眠直到:

  • 它被中断
  • 其他线程在这个condition上调用singal()或signalAll()方法
  • 指定的日期已经到了

三、使用ReentrantReadWriteLock 类 

      类RenntrantLock具有完全互斥排他的效果,即同一时间只有一个线程在执行RenntrantLock.lock()方法后面的任务。这样做虽然保证了实例变量的线程安全性,但效率却是非常低下的,因为即使有时候锁内没有写入内容,而也要等锁释放后,才能进行读取。所以JDK提供了一种读写锁ReentrantReadWriteLock 类,使用它可以加快运行效率。              
      ReentrantReadWriteLock有两个锁,一个是读操作相关的锁,也称为 共享锁;另一个是写操作相关的锁,也叫 排他锁。也就是 多个读锁之间不互斥、读锁与写锁互斥、写锁与写锁互斥。在没有线程Thread进行写入操作时,进行读取操作的多个Thread 都可以获取读锁。而进行写入操作的Thread 只有在获取写锁后才能进行写入操作。即多个Thread可以同时进行读取操作,但是同一个时刻只允许一个Thread 进行写入操作。
 
读读不互斥:
复制代码
public class Read
{
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    public void read(){
        try {
            lock.readLock().lock();
            System.out.println(Thread.currentThread().getName()+"正在读"+System.currentTimeMillis());
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.readLock().unlock();
        }
    }
}
复制代码
复制代码
public class Run
{
    public static void main(String[] args)
    {
        Read read=new Read();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                read.read();
            }
        };
        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].start();
            //通过结果可以看到所有线程几乎同时进入lock()方法
            //后面的代码,读读不互斥,可以提高程序运行效率,允许
            //多个线程同时执行lock()方法后面的代码
        }
    }
}
复制代码
写写互斥:
复制代码
public class Write
{
    private ReentrantReadWriteLock lock=new ReentrantReadWriteLock();

    public void write(){
        try {
            lock.writeLock().lock();
            System.out.println(Thread.currentThread().getName()+"正在写"+System.currentTimeMillis());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.writeLock().unlock();
        }
    }
}
复制代码
复制代码
public class Run
{
    public static void main(String[] args)
    {
        Write write=new Write();
        Runnable runnable=new Runnable()
        {
            @Override
            public void run()
            {
                write.write();
            }
        };
        Thread[] threads=new Thread[10];
        for (int i=0;i<10;i++){
            threads[i]=new Thread(runnable);
            threads[i].start();
            //通过结果可以看到所有线程每隔两秒运行一次,写写互斥,线程之间是同步运行的
        }
    }
}
复制代码

 

    另外,写读、读写都是互斥的,就不举例了。总之,只要出现"写"操作,就是互斥的!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值