/** * 可重入内置锁 * 每一个Java对象均可以用作一个实现同步的锁,这些锁被称为内置锁或监视锁。线程在进入同步代码块以前会自动获取锁,而且在退出同步代码块时 * 会自动释放锁。获取内置锁的惟一途径是进入由这个锁保护的同步代码块或方法。 * “重入”意味着获取锁的操做的粒度是“线程”,而不适合调用。 * 同一个线程在调用本类中其它synchronized方法/块或父类中的synchronized方法/块时,都不会阻碍该线程地执行,由于互斥锁时可重入的。 */ public class Deadlock extends Object { private String objID; public Deadlock(String id) { objID = id; } public synchronized void checkOther(Deadlock other) { print("entering checkOther()"); try { Thread.sleep(2000); } catch (InterruptedException e) { } print("in checkOther() - about to" + "invoke 'other.action()'"); other.action(); print("leaving checkOther()"); } public synchronized void action() { print("entering action()"); try { Thread.sleep(500); } catch (InterruptedException e) { } print("leaving action()"); } public void print(String msg) { threadPrint("objID=" + objID + "-" + msg); } public static void threadPrint(String msg) { String threadName = Thread.currentThread().getName(); System.out.println("[Deadlock] threadName= " + threadName + "#" + msg); } public static void main(String[] args) { final Deadlock obj1 = new Deadlock("obj1"); final Deadlock obj2 = new Deadlock("obj2"); Runnable runA = new Runnable() { @Override public void run() { obj1.checkOther(obj2); } }; Thread threadA = new Thread(runA, "threadA"); threadA.start(); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } Runnable runB = new Runnable() { @Override public void run() { obj2.checkOther(obj1); } }; Thread threadB = new Thread(runB, "threadB"); threadB.start(); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("about to interrupt() threadA"); threadA.interrupt(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("about to interrupt() threadB"); threadB.interrupt(); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } threadPrint("did that break the deadlock?"); } }
/** * 重入锁使用java.util.concurrent.locks.ReentrantLock类来实现 * 为了保证锁最终必定会被释放(可能会有一场发生),要把互斥区放在try语句块内,并在finally语句块中释放锁,尤为当有return语句时, * return语句必须放在try字句中,以确保unlock()不会过早发生,从而将数据暴露给第二个任务。 * * 有助于提升“锁”性能的几点建议: * 一、减小锁持有时间;减小锁的持有时间有助于下降锁冲突的可能性,进而提高系统的并发能力。 * 二、减少锁粒度 * a)线程安全的HashMap:使用Collections.synchronizedMap()方法包装HashMap * public static Map m = Collections.synchronizedMap(new HashMap()) * b)对于ConcurrentHashMap,它内部进一步细分了若干小的HashMap,称之为段(SEGMENT)。默认状况下,一个ConcurrentHashMap被 * 进一步细分为16个段。因为默认有16个段,所以,若是够幸运的话,ConcurrentHashMap能够同时接受16个线程同时插入 * (若是都插入不一样的段中),从而大大提升其吞吐量;减小锁粒度会引入一个新的问题,即:当系统须要取得全局锁时,其消耗的资源会比较多。 * c)所谓减小锁粒度,就是只缩小锁定对象的范围,从而减小锁冲突的可能性,进而提升系统的并发能力; * 三、读写分离锁来替换独占锁 * a)使用读写锁ReadWriteLock能够提升系统的性能。使用读写分离锁来替代独占锁是减小锁粒度的一种特殊状况。 * b)减小锁粒度是经过分割数据结构实现的,那么,读写锁则是对系统功能点的分割; * c)在读多写少的场合,使用读写锁能够有效提高系统的并发能力; * 四、锁分离 * * 五、锁粗化 * 虚拟机在遇到一连串连续地对同一锁不断进行请求和释放的操做时,便会把全部的锁操做整合成对锁的一次请求,从而减小对锁的请求同步次数, * 这个操做叫作锁的粗化。 * * synchronized和ReetrantLock * 1)互斥同步最主要的问题就是进行线程阻塞和唤醒所带来的性能问题,于是这种同步又称为阻塞同步,它属于一种悲观的并发策略,即线程得到的是 * 独占锁。独占锁意味着其它线程只能依靠阻塞来等待线程释放锁。而在CPU转换线程阻塞时会引发线程上下文切换,当有不少线程竞争锁的时候, * 会引发CPU频繁的上下文切换致使效率很低。synchronized采用的即是这种并发策略。 * * 2)基于冲突检测的乐观并发策略,通俗地将就是先进性操做,若是没有其它线程争用共享数据,那操做就成功了,若是共享数据被争用,产生了冲突, * 那就在进行其它的补偿措施(最多见的补偿措施就是不断的重试,直到试成功为止),这种乐观的并发策略的许多实现都不须要把线程挂起,所以这种 * 同步被称为非阻塞同步。ReetrantLock采用的即是这种并发策略。在乐观的并发策略中,须要操做和冲突检测这两个步骤具有原子性,它靠硬件指令 * 来保证,这里用的是CAS操做(Compare and Swap)。 * * 3)基本语法上,ReetrantLock与synchronized很类似,它们都具有同样的线程重入特性,只是代码写法上有点区别而已,一个表现为API层面的互斥锁(Lock), * 一个表现为原生语法层面的互斥锁(synchronized)。 * * 《Java并发编程实践》一书给出了使用ReetrantLock的最佳时机: * 当你须要如下高级特性时,才应该使用:可定时的、可轮询的与可中断的锁获取操做,公平队列,或者非块结构的锁。不然,请使用synchronized。 */
public class ReentrantLockTest implements Runnable { public static ReentrantLock lock = new ReentrantLock(); public static int i = 0; @Override public void run() { for (int j = 0; j < 10000000; j++) { lock.lock(); try { i++; } finally { lock.unlock(); } } } public static void main(String[] args) throws InterruptedException { ReentrantLockTest reentrantLockTest = new ReentrantLockTest(); Thread thread1 = new Thread(reentrantLockTest); Thread thread2 = new Thread(reentrantLockTest); thread1.start(); thread2.start(); thread1.join(); thread2.join(); System.out.println("[ReentrantLockTest] i=" + i); } }