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

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

一、前言

    本文要介绍使用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();
    }
}
View Code

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();
        }
    }
}
View Code

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();
        }
    }
}
View Code
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();

    }
}
View Code

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();
        }
    }
}
View Code
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();
        }
    }
}
View Code

 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();
        }

    }
}
View Code
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());
    }
}
View Code

(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()+"未获得锁");
          }
    }
}
View Code
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();
    }
}
View Code

(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();
        }
    }
}
View Code
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()方法后面的代码
        }
    }
}
View Code
写写互斥:
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();
        }
    }
}
View Code
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();
            //通过结果可以看到所有线程每隔两秒运行一次,写写互斥,线程之间是同步运行的
        }
    }
}
View Code

 

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

目录
相关文章
|
7天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
21 2
|
1月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
105 6
【Java学习】多线程&JUC万字超详解
|
15天前
|
Java
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
领略Lock接口的风采,通过实战演练,让你迅速掌握这门高深武艺,成为Java多线程领域的武林盟主
23 7
|
12天前
|
Java 调度 开发者
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java多线程编程的核心概念和实际应用,通过浅显易懂的语言解释多线程的基本原理,并结合实例展示如何在Java中创建、控制和管理线程。我们将从简单的线程创建开始,逐步深入到线程同步、通信以及死锁问题的解决方案,最终通过具体的代码示例来加深理解。无论您是Java初学者还是希望提升多线程编程技能的开发者,本文都将为您提供有价值的见解和实用的技巧。
15 2
|
14天前
|
Java 数据处理
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java中的多线程编程,涵盖其基本概念、创建方法、同步机制及实际应用。通过对多线程基础知识的介绍和具体示例的演示,希望帮助读者更好地理解和应用Java多线程编程,提高程序的效率和性能。
19 1
|
18天前
|
Java
深入理解Java中的多线程编程
本文将探讨Java多线程编程的核心概念和技术,包括线程的创建与管理、同步机制以及并发工具类的应用。我们将通过实例分析,帮助读者更好地理解和应用Java多线程编程,提高程序的性能和响应能力。
20 4
|
26天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
17天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
27天前
|
API Python
探索Python中的多线程编程
探索Python中的多线程编程
39 5
|
27天前
|
算法 Java 数据处理
Java并发编程:解锁多线程的力量
在Java的世界里,掌握并发编程是提升应用性能和响应能力的关键。本文将深入浅出地探讨如何利用Java的多线程特性来优化程序执行效率,从基础的线程创建到高级的并发工具类使用,带领读者一步步解锁Java并发编程的奥秘。你将学习到如何避免常见的并发陷阱,并实际应用这些知识来解决现实世界的问题。让我们一起开启高效编码的旅程吧!