5)lock锁(ReentrantLock与ReentrantReadWriteLock)

目录

1) Lock显示锁之ReentrantLock

1.1)创建锁常用方法

1.1.1)lock()与unlock()

1.1.2) lockInterruptibly()使用

1.1.3) tryLock()方法使用

1.2) condition 使用

1.3)公平锁与非公平锁

1.3.1)概述

1.3.2)公平锁与非公平锁区别:

1.4)常用的方法

2) ReentrantReadWriteLock读写锁

2.1)概述

2.2)读写锁的基本使用


1) Lock显示锁之ReentrantLock

1.1)创建锁常用方法

在JDK5中增加了lock锁接口,有ReentrantLock实现类,ReentrantLock锁称为可重入锁,功能比synchronized多,不但能实现锁的功能,还能使现等待/通知 机制,是通过方法进行同步代码块的操作

lock()获取锁,锁元素为调用lock()对象本身
unlock()释放锁
lockInterruptibly()获取锁,如果线程有中断标识(interrupt),抛出异常,跟据这个特点可以解决死问题,在两个线程死锁,可以中断其中一个线程,解决死锁
tryLock()获取锁,如果获取到锁返回true,否则false(不等待其它线程释放锁)
tryLock(long var1,TimeUnit var3)获取锁,在指定时间获取到锁返回true,否则返回false,也不再获取锁
newCondition()实现 等待/通知 机制
  

1.1.1)lock()与unlock()

相当于synchronized的同步代码块,功能是一样的,锁对象变成了lock变量。即使在两个方法,只同使用一个lock变量,锁对象就是一样的

public class LockTest01 {
    //定义显示锁
    static Lock lock = new ReentrantLock();
    //定义方法
    public static void sm(){
        //先获取锁
        lock.lock();
        for (int i = 0; i < 100; i++) {
            System.err.println(Thread.currentThread().getName()+":"+i);
        }
        //释放锁
        lock.unlock();
    }

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

1.1.2) lockInterruptibly()使用

lockInterruptibly()与lock()类似都是获取锁,区别是当前线程处于interrupt时,lock()获取锁不报错而lockInterruptibly()会抛出异常

故意在执行lockInterruptibly()前执行interrupt()方法,抛出异常。不能使用sleep()阻碍线程执行,因为如果线程处理sleep()状态,执行interrupt()同样会抛出异常

public class LockTest05 {
    static class LockInte{
        private static Lock lock = new ReentrantLock();
        public void sm() {
            try {
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    new StringBuffer();
                }
                System.err.println("::"+Thread.currentThread().isInterrupted());
                System.out.println("开始获取锁");
                lock.lockInterruptibly();
                System.out.println("开始执行");
                for (int i = 0; i < Integer.MAX_VALUE; i++) {
                    new StringBuffer();
                }
                System.out.println("执行结束");
            }catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        LockInte lockInte = new LockInte();
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                lockInte.sm();
            }
        });

        thread.start();
        thread.interrupt();

    }
}

1.1.3) tryLock()方法使用

public class tryLockTest {
    static class Tlt implements Runnable{
        private static ReentrantLock reentrantLock = new ReentrantLock();
        @Override
        public void run() {
            try{
                //tryLock获取锁,如果3秒内成功获取锁,返回true
                if(reentrantLock.tryLock(3, TimeUnit.SECONDS)){
                    System.out.println(Thread.currentThread().getName()+"获取了锁");
                    Thread.sleep(4000);
                }else {
                    System.out.println(Thread.currentThread().getName()+"没有获取锁");
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }finally {
                //如果当前线程持有锁,返回 true
                if(reentrantLock.isHeldByCurrentThread()){
                    reentrantLock.unlock(); //如果线程没有获取锁,释放会抛出异常
                }
            }

        }
    }

