synchonrized和lock的区别
synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。是原生语法层面的互斥锁。
lock:需要显示指定起始位置和终止位置。一般使用ReentrantLock类做为锁,多个线程中必须要使用一个ReentrantLock类做为对象才能保证锁的生效。且在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。是API层面的互斥锁。
ReentrantLock相对于synchronized更灵活更强大,增加了轮询、超时、中断等高级功能。
ReentrantLock的实现
ReentrantLock实现Lock接口,包含一个内部类Sync,Sync继承自AQS(AbstractQueuedSynchronizer),一个用来构建锁和同步工具的框架。Sync有两个子类,FairSync和NonfairSync,即公平锁和非公平锁。AQS中有个volatile类型的int state变量,用来记录锁被获取的次数(可重入锁可以多次获取锁,一次只能有一个线程获取锁),默认以非公平方式获取锁,并尝试以CAS方式更新state为1(compareAndSetState(0, 1)),更新成功表明成功获取锁,将此线程记录为独占锁的线程;更新失败则调用acquire(1)方法获取锁(同样采用CAS方式更新state),如果没有成功获取到锁,则调用interrupt()方法中断当前线程。
什么是CAS?
CAS(Compare and Swap),通过JNI(Java Native Interface)调用CPU的CAS指令来完成原子更新操作。CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值。否则,处理器不做任何操作。无论V值是否等于A值,都将返回V的原值。
乐观锁、悲观锁,什么情况向用乐观锁,什么情况下用悲观锁?
volatile关键字的作用
处理器为了提高处理速度,不会频繁和内存进行通讯,而是先将内存数据读到内部缓存后再进行操作。如果对声明了volatile变量进行写操作,JVM就会向处理器发送一条Lock前缀的指令,将这个变量所在缓存行的数据写回到系统内存,但是其他处理器缓存的值还是旧的,所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址内容被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进行修改操作的时候,会强制重新从系统内存里把数据读到处理器缓存里。volatile变量的另一个作用是禁止指令重排序,即在汇编代码中插入内存屏障指令来保证处理器不会乱序执行代码(volatile变量前的代码不会排序到变量之后执行)。
Java内存模型中volatile类型变量通过happens-before原则保证写线程对变量的写操作对读线程对变量的读操作时可见的。
Java内存模型
Java阻塞队列
阻塞队列:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。典型的应用是生产者消费者模式。 jdk使用通知模式实现阻塞队列,当生产者线程向满队列中添加元素时会被阻塞,调用await()方法,当消费者线程移除了队列中的一个元素后,会通知生产者线程,调用signal()方法。BlockingQueue的put(e)和take()方法是阻塞的。
GC的过程,GC对程序有什么影响?当发现虚拟机频繁GC时应该怎么办?
JVM&并发