最新消息: 快速上手IntelliJ IDEA常用快捷键,放开那个鼠标。
您现在的位置是:群英 > 网络安全 > 安全技术 >
如何编写lock锁?难不难?
CSDN发表于 2020-09-07 09:51 次浏览
         如何编写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
标签:lock锁怎么写
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
下一篇:没有了
相关信息推荐