    public static void main(String[] args) {
        Tlt tlt = new Tlt();
        new Thread(tlt).start();
        new Thread(tlt).start();
    }
}

tryLock()真正意义上避免死锁 

lockInterruptibly()虽然可以通过结束某线程的方法,结束死锁,而tryLock()可以通过不断尝试的方法结束锁。

实际上就是while()不断执行,获取锁,获取不到就释放所有锁,在执行。这样其它线程就不会因为锁被占用造成假死

static class IntLock implements Runnable{
        private static ReentrantLock lock1 = new ReentrantLock();
        private static ReentrantLock lock2 = new ReentrantLock(); private int lockNum; //用于控制锁的顺序
        public IntLock(int lockNum) {
            this.lockNum = lockNum;
        }
        @Override
        public void run() {
            if ( lockNum % 2 == 0 ) { //偶数先锁 1,再锁 2
                while (true){
                    try {
                        if (lock1.tryLock()){
                            System.out.println(Thread.currentThread().getName() + "获得 锁 1, 还想获得锁 2");
                            Thread.sleep(new Random().nextInt(100));
                            try {
                                if (lock2.tryLock()){
                                    System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
                                    return; //结束 run()方法执行,即当前线程 结束
                                }
                            } finally {
                                if (lock2.isHeldByCurrentThread()){
                                    lock2.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock1.isHeldByCurrentThread()){
                            lock1.unlock();
                        }
                    }
                }
            }else { //奇数就先锁 2,再锁 1
                while (true){
                    try {
                        if (lock2.tryLock()){
                            System.out.println(Thread.currentThread().getName() + "获得 锁 2, 还想获得锁 1");
                            Thread.sleep(new Random().nextInt(100));
                            try {
                                if (lock1.tryLock()){
                                    System.out.println(Thread.currentThread().getName() + "同时获得锁 1 与锁 2 ----完成任务了");
                                    return; //结束 run()方法执行,即当前线程 结束
                                    }
                            } finally {
                                if (lock1.isHeldByCurrentThread()){
                                    lock1.unlock();
                                }
                            }
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } finally {
                        if (lock2.isHeldByCurrentThread()){
                            lock2.unlock();
                        }
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        IntLock intLock1 = new IntLock(11); 
        IntLock intLock2 = new IntLock(22); 
        Thread t1 = new Thread(intLock1); 
        Thread t2 = new Thread(intLock2); 
        t1.start(); t2.start();
    }

1.2) condition 使用

Lock.newCondition()返回一个Condition对象,通过Condition对象能使用 等待/通知机制

特点

  • notify()通知,jvm会随机唤醒某个等待的线程,而使用Condition类可以选择性通过

解释:

synchronized中调用等待格式为 锁对象.wait(),唤醒为 锁对象.notify()。由于锁对象都是一样的,也就无法区分了。而lock的condition()对象,可以通过new得到。

如:Condition c1 = lock.newCondition(),Condition c1 = lock.newCondition(),c1执行c1.await()后c2执行c2.signal()是唤醒不了的

synchronized
  static Lock lock = new ReentrantLock();
    //获得conditon对象
    static Condition condition = lock.newCondition();
    //定义线程子类
    static class SubThread extends Thread{
        @Override
        public void run() {
            try {
                lock.lock(); //await的操作也要获得锁后执行
                System.out.println("method lock");
                condition.await(); //设置等待
                System.out.println("methon awit");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
                System.out.println("method unlock");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        SubThread t = new SubThread();
        t.start();
        Thread.sleep(3000);

        lock.lock(); //唤醒线程同样需要获取锁
        condition.signal();
        lock.unlock();
    }

1.3)公平锁与非公平锁

1.3.1)概述

非公平锁:如果线程A与线程B都申请锁C,锁C空闲时,随机在线程A与线程B选择一个执行,如synchronized就是一个非公平锁

公平锁:会按照时间先后顺序保证先到先得,公平锁的这一特点不会出现线程饥饿现象

如果创建ReentrantLock时传入true对象获取一个公平锁,由于公平锁需要维护一个有序队列,公平锁成本高

例:

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

结果如下:

new ReentrantLock()或者new ReentrantLock(false)获取一个非公平锁结果:

Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象
Thread-0 获得了锁对象

.......
Thread-1 获得了锁对象
Thread-1 获得了锁对象
Thread-1 获得了锁对象

new ReentrantLock(true)获取一个公平锁结果:

 

Thread-0 获得了锁对象
Thread-1 获得了锁对象
Thread-4 获得了锁对象
Thread-3 获得了锁对象
Thread-2 获得了锁对象
Thread-0 获得了锁对象
 

1.3.2)公平锁与非公平锁区别:

  1. 如果是非公平锁,系统倾向于让一个线程再次获得已经持有的锁,这种分配策略是高效非公平的,如上0线程释放了锁,在获得比其它线程获得的机率大
  2. 如果是公平锁,多个线程不会发生同一个线程连续多次获得锁的可能,保证了公平性

1.4)常用的方法

getHoldCount()

返回当前线程调用lock()方法的次数

因为lock()是可重入的,实际上返回的是重入的次数

reentrantLock.lock();
System.out.println(reentrantLock.getHoldCount());
reentrantLock.lock();
System.out.println(reentrantLock.getHoldCount());
reentrantLock.unlock();
System.out.println(reentrantLock.getHoldCount());

===》

1

2

1

int getQueueLength()返回等待获得锁的线程数
static ReentrantLock reentrantLock = new ReentrantLock(true);
public static void main(String[] args) {
    reentrantLock.lock();
    new Thread(new Runnable() {
        @Override
        public void run() {
            reentrantLock.lock();
            reentrantLock.unlock();
        }
    }).start();
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("当前等待这个锁的线程:"+reentrantLock.getQueueLength());
    reentrantLock.unlock();

====
1   //有一个线程等待获得锁
int getWaitQueueLength(Condition)返回与Condition相关正在等待的线程数
static ReentrantLock reentrantLock = new ReentrantLock(true);
static Condition condition = reentrantLock.newCondition();

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            reentrantLock.lock();
            try {
                System.out.println("正在等待");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.unlock();
        }
    }).start();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    reentrantLock.lock();
    System.out.println("当前等待这个锁的线程:"+reentrantLock.getWaitQueueLength(condition));
    reentrantLock.unlock();
}

====

正在等待
当前等待这个锁的线程:1

 boolean  hasWaiters(Condition)返回是否有线程正在等待当前Condition条件。与getWaitQueueLength()类
static ReentrantLock reentrantLock = new ReentrantLock(true);
static Condition condition = reentrantLock.newCondition();

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            reentrantLock.lock();
            try {
                System.out.println("正在等待");
                condition.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.unlock();
        }
    }).start();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    reentrantLock.lock();
    System.out.println("返回是否有线程正在等待当前Condition条件:"+reentrantLock.hasWaiters(condition));
    reentrantLock.unlock();

====

正在等待
返回是否有线程正在等待当前Condition条件:true

boolean hasQueuedThread(Thread thread)查询参数指定的线程是否在等待获得锁
reentrantLock.lock();
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        reentrantLock.lock();
        try {
            System.out.println("正在等待");
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        reentrantLock.unlock();
    }
});
thread.start();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("线程是否在等待获得锁:"+reentrantLock.hasQueuedThread(thread));
reentrantLock.unlock();

