最近再一次接触到了一个熟悉的名词,分段锁,一篇文章中对于这个名词给出的介绍大意是使用多个锁来对分段后的数据进行加锁,而不是一个锁锁住所有数据。
然后我简单的思考了一下,如果说现在有一个数组长度为二十,将其分为两段前十个数据一段,后十个数据为另一段,然后当线程访问第一段数据时,使用第一个线程锁,以此类推。
应该大概也许是这个意思吧?但是,紧接着又提出了使用分段锁面临跨段访问的问题时,需要按顺序锁定所有段,按顺序释放锁,否则极可能出现死锁。
这一点就不太明白了,如果说跨段访问的话,加锁后及时释放为什么会引发死锁呢?就像是不同线程访问不同的资源,只要及时释放自己占用的资源就不会影响到另一个线程访问它当前占用的资源。
想不太明白,决定找篇文章学习一下。
首先是几个概念:
可伸缩性:当增加计算资源时(例如CPU,内存,存储容器或者IO带宽),程序的吞吐量或者处理能力会相应地增加。
这个概念说的应该是增加一些用于计算的资源后,程序处理数据的能力也要有所提升,比如如果只有一个cpu的情况下,每次执行一个线程,当增加一个cpu后每次可以执行两个线程。
但是如果给某个资源加了独占资源的线程锁,那么线程就必须串行,这就导致了计算资源增加,程序能力没有提升。
线程锁发生竞争时,会引发线程串行和上下文切换,前者降低可伸缩性,后者降低性能。
文章中介绍到,降低锁的竞争可以提升可伸缩性和降低性能,而导致锁竞争的因素包括锁的请求频率和持有时间。
仔细一想,挺合理的,如果请求频率高自然是比较可能发生多个线程竞争一个锁的情况,如果持有一个锁却长时间不释放,那么后续请求锁的线程会被阻塞。
然后,提到了锁分解和锁分段。
所谓的锁分解大意如下:
//分解前 public synchronized void method1() { } public synchronized void method2() { } //分解后 public void method1() { synchronized (A锁) { } } public void method2() { synchronized (B锁) { } }
synchronized关键字修饰方法使用的锁对象是this对象,也就是说即便在业务逻辑上两个方法可以同时执行,如果直接加关键字,也会导致串行。
验证代码如下:
public static synchronized void m1() { System.out.println("m1"); for(;;){} } public static synchronized void m2() { System.out.println("m2"); } public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { m1(); } }).start(); new Thread(new Runnable() { @Override public void run() { m2(); } }).start(); }
之后介绍到,这种技术适用于锁竞争不是很激烈的情况下。这一点也可以理解,将一个锁分解为多个锁后,如果线程竞争非常激烈,那么每一个分解后的锁也可能会面临激烈竞争的情况。
然后引出分段锁简单介绍了一下,然后就没有然后了... ...
又浏览了几篇文章问题还是没有解决,为什么跨段需要按顺序加锁解锁?否则为什么会引发死锁?