zoukankan      html  css  js  c++  java
  • 多线程之Lock显示锁

    ThreadLocal的使用

    除了控制资源访问外,还可以通过增加资源来保证线程安全,ThreadLocal主要解决为每个线程绑定自己的值,相当于增加了一个副本,在进行数据库访问的时候,多个线程需要自己独立的 static 成员变量吗,那就需要使用ThreadLocal。

    什么是Lock显示锁

    锁分为内部锁和外部锁,内部锁是通过synchronized实现的,显示锁lock是在JDK5中新增的,最常见的实现类是 ReentrantLock、ReadLock、WriteLock,可以起到 “锁” 的作用,由ReentrantLock实现类,ReentrantLock锁被称为可重入锁,它的功能比synchronized多。

    Lock接口有5个方法1个条件,

    public void lock() { }
    public void lockInterruptibly() throws InterruptedException { }
    public boolean tryLock() { }
    public boolean tryLock(long timeout, TimeUnit unit){  }
    public void unlock() { }
    public Condition newCondition() {  }
    

    Lock锁的使用

    Lock 有 4 种加锁方法,其中 lock 是最基础的。Lock 获取锁和释放锁都是显式的,不像 synchronized 是隐式的。所以 synchronized 会在抛异常时自动释放锁,而 Lock 只能是主动释放,加解锁都必须有显式的代码控制。

    lock()、unlock()方法

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class Text21 {
        public static void main(String[] args) {
            Runnable r=new Runnable() {
                @Override
                public void run() {
                    sm();
                }
            };
            new Thread(r).start();
            new Thread(r).start();
            new Thread(r).start();
        }
       public static Lock lock=new ReentrantLock();
        public  static  void  sm()
        {
            lock.lock();//获得锁
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+"-->"+i);
            }
            lock.unlock();//释放锁
        }
    }
    

    image-20210321203301232

    lock的获得和释放就相当于synchronized代码块

    trylock(定时锁)方法

    trylock(long time,TimeUnit unit)的作用在给定的等待时长内锁没有被另外的线程持有,并且当前线程也没有被中断,则获得该锁,通过该方法可以实现锁对象的限时等待

    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class TryLockText {
        public static void main(String[] args) {
            TimeLock timeLock=new TimeLock();
            Thread t1=new Thread(timeLock);
            Thread t2=new Thread(timeLock);
            t1.start();
            t2.start();
        }
        static  class  TimeLock implements  Runnable
        {
            static ReentrantLock reentrantLock=new ReentrantLock();
            @Override
            public void run() {
                try {
                    if(reentrantLock.tryLock(3, TimeUnit.SECONDS))//3秒内获得锁返回true
                    {
                        System.out.println(Thread.currentThread().getName()+"获得锁");
                        Thread.sleep(4000);
                    }
                    else
                        System.out.println(Thread.currentThread().getName()+"没有获得锁");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    if(reentrantLock.isHeldByCurrentThread())//如果锁被当前线程持有
                    {
                        reentrantLock.unlock();//释放锁
                    }
                }
            }
        }
    }
    

    image-20210324204645834

    Thread-0在获得锁之后休眠了4秒,Thread-1尝试等待3秒获得锁,还没有获得就不会继续等待了,释放锁了。

    trylock()

    仅在调用的时候锁定未被其他线程持有的锁,如果调用此方法时,被其他方法占用则放弃返回false.

    trylock可以用来避免死锁,线程在获取在尝试获取Thread-0,获取不到一直尝试获取,获取到了继续获取Thread-1,如果获取不到Thread-1就会释放Thread-0,这样避免了死锁,两个锁都不会被对方占用而无法释放。

    lockInterruptibly方法

    lockInterruptibly方法表示当前线程没有被中断就可以获得锁,如果中断就会产生异常

    一个线程在等待锁,只有两种结果,要么获得锁继续执行,要么就是在等待锁,可以理解为不限时的trylock,对于可重入锁来说还有一种可能,就是在需要的时候取消对锁的请求。

    通过在使用lock的时候,lockInterruptibly方法也可以避免死锁的产生

    newCondition方法

    关键字synchronized与wait、notify这两个方法一起使用可以实现等待/通知模式,Lock的newCondition方法返回Condition对象,Condition类也可以实现等到/通知模式。

    使用notify通知时,JVM会随机唤醒,Condition类可以进行选择性通知,比较常用的两个方法:

    await():会使当前线程等待,同时释放锁

    signal():用于唤醒一个线程

    注意:在调用Condition的await、signal方法前也需要线程持有相关的锁。例如

    import java.util.concurrent.locks.Condition;
    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
    public class ConditionText {
        public static void main(String[] args) throws InterruptedException {
            Text24 text24=new Text24();
            text24.start();
    
            Thread.sleep(3000);//睡眠三秒唤醒子线程
            lock.lock();//指定锁 并锁住
            condition.signal();//在唤醒之前需要持有锁
            lock.unlock();
    
        }
        static Lock lock=new ReentrantLock();
        static Condition condition=lock.newCondition();
        static  class  Text24 extends Thread
        {
            @Override
            public void run() {
                lock.lock();
                System.out.println("begin---》");
                try {
                    condition.await();
                    System.out.println("wait--》");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                finally {
                    lock.unlock();
                    System.out.println("解锁成功");
                }
    
            }
        }
    }
    

    image-20210324233748438

    公平锁与非公平锁

    大多数情况下,锁的申请都是非公平的,如果i当线程1线程2都在请求锁A,当锁A可用时,系统只是会从阻塞队列中随机的选择一个线程,并不能保证公平性。

    公平的锁会按照时间的先后顺序,保证先到先得,公平锁这一特点不会出现线程饥饿现象。
    synchronized内部锁就是非公平的,Reentrantlock(boolean fair),在创建锁的时候可以传递实参为true,就可以实现公平锁。

    import java.util.concurrent.locks.ReentrantLock;
    
    public class Text25 {
        static ReentrantLock lock=new ReentrantLock();
        public static void main(String[] args) {
            Runnable runnable=new Runnable() {
                @Override
                public void run() {
                    while (true)
                    {
                        lock.lock();
                        System.out.println(Thread.currentThread().getName()+"获得了锁对象");
                        lock.unlock();
                    }
                }
            };
            for (int i = 0; i < 5; i++) {
                new Thread(runnable).start();
            }
        }
    
    }
    

    image-20210325213324262

    这里一直是同一个线程一直获得锁对象,而不是差不多分配好的。这里明显的是非公平锁,系统倾向于再次获得已经持有的锁,这种分配是高效的但是非公平。

    为了实现公平锁,只需要在ReentrantLock方法传入参数true,表示实现公平锁

    image-20210325213551790

    但是要实现公平锁,必须要求系统维护一个有序对队列,实现成本较高,不是特别的需求,一般不使用公平锁。

    Reentrantlock常用方法

    int getHoldCount()//可以返回调用lock()方法的次数
    int getQueueLength()//可以返回等待获得锁的线程预估数量
    int getWaitQueueLength()//返回Condition条件相关的线程预估数量
    boolean hasQueuedThread()//查询指定的线程是否还有线程在等待获得该锁
    boolean hasWaiters()//查询线程是否有线程正在等待指定以Condition
    boolean isHeldByCurrentThread()//判断当前线程是否持有该锁
    boolean isLocked()//判断锁是否被线程持有
    
  • 相关阅读:
    HTML颜色表
    SQL 语法参考手册
    色环[微软MSDN]
    网页配色方案及方法[网上配色文章集合
    驰骋工作流程引擎公文流程引擎图片演示VSTO技术
    工作流程引擎手机应用方寸之间尽在掌握。
    关于流程的退回与撤消
    驰骋.net工作流程引擎设计开发讲座: 工作流程类型模式
    驰骋工作流程引擎,工作流程管理系统,工作流程中间件, 支持日历面板,采用vsto技术处理公文类的流程流转。
    驰骋.net工作流程引擎,工作流程管理系统定时启动约定
  • 原文地址:https://www.cnblogs.com/cg-ww/p/14599164.html
Copyright © 2011-2022 走看看