Lock与Synchronized小结

来源:互联网 发布:淘宝优惠券卖家发放 编辑:程序博客网 时间:2024/04/28 06:49

【区别】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(隐式监视器锁)和语句访问具有相同的基本行为和语义的重入的互斥锁,但功能更强大。

0 0