zoukankan      html  css  js  c++  java
  • synchronized和ReentrantLock

     多用例子测试,写一下,很好理解。

    synchronized和ReentrantLock都是什么?

    可参考

    https://stackoverflow.com/questions/11821801/why-use-a-reentrantlock-if-one-can-use-synchronizedthis

    https://juejin.im/post/5aeb0a8b518825673a2066f0

    https://juejin.im/post/594a24defe88c2006aa01f1c

    这是一个线程不安全的例子,因为每个线程都在公用一个state,并且没有锁

    public class ThreadSafeSimple {
    
        public int sharedState;
        public void noSafeAction() {
            while (sharedState < 10000) {
                int former = sharedState++;
                int latter = sharedState;
                if (former != latter - 1) {
                    System.out.println("former " + former + " latter " + latter);
                }
            }
        }
    
        public static void main(String[] args) {
            ThreadSafeSimple simple = new ThreadSafeSimple();
            Thread threadA = new Thread(() -> simple.noSafeAction());
    
            Thread threadB = new Thread(() -> simple.noSafeAction());
    
            threadA.start();
            threadB.start();
    
        }
    }

    加上一个方法锁,也叫同步代码块;还有一种在非静态方法上加synchronized。显然高并发并不合适

        public void noSafeAction() {
            while (sharedState < 10000) {
                synchronized (this) {
                    int former = sharedState++;
                    int latter = sharedState;
                    if (former != latter - 1) {
                        System.out.println("former " + former + " latter " + latter);
                    }
                }
    
            }
        }

    再来看看ReentrantLock。什么是再入?它是表示当一个线程试图获取一个它已经获取的锁时,这个获取动作就自动成功,这是对锁获取粒度的一个概念,也就是锁的持 有是以线程为单位而不是基于调用次数。Java锁实现强调再入性是为了和pthread的行为进行区分。

    再入锁可以设置公平性(fairness),我们可在创建再入锁时选择是否是公平的。

    ReentrantLock fairLock = new ReentrantLock(true);

    这里所谓的公平性是指在竞争场景中,当公平性为真时,会倾向于将锁赋予等待时间最久的线程。公平性是减少线程“饥饿”(个别线程长期等待锁,但始终无法获取)情况发生的一 个办法。

    如果使用synchronized,我们根本无法进行公平性的选择,其永远是不公平的,这也是主流操作系统线程调度的选择。通用场景中,公平性未必有想象中的那么重要,Java默认的调 度策略很少会导致 “饥饿”发生。与此同时,若要保证公平性则会引入额外开销,自然会导致一定的吞吐量下降。所以,我建议只有当你的程序确实有公平性需要的时候,才有必要指 定它。

    什么叫可重入,举个例子

    Lock lock = new ReentrantLock();
    
    long f(int n) throws InterruptedException
    {
        lock.lock();
        try
        {
            if (n <= 1) return 1;
            return f(n - 1) + 1;
        }
        finally {
            lock.unlock();
        }
    }

    The reentrantlock will return 2, if you call f(2)

    实现原理

    final boolean nonfairTryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        //1. 如果该锁未被任何线程占有,该锁能被当前线程获取
        if (c == 0) {
            if (compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        //2.若被占有,检查占有线程是否是当前线程
        else if (current == getExclusiveOwnerThread()) {
            // 3. 再次获取,计数加一
            int nextc = c + acquires;
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
    }

    释放锁

    protected final boolean tryRelease(int releases) {
        //1. 同步状态减1
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
        boolean free = false;
        if (c == 0) {
            //2. 只有当同步状态为0时,锁成功被释放,返回true
            free = true;
            setExclusiveOwnerThread(null);
        }
        // 3. 锁未被完全释放,返回false
        setState(c);
        return free;
    }

    如何实现公平锁

    protected final boolean tryAcquire(int acquires) {
        final Thread current = Thread.currentThread();
        int c = getState();
        if (c == 0) {
            if (!hasQueuedPredecessors() &&
                compareAndSetState(0, acquires)) {
                setExclusiveOwnerThread(current);
                return true;
            }
        }
        else if (current == getExclusiveOwnerThread()) {
            int nextc = c + acquires;
            if (nextc < 0)
                throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
        }
        return false;
      }
    }

    这里主要是hasQueuedPredecessors(),这个主要是避免刚释放锁的线程又取到,for公平。如果强调公平锁,那就不可避免的必须频繁切换线程,上下分切换很费时间的,所以不如在逻辑上控制。

    synchronized实例测试

    synchronized(this|object) {} 代码块和 synchronized 修饰非静态方法获取的锁是同一个锁,即该类的对象的对象锁。

    两个线程访问不同对象的 synchronized(this|object) {} 代码块和 synchronized 修饰非静态方法是异步的,同一个类的不同对象的对象锁互不干扰。

    在同一对象的情况下,synchronized(类.class) {} 代码块或 synchronized 修饰静态方法和 synchronized(this|object) {} 代码块和 synchronized 修饰非静态方法的行为一致。

    两个线程访问不同对象的 synchronized(类.class) {} 代码块或 synchronized 修饰静态方法还是同步的,类中 synchronized(类.class) {} 代码块和 synchronized 修饰静态方法获取的锁是类锁。对于同一个类的不同对象的类锁是同一个。
    对象锁和类锁是独立的,互不干扰。
     
     
    一个没有高级趣味的人。 email:hushui502@gmail.com
  • 相关阅读:
    Ganglia Install
    [ZZ]perl一揽子特殊变量
    点滴积累【C#】---C#实现上传word将路径保存到数据库,文件保存到服务器。并且按照名称读取服务器的word
    点滴积累【C#】---C#实现上传照片到物理路径,并且将地址保存到数据库,
    点滴积累【C#】---C#实现上传word以流形式保存到数据库和读取数据库中的word文件。
    点滴积累【C#】---将Excel数据导入到数据库
    点滴积累【C#】---TreeView读取数据库
    DIV内英文或者数字不换行的问题 解决办法
    Rabbitmq中rabbitmqctl的常用命令
    Uploadify参数详解
  • 原文地址:https://www.cnblogs.com/CherryTab/p/12177995.html
Copyright © 2011-2022 走看看