=====

线程是否在等待获得锁:true
正在等待

boolean hasQueuedThreads()查询是否还有线程在等待获得锁
reentrantLock.lock();
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        reentrantLock.lock();
        try {
            System.out.println("正在等待");
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        reentrantLock.unlock();
    }
});
thread.start();
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("是否还有线程在等待获得锁:"+reentrantLock.hasQueuedThreads());
reentrantLock.unlock();

====

是否还有线程在等待获得锁:true
正在等待

boolean isFair()判断是否为公平锁
static ReentrantLock reentrantLock = new ReentrantLock(true);
static ReentrantLock reentrantLock1 = new ReentrantLock();
static Condition condition = reentrantLock.newCondition();

public static void main(String[] args) {
    System.out.println("reentrantLock是否为公平锁:"+reentrantLock.isFair());
    System.out.println("reentrantLock1是否为公平锁:"+reentrantLock1.isFair());
}

====

reentrantLock是否为公平锁:true
reentrantLock1是否为公平锁:false

boolean isHeldByCurrentThread()当前线程是否占用此锁,否则在unLock()时使用,如果线程没有占用锁,使用锁.unLock()会报错
static ReentrantLock reentrantLock = new ReentrantLock(true);
static ReentrantLock reentrantLock1 = new ReentrantLock();
static Condition condition = reentrantLock.newCondition();

public static void main(String[] args) {
    reentrantLock.lock();
    System.out.println("当前线程是否占用reentrantLock锁"+reentrantLock.isHeldByCurrentThread());
    System.out.println("当前线程是否占用reentrantLock1锁"+reentrantLock1.isHeldByCurrentThread());
    reentrantLock.unlock();
}

===

当前线程是否占用reentrantLock锁true
当前线程是否占用reentrantLock1锁false

boolean isLocked()查看锁是否被线程持有(不仅本线程)
static ReentrantLock reentrantLock = new ReentrantLock(true);

public static void main(String[] args) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            reentrantLock.lock();
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            reentrantLock.unlock();
        }
    }).start();
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    System.out.println("reentrantLock锁是否被线程持有"+reentrantLock.isLocked());
}

===

reentrantLock锁是否被线程持有true

2) ReentrantReadWriteLock读写锁

2.1)概述

ReentrantReadWriteLock在读时共享锁,写时排他锁提升了程序的运行效率。如:A,B线程需要读数据,C线程写数据,如果读锁共享,A,B可以同时读取数据提升了程序的运行效率,而C写数据时,具有排他性,A,B无法获得锁,保证了数据安全。

问题:如果实现 a = a+1;如果A,B都执行+1操作,写入的数据也是不对的

读数据使用锁,就是为了保证数据的正确性,如读User{name,age},容易出现脏读,所以只要保证读数据时,没有线程在写数据就可以了。

 获得条件排他性作用
读锁写锁未被任意线程持有对读线程是共享的,对写线程是排他的允许多个读线程可以同时读取共享数据,保证在读共享数据时,没有其他线程对共享数据进行修改
写锁该写锁未被其他线程持有,并且相应的读锁也未被其他线程持有对读线程或者写线程都是排他的保证写线程以独占的方式修改共享数据

读写锁允许读读共享(提高读取数据的效率),读写互斥,写写互斥

在 java.util.concurrent.locks 包中定义了 ReadWriteLock 接口,该接口中定义了 readLock()返回读锁,定义 writeLock()方法返回写锁. 该接口的实现类是 ReentrantReadWriteLock.注意 readLock()与 writeLock()方法返回的锁对象是同一个锁的两个不同的角色, 不是分别获得两个不同的锁. ReadWriteLock 接口实例可以充当两个角色.读写锁的其他使用方法

2.2)读写锁的基本使用

//定义读写锁
        ReadWriteLock rwLock = new ReentrantReadWriteLock();
        //获得读锁
        Lock readLock = rwLock.readLock();
        //获得写锁
        Lock writeLock = rwLock.writeLock();
        //读数据
        readLock.lock();
        try {
            //读取共享数据
        }finally {
            readLock.unlock(); //释放锁
        }
       //写数据
        writeLock.lock();
        try {
            //写共享数据
        }finally {
            writeLock.unlock(); //释放锁
        }
        

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值