synchronized 和 Lock的区别
- synchronized是java中的一个关键字,也就是说是Java语言内置的特性。
- Lock不是Java语言内置的,Lock是一个接口。
- Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。
synchronized 锁方法, 这个方法将不能被同时访问。
synchronized 锁对象,这个对象将不能被同时访问。
synchronized 锁类,这个类的所有实例对象将不能被同时访问。
当synchronized方法或者synchronized代码块执行完之后,系统会自动让线程释放对锁的占用
synchronized 抢单的实现
- 首先对于抢单我们不能锁住整个方法,我们应该锁住的是单号。单号是字符串,intern() 方法 返回值:一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。只有当jvm中没有对常量池中此字符串的引用并没有任何对象的字面值等于此字符串时,此字符串才会被gc回收。在此之前所有intern() 方法返回的字符串均是同一个对象
// 订单类
class OrderDemo {
private String name;
private String orderNum;
public OrderDemo(String name, String orderNum) {
this.name = name;
this.orderNum = orderNum;
}
public String getOrderNum() {
return orderNum;
}
public String getName() {
return name;
}
}
// 业务方法
// String 的 intern() 方法的返回值 一个字符串,内容与此字符串相同,但一定取自具有唯一字符串的池。
public static void test(OrderDemo orderDemo) {
synchronized (orderDemo.getOrderNum().intern()){
System.out.println(orderDemo.getName() + "获得了锁");
System.out.println(orderDemo.getName() + "释放了锁");
}
}
// 测试方法
public static void main(String[] a){
OrderDemo orderDemo = new OrderDemo("线程1","001002003");
Thread thread = new Thread(() -> test(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("线程2","001002003");
Thread thread2 = new Thread(() -> test(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("线程3","001002003");
Thread thread3 = new Thread(() -> test(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("线程4","001002004");
Thread thread4 = new Thread(() -> test(orderDemo4));
thread.start();
thread2.start();
thread3.start();
thread4.start();
}
打印结果如下
线程1获得了锁
线程4获得了锁
线程4释放了锁
线程1释放了锁
线程3获得了锁
线程3释放了锁
线程2获得了锁
线程2释放了锁
- 可以看到,对于相同订单号(001002003)的订单,会锁住订单号,一直等到锁释放。下一个订单才可以进行处理。
- 对于订单号不同的订单会并行执行。
Lock
方法 | 说明 | 参数说明 |
---|---|---|
lock() | 获得锁 | |
tryLock() | 返回boolean, ture代表锁没有被占用,获得锁。false代表锁被占用 | |
tryLock(long time, TimeUnit unit) | 返回boolean, 等待time时长后,锁还没有被释放。返回false。time时长内释放了返回true, 并获得锁 | time:时长, unit:时间类型 |
lockInterruptibly() | 获得锁,和lock()不同,举例:当A线程处于等待时(对执行中的线程无效),其余的线程可调用A线程的interrupt()来中断A线程,A线程直接返回,并抛出InterruptException异常。如果是lock()的话,A线程还是会继续等待并执行,只是给A线程做了个中断标志 | |
unlock() | 释放锁 |
lock()
// 业务测试
public static void test2(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
try {
System.out.println(orderDemo.getName()+"得到了锁");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(orderDemo.getName()+"释放了锁");
lock.unlock();
}
}
// 测试
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("线程1","001002003");
Thread thread = new Thread(() -> test2(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("线程2","001002003");
Thread thread2 = new Thread(() -> test2(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("线程3","001002003");
Thread thread3 = new Thread(() -> test2(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("线程4","001002004");
Thread thread4 = new Thread(() -> test2(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("线程5","001002004");
Thread thread5 = new Thread(() -> test2(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印如下
线程1得到了锁
线程5得到了锁
线程5释放了锁
线程4得到了锁
线程1释放了锁
线程3得到了锁
线程3释放了锁
线程4释放了锁
线程2得到了锁
线程2释放了锁
1. 将订单号和Lock 进行绑定。确保相同的单号共享一个锁。
2. 可以看到,对于相同订单号的订单,会锁住订单号,一直等到锁释放。下一个订单才可以进行处理。
3. 对于订单号不同的订单会并行执行。
tryLock()
// 业务方法
public static void test3(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
if (lock.tryLock()) {
try {
System.out.println(orderDemo.getName() + "得到了锁");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "释放了锁");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "获取锁失败, 锁已被占用");
}
}
// 测试方法
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("线程1","001002003");
Thread thread = new Thread(() -> test3(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("线程2","001002003");
Thread thread2 = new Thread(() -> test3(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("线程3","001002003");
Thread thread3 = new Thread(() -> test3(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("线程4","001002004");
Thread thread4 = new Thread(() -> test3(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("线程5","001002004");
Thread thread5 = new Thread(() -> test3(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印如下
线程1得到了锁
线程4得到了锁
线程2获取锁失败, 锁已被占用
线程5获取锁失败, 锁已被占用
线程3获取锁失败, 锁已被占用
线程4释放了锁
线程1释放了锁
我们可以看到线程1得到了锁,而和它共享锁的线程2和线程3都没有得到锁。
线程4得到了锁,而和它共享锁的线程5没有得到锁。
tryLock(long time, TimeUnit unit)
// 业务方法
public static void test4(OrderDemo orderDemo){
Lock lock = map.get(orderDemo.getOrderNum());
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
System.out.println(orderDemo.getName() + "得到了锁");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "释放了锁");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "获取锁失败, 锁已被占用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 测试方法
public static void main(String[] a){
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
Lock lock2 = new ReentrantLock();
map.put("001002004", lock2);
OrderDemo orderDemo = new OrderDemo("线程1","001002003");
Thread thread = new Thread(() -> test4(orderDemo));
OrderDemo orderDemo2 = new OrderDemo("线程2","001002003");
Thread thread2 = new Thread(() -> test4(orderDemo2));
OrderDemo orderDemo3 = new OrderDemo("线程3","001002003");
Thread thread3 = new Thread(() -> test4(orderDemo3));
OrderDemo orderDemo4 = new OrderDemo("线程4","001002004");
Thread thread4 = new Thread(() -> test4(orderDemo4));
OrderDemo orderDemo5= new OrderDemo("线程5","001002004");
Thread thread5 = new Thread(() -> test4(orderDemo5));
thread.start();
thread2.start();
thread3.start();
thread4.start();
thread5.start();
}
打印
线程1得到了锁
线程4得到了锁
线程4释放了锁
线程1释放了锁
线程5得到了锁
线程2得到了锁
线程3获取锁失败, 锁已被占用
线程5释放了锁
线程2释放了锁
1. 程序中写了每个线程最少会锁3秒的时间。但线程只会等待五秒。现在线程(1,2,3)共享一个锁,线程(4,5)共享一个锁。可以猜到,线程(1,2,3)会只有两个会获取到锁,而线程(4,5)会都获得锁。而打印也证实了我们的猜测。
lockInterruptibly()
// 业务
public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Lock lock = map.get(orderDemo.getOrderNum());
lock.lockInterruptibly();
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "得到了锁");
try {
// 判断线程是否被中断
if (!thread.isInterrupted()) {
Thread.sleep(2000);
} else {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中断");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "释放了锁");
lock.unlock();
}
}
// 测试方法
public static void main(String[] a) {
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
OrderDemo orderDemo = new OrderDemo("线程1", "001002003");
Thread thread = new Thread(() -> {
try {
test5(orderDemo, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo2 = new OrderDemo("线程2", "001002003");
Thread thread2 = new Thread(() -> {
try {
test5(orderDemo2, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo3 = new OrderDemo("线程3", "001002003");
Thread thread3 = new Thread(() -> {
try {
test5(orderDemo3, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread2.start();
thread3.start();
try {
Thread.sleep(1000);
thread3.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
打印
2019年08月28日 18:06:29:线程1得到了锁
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.lzq.collection.SynchronizedDemo.test5(SynchronizedDemo.java:130)
at com.lzq.collection.SynchronizedDemo.lambda$main$2(SynchronizedDemo.java:47)
at java.lang.Thread.run(Thread.java:745)
2019年08月28日 18:06:31:线程1释放了锁
2019年08月28日 18:06:31:线程2得到了锁
2019年08月28日 18:06:33:线程2释放了锁
此代码实现在1秒后中断线程3的线程。使用lockInterruptibly获取锁的话,线程3会直接中断返回并抛出InterruptedException异常。线程不会继续等待。
如果 lock.lockInterruptibly() 换成 lock.lock() 线程3不会被立即中断。还是会继续等待并执行。只是对线程3做了个中断状态。
换成lock.lock()打印如下
2019年08月28日 18:08:32:线程1得到了锁
2019年08月28日 18:08:34:线程1释放了锁
2019年08月28日 18:08:34:线程2得到了锁
2019年08月28日 18:08:36:线程2释放了锁
2019年08月28日 18:08:36:线程3得到了锁
2019年08月28日 18:08:36:线程3被中断
2019年08月28日 18:08:36:线程3释放了锁
可以看出线程3还是继续等待并执行,只是做了个中断状态
随堂代码
package com.lzq.collection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* 开发公司:个人
* 版权:个人
* <p>
* ListDemo
*
* @author 刘志强
* @created Create Time: 2019/8/26
*/
public class SynchronizedDemo {
static Map<String,Lock> map = new HashMap<>();
// public static void main(String[] a){
// Lock lock1 = new ReentrantLock();
// map.put("001002003", lock1);
// Lock lock2 = new ReentrantLock();
// map.put("001002004", lock2);
// OrderDemo orderDemo = new OrderDemo("线程1","001002003");
// Thread thread = new Thread(() -> test3(orderDemo));
// OrderDemo orderDemo2 = new OrderDemo("线程2","001002003");
// Thread thread2 = new Thread(() -> test3(orderDemo2));
// OrderDemo orderDemo3 = new OrderDemo("线程3","001002003");
// Thread thread3 = new Thread(() -> test3(orderDemo3));
// OrderDemo orderDemo4 = new OrderDemo("线程4","001002004");
// Thread thread4 = new Thread(() -> test3(orderDemo4));
// OrderDemo orderDemo5= new OrderDemo("线程5","001002004");
// Thread thread5 = new Thread(() -> test3(orderDemo5));
// thread.start();
// thread2.start();
// thread3.start();
// thread4.start();
// thread5.start();
// }
public static void main(String[] a) {
Lock lock1 = new ReentrantLock();
map.put("001002003", lock1);
OrderDemo orderDemo = new OrderDemo("线程1", "001002003");
Thread thread = new Thread(() -> {
try {
test5(orderDemo, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo2 = new OrderDemo("线程2", "001002003");
Thread thread2 = new Thread(() -> {
try {
test5(orderDemo2, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
OrderDemo orderDemo3 = new OrderDemo("线程3", "001002003");
Thread thread3 = new Thread(() -> {
try {
test5(orderDemo3, Thread.currentThread());
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread.start();
thread2.start();
thread3.start();
try {
Thread.sleep(1000);
thread3.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test(OrderDemo orderDemo) {
synchronized (orderDemo.getOrderNum().intern()){
System.out.println(orderDemo.getName() + "获得了锁");
System.out.println(orderDemo.getName() + "释放了锁");
}
}
public static void test2(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
try {
System.out.println(orderDemo.getName()+"得到了锁");
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}finally {
System.out.println(orderDemo.getName()+"释放了锁");
lock.unlock();
}
}
public static void test3(OrderDemo orderDemo) {
Lock lock = map.get(orderDemo.getOrderNum());
if (lock.tryLock()) {
try {
System.out.println(orderDemo.getName() + "得到了锁");
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "释放了锁");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "获取锁失败, 锁已被占用");
}
}
public static void test4(OrderDemo orderDemo){
Lock lock = map.get(orderDemo.getOrderNum());
try {
if (lock.tryLock(5, TimeUnit.SECONDS)) {
try {
System.out.println(orderDemo.getName() + "得到了锁");
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(orderDemo.getName() + "释放了锁");
lock.unlock();
}
} else {
System.out.println(orderDemo.getName() + "获取锁失败, 锁已被占用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void test5(OrderDemo orderDemo, Thread thread) throws InterruptedException {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
Lock lock = map.get(orderDemo.getOrderNum());
lock.lock();
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "得到了锁");
try {
// 判断线程是否被中断
if (!thread.isInterrupted()) {
Thread.sleep(2000);
} else {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "被中断");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(dateFormat.format(new Date()) + ":" + orderDemo.getName() + "释放了锁");
lock.unlock();
}
}
}
class OrderDemo {
private String name;
private String orderNum;
public OrderDemo(String name, String orderNum) {
this.name = name;
this.orderNum = orderNum;
}
public String getOrderNum() {
return orderNum;
}
public String getName() {
return name;
}
}