并发编程的关键是什么,你知道吗?我淡淡一笑。还好我平时玩的是高并发架构设计,不然就被你忽悠了!互斥同一时刻只允许一个线程访问共享资源。同步线程之间的通信和协作这两个问题都可以由monitor来处理。JUC是通过Lock和Condition接口实现的监视器:Lock解决互斥,Condition解决同步。只见P8不着急,又开始问:说到这个monitor,synchronized也是monitor的实现。既然JDK已经实现了,如果你没有监视器,为什么要提供另一个实现?这绝不是重新发明轮子,它们是非常不同的。最简单的就是在JDK1.5中,synchronized的性能比Lock差,但是1.6之后对synchronized进行了优化,提高了性能,所以1.6之后推荐使用synchronized。但性能问题只需要优化,根本不需要“重新发明轮子”。问题的症结在于死锁问题破坏了“不可抢占”的条件,synchronized无法达到这个目的。因为synchronized在申请资源的时候,如果申请不到,线程就会直接阻塞,处于阻塞状态的线程什么都不做,线程已经占用的资源自然也就无法释放了。但我们希望:对于“不可抢占”的情况,当占用了一些资源的线程进一步申请其他资源时,如果申请不上,能够主动释放自己已经占用的资源,从而实现“不可抢占”条件被破坏。如果你重新设计一个互斥体来解决这个问题,你应该怎么做?下面的设计可以破坏“不可抢占”的条件:在能够响应中断并使用synchronized持有锁X后,如果尝试获取锁Y失败,线程将进入阻塞状态,一旦死锁,就没有机会了唤醒阻塞的线程。但是如果处于阻塞状态的线程能够响应中断信号,即当中断信号发送到阻塞线程时,它能够被唤醒,那么它就有机会释放它曾经持有的锁X。支持超时如果线程在一段时间内没有获取到锁,不是进入阻塞状态,而是返回错误,线程还有机会释放曾经持有的锁。非阻塞地获取锁。进入阻塞状态,却直接返回,那么这个线程也有机会释放它曾经持有的锁。其实就是Lock接口的如下方法:lockInterruptibly()支持中断tryLock(longtime,TimeUnitunit)支持超时tryLock()支持非阻塞获取Lock,你知道它是怎么保证可见性的吗?Lock的经典案例是try/finally,必须在finally块中释放锁。Java多线程的可见性是由Happens-Before规则保证的,而Happens-Before并没有提到Lock锁。那么Lock靠什么保证可见性呢?当然,它使用volatileHappens-Before规则。因为ReentrantLock的内部类继承了AQS,内部维护了一个volatile变量状态。获取锁时,会读写状态。解锁时,也会读写状态。因此,在执行value+=1之前,程序先对volatile状态进行一次读写。执行完value+=1后,再次读写volatile状态。根据以下Happens-Before规则判断:顺序规则Threadt1'svalue+=1Happens-Beforethreadt1'sunlock()volatilevariablerule由于此时state为1,会先读取state,所以线程t1的unlock()Happens-Beforethreadt2'slock()传递性规则Threadt'svalue+=1Happens-Beforethreadt2'slock()什么是可重入锁?可重入锁意味着一个线程可以重复获取同一个锁。一个例子如下:你听说过可重入方法吗?orz,这是什么鬼?P8看我一时语塞,明白了,说:没关系,随便问问看你的见识。事实上,多个线程可以同时调用这个方法,每个线程都能得到正确的结果;同时,线程内支持线程切换,无论切换多少次,结果都是正确的。可以同时执行多个线程,也支持线程切换。因此,可重入方法是线程安全的。那我们简单说一下公平锁和非公平锁?比如ReentrantLock有两个构造函数,一个是无参构造函数,一个是公平参数。fair参数表示锁的公平策略,true:需要构造公平锁,false:构造非公平锁(默认)。你知道锁的入口等待队列吗?锁对应一个等待队列。如果一个线程没有获得锁,就会进入等待队列。当一个线程释放锁时,需要从等待队列中唤醒一个正在等待的线程。如果是公平锁,唤醒策略就是谁等的时间长了谁就唤醒谁,这是公平的。如果是非公平锁,则不提供这种公平性保证,所以等待时间最短的线程可能会先被唤醒。非公平锁的场景应该是线程释放锁后,如果有线程来获取锁,不需要排队直接获取,不会进入队列。拿不到就加入队伍。谈谈您在锁方面的一些最佳实践。锁不是解决并发问题的灵丹妙药。风险很大,比如各种死锁随处可见,还会影响性能。并发大师DougLea的最佳实践:AlwaysonlyonlyupdateobjectmembervariablesAlwaysonlylockwhenaccessingvariablemembervariablesNeverlockwhencallingotherobjectsmethodsbecausecallingthemethodsofotherobjectsreallyistoounsafe,也许有一个threadsleep()在“other”方法中调用,否则可能出现极慢的I/O操作,严重影响性能。更可怕的是,“其他”类的方法也有可能被加锁,那么双重加锁就可能导致死锁。还有一些常见的比如只在该锁的地方锁。最后,扩展一些小知识点:notifyAll()在面对公平锁和非公平锁的时候效果是一样的。等待队列中的所有线程都被唤醒,都在入口等待队列中排队?这些被唤醒的线程不需要按照等待时间排队然后放入入口等待队列?他们都觉醒了。理论上他们是同时进入入口等待队列的,等待时间是一样的。CPU级别的原子性是单个cpu指令。Java层面的互斥(监控)保证了原子性。这两种原子性有不同的含义。cpu的原子性不受线程调度的影响,指令要么执行要么不执行。Java层面的原子性就是保证在锁机制下只有一个线程执行,其余等待。这时候CPU仍然可以进行线程调度,让正在运行的线程让出CPU时间。当然,线程仍然持有锁。
