Java锁与CountDownLatch
为什么需要锁?
-
多任务环境中才需要
-
任务都需要对同一共享资源进行写操作
-
对资源的访问是互斥的
比如说现在我们正在抢购某店铺的一个限量的球鞋,抢购时间是22.00整,只有50件的数量。现在有大于10000的人准备去抢这件商品,我们程序里面肯定会为成功抢购的消费者创建一个唯一的订单号。看一下我们程序中的订单生成器
其中应用了一个非常实用的多线程控制工具类CountDownLatch。常用的就下面几个方法: -
CountDownLatch(int count) //实例化一个倒计数器,count指定计数个数
-
countDown() // 计数减一
-
await() //等待,当计数减到0时,所有线程并发执行
再没有加锁的情况下运行结果
所以在这一类存在线程安全问题的情况下我们需要对有多线程竞争的资源进行同步处理,一般有两种方式lock和synchronized,
LOCK是接口
synchornized是关键字
使用角度:Lock需要获取锁,释放锁;synchornized自动获取锁和释放锁
性能角度:线程较多时,Lock使用更灵活,性能相对好;线程少时,synchornized性能好。
为什么说lock锁使用起来相对灵活呢?因为lock锁含有多种功能的方法。
//获取锁
void lock();
//获取锁过程中可以响应中断
void lockInterruptibly() throws InterruptedException;
//非阻塞式响应能够马上返回,获取锁返回true,反之false
boolean tryLock();
//超时获取锁,在超时time内或未中断情况下,可以获取锁
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放锁
void unlock();
//获取与lock绑定的等待通知组件,当前线程必须获取锁才能进行等待//等待时释放锁,当再次获取锁才能从等待中返回
Condition newCondition();
总结的来说
1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;
2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直等待下去,不能够响应中断;
4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
5)Lock可以提高多个线程进行读操作的效率。
在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。