Lock与Synchronized小结

本文详细对比了synchronized和ReentrantLock两种锁机制的特点与应用场景,通过具体实例展示了如何使用ReentrantLock实现更细粒度的并发控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【区别】synchronized:对象锁,调用synchronized方法的线程取得该对象锁后,其他线程需要等待该线程释放该对象锁后才能进入该方法。若是持有不同的对象的线程,则可以同时进入该方法。注意:类锁(全局锁)synchronized(A.getClass)与对象锁synchronized(this)的区别【特点】隐式锁,锁的范围是整个方法或代码块中,离开该方法/块后自动释放锁

2.重入锁:ReentrantLock:更灵活的锁机制,提供可轮询和可中断、可定时的锁获取机制。持有该锁的线程可以重复进入该方法(count加1,但只执行一次Method),可以实现更细微的锁颗粒。【特点】需要显式的获取锁和释放锁

使用事例:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds  <span style="font-family: Arial, Helvetica, sans-serif;">一次只能有一个线程进入,锁的颗粒较大</span>
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
锁的颗粒对并发的控制作用是明显的,例如两个线程对相同的一个属性进行操作,A转账B的两个线程中,一个线程需要等待另一个线程释放该锁才能进入方法,这样是没有问题的。但是两个线程一个是A转账B,一个是C转账D,上面的锁的方法也会等待一个线程执行完毕释放锁后,另一个线程才能获取锁然后执行,这样就比较慢了,线程只能排队执行,不能并发执行。因此需要改进锁实现方法,实现线程合理的并发控制。实现方法(属性上加锁操作,减少锁的颗粒):
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
/**
 * @Author : hd
 * @Date : 2016/8/18 16:27
 * @Version : 1.0.0
 */
/**
 * 分段锁,系统提供一定数量的原始锁,根据传入对象的哈希值获取对应的锁并加锁
 * 注意:要锁的对象的哈希值如果发生改变,有可能导致锁无法成功释放!!!
 */
public class SegmentLock<T> {

    private Integer segments = 16;//默认分段数量
    private final Map<Integer, ReentrantLock> lockMap = new HashMap<>();

    public SegmentLock() {
        init(null, false);
    }

    public SegmentLock(Integer counts, boolean fair) {
        init(counts, fair);
    }

    private void init(Integer counts, boolean fair) {
        if (counts != null) {
            segments = counts;
        }
        for (int i = 0; i < segments; i++) {
            lockMap.put(i, new ReentrantLock(fair));
        }
    }

    public void lock(T key) {
        ReentrantLock lock = lockMap.get(key.hashCode() % segments);
        lock.lock();
    }

    public void unlock(T key) {
        ReentrantLock lock = lockMap.get(key.hashCode() % segments);
        lock.unlock();
    }
}
另一个事例:三个线程同时进入该锁的情况
public class Test {
    //public static volatile int i=10;
    static int i=10;
    @org.junit.Test
    public void testReenTrant(){
        final  SegmentLock<String> segmentLock=new SegmentLock<String>();
        final String key="2016";
        for(int j=0;j<3;j++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try{
                        segmentLock.lock(key);
                        i=i-1;
                        System.out.println("lock i:"+i);
                    }finally {
                        segmentLock.unlock(key);
                    }
                    System.out.println("unlock i:"+i);
                }
            }).start();
        }
    }
}
/**
 结果:lock i:9
 unlock i:9
 lock i:8
 unlock i:8
根据结果可知:一个线程获取锁进入方法执行后,释放了该锁,另外两个线程重复获取到了锁

synchronized例子:

class Sync {  
  
    public synchronized void test() {  
        System.out.println("test开始..");  
        try {  
            Thread.sleep(1000);  
        } catch (InterruptedException e) {  
            e.printStackTrace();  
        }  
        System.out.println("test结束..");  
    }  
}  
  
class MyThread extends Thread {  
  
    public void run() {  
        Sync sync = new Sync();  
        sync.test();  
    }  
}  
  
public class Main {  
  
    public static void main(String[] args) {  
        for (int i = 0; i < 3; i++) {  
            Thread thread = new MyThread();  
            thread.start();  
        }  
    }  
}  
运行结果://持有不同对象锁 的线程同时进入
test开始..
test开始..
test开始..
test结束..
test结束..
test结束..

class MyThread extends Thread {  
  
    private Sync sync;  
  
    public MyThread(Sync sync) {  
        this.sync = sync;  
    }  
  
    public void run() {  
        sync.test();  
    }  
}  
  
public class Main {  
  
    public static void main(String[] args) {  
        Sync sync = new Sync();  
        for (int i = 0; i < 3; i++) {  
            Thread thread = new MyThread(sync);  
            thread.start();  
        }  
    }  
}  
运行结果://持有相同对象锁 的线程要等另一个线程的对象锁释放才能进入
test开始..
test结束..
test开始..
test结束..
test开始..
test结束..
class Sync {  
  
    public void test() {  
        synchronized (Sync.class) {  
            System.out.println("test开始..");  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            System.out.println("test结束..");  
        }  
    }  
}  
  
class MyThread extends Thread {  
  
    public void run() {  
        Sync sync = new Sync();  
        sync.test();  
    }  
}  
  
public class Main {  
  
    public static void main(String[] args) {  
        for (int i = 0; i < 3; i++) {  
            Thread thread = new MyThread();  
            thread.start();  
        }  
    }  
}  
运行结果://对象类锁,无论创建了多少对象都是属于这个类,需要等待这个线程释放锁,其他线程才能进入,全局锁
test开始..
test结束..
test开始..
test结束..
test开始..
test结束..

【总结】开发中,常常需要对锁的粒度对象细化来实现符合逻辑并发控制,ReentrantLock实现了区别于Synchronized的另一种锁的机制,正如api解释的一样

A reentrant mutual exclusion Lock with the same basic behavior and semantics as the implicit monitor lock accessed using synchronized <span style="font-family: Arial, Helvetica, sans-serif;">methods and statements, but with extended capabilities.</span>

和使用synchronized(隐式监视器锁)和语句访问具有相同的基本行为和语义的重入的互斥锁,但功能更强大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值