并发学习笔记——Synchronized & ReentrantLock

并发学习笔记——Synchronized & ReentrantLock,第1张

并发学习笔记——Synchronized & ReentrantLock 0 互斥同步

同步:保证共享数据在同一时刻只被一个(使用信号量的时候可以是多个)线程使用
互斥:互斥是实现同步的手段,同步是互斥要实现的目的
*** 作系统层面的概念:

  • 临界区
  • 互斥量
  • 信号量
1 Synchronized 1.1. 修饰代码块
  • synchronized关键字经过编译后会在代码块(同步块)的前后形成monitorenter、monitorexit两个字节码指令,这两个字节码都需要一个reference类型的参数来指明要锁定和解锁的对象;
  • 执行monitorenter,如果获取到对象的锁或者当前线程已经持有锁,锁计数器+1
    • 如果获取对象锁失败,线程阻塞等待,直到其他线程执行monitorexit指令,释放锁资源
    • 阻塞或唤醒一个线程,都需要 *** 作系统从用户态转换为核心态,会消耗很多系统资源,所以synchronized是重量级锁
  • 执行monitorexit 锁计数器 -1

    下面描述来自 https://www.hollischuang.com/archives/1883
1.1.2 修饰方法
  • 方法级的同步是隐式的,运行时常量池中会有一个ACC_SYNCHRONIZED标志。
  • 当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,
  • 如果有设置ACC_SYNCHRONIZED标志,需要先获取锁,然后开始执行方法,方法执行之后再释放锁。
  • 这时如果其他线程来请求执行方法,会因为无法获得锁而被阻塞。
  • 如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前锁会被自动释放。
2 ReentrantLock 2.0 AQS

https://blog.csdn.net/hduzjn/article/details/121420595
package java.util.concurrent.locks

2.1 Lock
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;

public interface Lock {
    // try
    void lock();
    // finally
    void unlock();
    // 获取锁的过程中可中断 synchronized不可以
    void lockInterruptibly() throws InterruptedException;
    // Acquires the lock if it is available and returns immediately with the value true. 
    // If the lock is not available then this method will return immediately with the value false
    // This usage ensures that the lock is unlocked if it was acquired, 
    // and doesn't try to unlock if the lock was not acquired.
    // Returns : true if the lock was acquired and false otherwise
    // 
    boolean tryLock();
   // 根据传入的时间段获取锁,在指定时间内没有获取锁则返回false,
   // 如果在指定时间内当前线程未被中并断获取到锁则返回true
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
  
    Condition newCondition();
}
2.2 Condition 接口

Where a Lock replaces the use of synchronized methods and statements, a Condition replaces the use of the Object monitor methods.

  • 替代同步方法和状态,以及监视器方法 synchronized/wait/notify/notifyAll
  • await 释放锁
  • signal 获取锁

A Condition instance is intrinsically bound to a lock. To obtain a Condition instance for a particular Lock instance use its newCondition() method.

  • Condition和锁绑定,配合锁才能使用,通过 lock.newCondition() 获得Condition实例
public interface Condition {

    
    void await() throws InterruptedException;

    
    void awaitUninterruptibly();
    
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    void signal();
    
    void signalAll();
}
2.2.1 使用例子
 * class BoundedBuffer {
 *   final Lock lock = new ReentrantLock();
 *   final Condition notFull  = lock.newCondition(); 
 *   final Condition notEmpty = lock.newCondition(); 
 *
 *   final Object[] items = new Object[100];
 *   int putptr, takeptr, count;
 *
 *   public void put(Object x) throws InterruptedException {
 *     lock.lock();
 *     try {
 *       while (count == items.length)
 *        notFull.await();
 *       items[putptr] = x;
 *       if (++putptr == items.length) putptr = 0;
 *       ++count;
 *       notEmpty.signal();
 *     } finally {
 *       lock.unlock();
 *     }
 *   }
 *
 *   public Object take() throws InterruptedException {
 *    lock.lock();
 *     try {
 *       while (count == 0)
 *         notEmpty.await();
 *       Object x = items[takeptr];
 *       if (++takeptr == items.length) takeptr = 0;
 *       --count;
 *       notFull.signal();
 *       return x;
 *      } finally {
 *       lock.unlock();
 *     }
 *   }
 * }
 * 
2.2.2 ConditionObject Condition Queue
        private static final long serialVersionUID = 1173984872572414699L;
        
        private transient Node firstWaiter;
        
        private transient Node lastWaiter;

https://www.cnblogs.com/zerotomax/p/8969416.html
https://blog.csdn.net/zy1994hyq/article/details/84562475

2.3 ReentrantLock 2.3.1 构造方法
  • 默认是非公平锁
2.3.2 上锁/解锁

调用内部的sync的方法

public void lock() {
        sync.lock();
    }

public boolean tryLock() {
        return sync.nonfairTryAcquire(1);
    }
    
public void unlock() {
        sync.release(1);
    }
    
public Condition newCondition() {
        return sync.newCondition();
    }
2.3.3 Sync / NoFairSync / FairSync ① Sync nonfairTryAcquire
  • 可重入的逻辑
        
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            // 锁没被占用 CAS 获取锁 
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            // 判断获取锁的线程 是否是当前占用锁的线程
            else if (current == getExclusiveOwnerThread()) {
            // 如果是  state =state + acquires 
            // 可重入在此展现
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
② NonfairSync 非公平锁
 static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        
        final void lock() {
            // 判断state是否是0(表示当前锁未被占用),如果是0则把它置为1,然后设置独占线程为当前线程
            // 非公平的体现 ,占用锁的线程释放了锁,state状态变成0,此时如果有一个新来的线程正好调用lock方法,
            // 那么它会在 wait queue队列 中等待的线程 之前获得锁
            if (compareAndSetState(0, 1))
            
                setExclusiveOwnerThread(Thread.currentThread());
            else
            // 否则执行 AQS 的 acquire方法
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
③ FairSync 公平锁
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;
        // 这里没有 if else判断 state的状态,然后获取锁
        final void lock() {
        // 直接调用 acquire ,这样所有线程都要在 wait queue 里排队
            acquire(1);
        }

        
        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;
        }
    }

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5606057.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存