java中Lock锁的应用简介

整体描述

说到java,就不能不提多线程和锁,这篇文章简单介绍一下java中Lock的几种常用的应用方式。先简单介绍一下锁。锁,顾名思义,就是可以把东西锁住,在java里,可以理解为把一段代码锁住,只能让一个线程访问并执行,其他线程想再执行这段代码,就要等前面的线程结束才可以。锁的应用场景有很多,具体可以用于:读写数据操作,请求第三方接口时要求不能并发请求等。

方法介绍

lock是java自带的类,包括几个方法,下面具体介绍一下。

1. void lock()

获取锁。如果没有线程再使用这个锁,即锁可用,则能正常获取到这个锁,就可以正常往下执行,如果其他线程在使用这个锁,即获取锁失败,就会在这里等待,直到其他线程使用完释放这个锁,才能继续执行。

2. boolean tryLock()

获得锁。和上面的方法的区别是,这个方法获取锁不会等待,如果锁可用立即返回true,如果锁不可用立即返回false;即不管锁可用不可用,都会立即返回锁的状态。

3. boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException

获得锁。和上面的方法的区别是,如果锁可用,立即返回true,否则会等待,等待时间期间锁变为可用状态,立即返回true,到达等待时间,返回false。

4. void unlock()

释放锁。使用之后,必须手动释放锁,否则其他线程就永远无法访问了。所以释放锁的操作要放在finally块中进行,以保证锁一定会被释放,防止死锁的发生。

代码演示

先介绍了最简单的Lock,这里写几段代码Demo,根据执行结果,更加好理解锁的原理。

1. 基础代码

先写一个Test类,用于测试

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockTest {

    public static final String FULL_TIME_PATTERN_ALL = "yyyy-MM-dd HH:mm:ss:SSS";
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) {

        for (int i = 1; i < 4; i++) {
            Integer finalI = i;
            new Thread(() -> {
                LockTest(finalI);
            }, finalI.toString()).start();
        }
    }

    public static String getDateFormat(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FULL_TIME_PATTERN_ALL, Locale.CHINA);
        return simpleDateFormat.format(date);
    }
}

1. lock()

public static void LockTest(Integer id) {
        System.out.println(getDateFormat(new Date()) + "-准备执行:" + id);
        lock.lock();
        try {
            System.out.println(getDateFormat(new Date()) + "-开始执行:" + id);
            Thread.sleep(2000L);
        } catch (Exception e) {
            System.out.println(getDateFormat(new Date()) + "-执行出错:" + e.toString());
        } finally {
            lock.unlock();
            System.out.println(getDateFormat(new Date()) + "-成功执行:" + id);
        }
    }

运行结果:
运行结果1
根据打印的时间可以看出,三个线程同时调用锁,先是线程1获得了锁,并执行,2秒之后执行完成,线程1释放锁,之后线程2获得了锁,然后是线程3,和我们预想的一样。

2. boolean tryLock()

public static void LockTest(Integer id) {
        System.out.println(getDateFormat(new Date()) + "-准备执行:" + id);
        boolean flag = lock.tryLock();
        if (flag) {
            try {
                System.out.println(getDateFormat(new Date()) + "-开始执行:" + id);
                Thread.sleep(2000L);
            } catch (Exception e) {
                System.out.println(getDateFormat(new Date()) + "-执行出错:" + e.toString());
            } finally {
                lock.unlock();
                System.out.println(getDateFormat(new Date()) + "-成功执行:" + id);
            }
        } else {
            System.out.println(getDateFormat(new Date()) + "-成功失败:" + id);
        }
    }

运行结果:
运行结果2
根据打印的时间可以看出,三个线程同时调用锁,先是线程1获得了锁,并执行,线程2和线程3都是立即返回获取锁失败,都执行失败了,线程1两秒之后执行完成,和我们预想的一样。

3. boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException

public static void LockTest(Integer id) {
        System.out.println(getDateFormat(new Date()) + "-准备执行:" + id);
        boolean flag = false;
        try {
            flag = lock.tryLock(3000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            System.out.println(getDateFormat(new Date()) + "-执行出错:" + e.toString());
        }
        if (flag) {
            try {
                System.out.println(getDateFormat(new Date()) + "-开始执行:" + id);
                Thread.sleep(2000L);
            } catch (Exception e) {
                System.out.println(getDateFormat(new Date()) + "-执行出错:" + e.toString());
            } finally {
                lock.unlock();
                System.out.println(getDateFormat(new Date()) + "-成功执行:" + id);
            }
        } else {
            System.out.println(getDateFormat(new Date()) + "-执行失败:" + id);
        }
    }

运行结果:
运行结果3
根据打印的时间可以看出,三个线程同时调用锁,先是线程1获得了锁,并执行,其他两个线程获取锁失败,但是由于我们设置了等待时间是3秒,等过两秒之后线程1执行完成,线程2开始执行,等过三秒之后,由于到达等待时间,线程3执行失败,之后线程2执行完成,和我们预想的一样。

简单总结一下,这三种方式各有各的用处,在使用的时候,可以按需选择。下面再介绍一个ReadWriteLock。

读写锁;

ReadWriteLock。用于读写文件操作,定义的方法为readLock()和writeLock()。一个用来获取读锁,一个用来获取写锁。由于读写操作的特殊性,度操作可以多线程同时访问,但是有读操作的时候就不可以写,而写操作只能允许一个线程操作,而且写操作进行时,也不能进行读取操作。
用法和上述Lock的用法差不多,就不再单独写Demo了。

和synchronized关键字的比较;

synchronized是java的一个关键字,也有锁的作用,但是用法比较局限,也有很多synchronized和lock的比较,这里也简单说一下。

synchronizedlock
类型关键字抽象类
用法用法单一用法丰富,上文中的tryLock()等方法都是synchronized 不具备的
释放自动释放必须手动释放,否则会出现死锁的问题
效率较低在调用频繁的情况下,效率会比synchronized 高很多
状态无法获取锁状态可以通过Lock得知线程有没有成功获取到锁
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值