如何编写lock锁?难不难?如何写一个简单的lock?
非线程安全的计数
我们编写一个小程序,使用100个线程,循环100次累加,我们期望的计算结果是10000
定义一个NoLockAdd 类,代码如下:
/**
* 无锁的累加
* @author yangyanping
* @date 2020-09-04
*/
public class NoLockAdd {
private int sum;
public int add() {
return sum++;
}
public int getSum() {
return sum;
}
}
编写一个main方法,开启100个线程,循环100次调用NoLockAdd的add方法
public class TestLock {
public static void main(String[] args) throws Exception {
testNoLock();
}
private static void testNoLock() throws Exception {
NoLockAdd noLockAdd = new NoLockAdd();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int k = 0; k < 100; k++) {
noLockAdd.add();
Thread.sleep(1);
}
} catch (Exception ex) {
}
}
});
thread.start();
}
Thread.sleep(5000);
System.out.println(noLockAdd.getSum());
}
}
运行main方法,打印的结果为9851,并不是我们期望的结果值10000,为什么呢?
9851
Process finished with exit code 0
我们在idea 的Terminal 窗口中使用javap 命令 看下NoLockAdd java的字节码
javap -p -c NoLockAdd.class
Compiled from "NoLockAdd.java"
public class com.yyp.redis.lock.NoLockAdd {
public com.yyp.redis.lock.NoLockAdd();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public int add();
Code:
0: aload_0
1: dup
2: getfield #2 // Field sum:I
5: dup_x1
6: iconst_1
7: iadd
8: putfield #2 // Field sum:I
11: ireturn
public int getSum();
Code:
0: aload_0
1: getfield #2 // Field sum:I
4: ireturn
}
我们主要看 add() 方法的字节码,发现一条语句 sum++ 被编译了 多条 指令,可见sum++并不是一个原子操作。
指令 描述
aload_0 从局部变量0中装载引用类型值入栈。
iconst_1 1(int)值入栈。
iadd 将栈顶两int类型数相加,结果入栈。
ireturn 返回int类型值。
使用AtomicInteger计数
我们使用java 并发包里的原子类操作AtomicInteger 来进行累加。
定义AtomicAdd类
/**
* AtomicInteger的累加
* @author yangyanping
* @date 2020-09-04
*/
public class AtomicAdd {
private AtomicInteger sum = new AtomicInteger(0);
public void add() {
sum.incrementAndGet();
}
public int getSum() {
return sum.get();
}
}
编写main 方法 测试AtomicAdd 的累加
public class TestLock {
public static void main(String[] args) throws Exception {
testAtomicAdd();
}
private static void testAtomicAdd() throws Exception{
AtomicAdd atomicAdd = new AtomicAdd();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int k = 0; k < 100; k++) {
atomicAdd.add();
Thread.sleep(1);
}
} catch (Exception ex) {
}
}
});
thread.start();
}
Thread.sleep(5000);
System.out.println(atomicAdd.getSum());
}
}
运行结果为 10000,和我们期望的结果一致
10000
Process finished with exit code 0
使用synchronized关键字计数
定义SysAdd 类,在add 方法上添加 synchronized 关键字
/**
* synchronized
* @author yangyanping
* @date 2020-09-04
*/
public class SysAdd {
private int sum;
public synchronized void add() {
sum++;
}
public int getSum() {
return sum;
}
}
编写main 方法 测试SysAdd 的累加
public class TestLock {
public static void main(String[] args) throws Exception {
testAtomicAdd();
}
private static void testAtomicAdd() throws Exception{
SysAdd sysAdd = new SysAdd();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int k = 0; k < 100; k++) {
sysAdd.add();
Thread.sleep(1);
}
} catch (Exception ex) {
}
}
});
thread.start();
}
Thread.sleep(5000);
System.out.println(sysAdd.getSum());
}
}
运行结果为10000 和 我们期望的结果也一致:
10000
Process finished with exit code 0
使用Lock锁计数
我们使用java 并发包里的Lock 锁
LockAdd 类定义如下,也可以实现 安全的计数。
/**
* ReentrantLock 使用
* @author yangyanping
* @date 2020-09-04
*/
public class LockAdd {
private int sum;
private final Lock lock = new ReentrantLock();
public void add() {
lock.lock();
try {
sum++;
} finally {
lock.unlock();
}
}
public int getSum() {
return sum;
}
}
手写YypLock 锁
我们模仿ReentrantLock,自己动手写一个YypLock 锁。
/**
* 自定义锁
* @author yangyanping
* @date 2020-09-3
*/
public class YypLock implements Lock {
/**
* 独占资源所有者
*/
private AtomicReference<Thread> owner = new AtomicReference<>();
/**
* 等待的线程
*/
private LinkedBlockingQueue<Thread> queue = new LinkedBlockingQueue();
@Override
public void lock() {
//当前线程
Thread thread = Thread.currentThread();
//获取锁失败,进入循环
while (!owner.compareAndSet(null, thread)) {
//添加数据到等待队列
queue.offer(thread);
//线程等待
LockSupport.park();
//线程被唤醒后,从等待集合中移除
queue.remove(thread);
}
}
@Override
public void unlock() {
Thread thread = Thread.currentThread();
//释放锁
if (owner.compareAndSet(thread, null)) {
Iterator<Thread> iterator = queue.iterator();
while (iterator.hasNext()) {
Thread next = iterator.next();
if (next != null) {
//唤醒线程
LockSupport.unpark(next);
}
}
@Override
public void lockInterruptibly() throws InterruptedException {
}
@Override
public boolean tryLock() {
return false;
}
@Override
public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
return false;
}
@Override
public Condition newCondition() {
return null;
}
}
定义YypLockAdd 类,使用YypLock 锁
/**
* 使用自定义的Lock锁
* @author yangyanping
* @date 2020-09-04
*/
public class YypLockAdd {
private int sum;
private final YypLock lock = new YypLock();
public void add() {
lock.lock();
try {
sum++;
} finally {
lock.unlock();
}
}
public int getSum() {
return sum;
}
}
我们使用YypLock锁,测试并发计算的结果sum=10000
public class TestLock {
public static void main(String[] args) throws Exception {
testAtomicAdd();
}
private static void testAtomicAdd() throws Exception{
YypLockAdd yypLockAdd = new YypLockAdd();
for (int i = 0; i < 100; i++) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
for (int k = 0; k < 100; k++) {
yypLockAdd.add();
Thread.sleep(1);
}
} catch (Exception ex) {
}
}
});
thread.start();
}
Thread.sleep(5000);
System.out.println(yypLockAdd.getSum());
}
}
运行程序,输出如下:
10000
Process finished with exit code 0