java基础知识:
volatile是如何保证可见性的呢?
有volatile修饰的共享变量在进行写操作的时候,会在前面多出一条 lock 前缀的指令,而lock前缀的指令在多核处理器下会引发两件事情: ①将当前处理器的缓存行的数据写会到系统内存中 ②这个写会操作会使在其他CPU 里缓存了该内存地址的数据无效。 所以在多处理器下,为了保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的地址被修改, 就会将当前处理器的缓存行设置成无效状态。 当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据读到处理器缓存里。
synchronized 篇
- 实现同步的基础: java中的每一个对象都可以作为锁, 对于普通同步方法,锁是当前实例对象;对于静态同步方法, 锁是当前类的Class对象; 对于同步方法快,锁是 synchronized 括号里配置的对象。
- 锁的实现原理: jvm 是基于进入和退出 monitor 对象来实现方法同步和代码块的同步的,但是两者实现的细节不太一样,代码块同步是使用 monitorenter 和 monitorexit 指令来实现的, monitorenter 指令是在编译后插入到同步代码块的开始位置,而 monitorexit 是插入到方法结束处 和 异常处, 任何对象都有一个monitor 与之关联,当且仅当一个 monitor 被持有后,她将处于锁定状态。 线程执行到 monitorenter 指令时,将会尝试获得对象所对应的 monitor 的所有权,尝试获得对象的锁。。。。
- 1.6之后的锁的优化: 锁从级别低到高依次是: 无锁状态、偏向锁状态、轻量级锁、重量级锁。这几个状态会随着竞争情况逐渐升级。 锁可以升级但是不可以降级,下面从偏向锁开始一一介绍,
- 偏向锁:
- 引入的原因:大多数情况下, 锁不仅不存在多线程竞争,而且总是由同一线程多次获得,为了让线程获得锁的代价更低,就引入了偏向锁。
- 过程: 当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录存储偏向的线程ID,以后该线程在进入和退出同步块时不需要CAS操作来加锁和解锁,只需要简单测试一下对象头中 mark word 里是否存储着指向当前线程的偏向锁。 测试成功,则获得锁;失败,则在测试一下 mark word中偏向锁的标识是否设置成1(表示当前是偏向锁), 如果设置了,尝试使用CAS将对象头的偏向锁指向当前线程;否则,使用CAS竞争该锁
- 轻量级锁:
- 线程在执行同步块之前,jvm会先在当前线程的栈帧中创建用于存储锁记录的空间,并将对象头中的 mark word复制到锁记录中,然后线程尝试使用CAS将对象头中mark word替换为指向锁记录的指针。如果成功,当前线程获得锁,否则,表示其他线程竞争锁,当前线程 通过尝试自旋来获取锁。
- 重量级锁:
- 独占锁,在锁处于这个状态下,其他线程试图获取锁时,都会被阻塞住,当持有这个锁的线程释放锁之后会唤醒这些线程, 被唤醒的线程会进行新一轮的夺锁之争。
- 偏向锁:
锁 | 优点 | 缺点 | 使用场景 |
偏向锁 | 加解锁不需要额外的消耗,和执行非同步方法相比仅存在纳秒级的差距 | 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 | 适用于只有一个线程的场景 |
轻量级锁 | 竞争的线程不会阻塞, 提高了程序的相应速度 | 如果始终得不到锁竞争的线程, 使用自旋会消耗cpu | 追求响应时间,同步代码块执行速度非常快 |
重量级锁 | 线程竞争不使用自旋,不会消耗cpu | 线程阻塞, 响应时间缓慢 | 追求吞吐量,同步代码块执行时间较长 |
线程篇
- 等待/通知机制
- 可以使用 wait() 、 notify() / notifyAll() 方法
public class WaitNotify { static Object lock = new Object(); static boolean flag = true; public static void main(String[] args) throws InterruptedException { Thread waitThread = new Thread(new Wait(), "WaitThread"); waitThread.start(); TimeUnit.SECONDS.sleep(1); Thread notifyThread = new Thread(new Notify(), "notifyThread"); notifyThread.start(); } static class Wait implements Runnable { @Override public void run() { synchronized (lock) { while (flag) { try { System.out.println(String.format(String.format(Thread.currentThread() + " flag is true. wait @" + new SimpleDateFormat("HH:mm:ss").format(new Date())))); lock.wait(); } catch (InterruptedException e) { } } System.out.println(Thread.currentThread() + " flag is false. running @" + new SimpleDateFormat("HH:mm:ss").format(new Date())); } } } static class Notify implements Runnable { @Override public void run() { synchronized (lock) { System.out.println(Thread.currentThread() + " hold lock. notify @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); lock.notifyAll(); flag = false; try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { } } synchronized (lock) { System.out.println(Thread.currentThread() + " hold lock again. sleep @ " + new SimpleDateFormat("HH:mm:ss").format(new Date())); try { TimeUnit.SECONDS.sleep(5); } catch (InterruptedException e) { } } } } }
- 可以使用 wait() 、 notify() / notifyAll() 方法
- 等待/超时机制
- 等待超时机制就是在等待/通知机制上增加了超时控制,这使得该模式相比原来范式更有灵活性,因为即使方法执行时间过长,也不会“永久”阻塞调用者,而是会按照调用者的要求“按时”返回。
public class ConnectionPool { private LinkedList<Connection> pool = new LinkedList<Connection>(); public ConnectionPool(int initialSize) { if(initialSize > 0) { for(int i = 0; i < initialSize; i++) { pool.addLast(ConnectionDriver.createConnection()); } } } public void releaseConnection(Connection connection) { if(connection != null) { synchronized (pool) { // 连接释放后需要进行通知,这样其他消费者能够感知到连接池中已经归还了一个链接 pool.addLast(connection); pool.notifyAll(); } } } public Connection fetchConnection(long mills) throws InterruptedException { synchronized (pool) { // 完全超时 if (mills <= 0) { while(pool.isEmpty()) { pool.wait(); } return pool.removeFirst(); } else { long future = System.currentTimeMillis() + mills; long remaining = mills; while(pool.isEmpty() && remaining > 0) { pool.wait(remaining); remaining = future - System.currentTimeMillis(); } Connection result = null; if(!pool.isEmpty()) { result = pool.removeFirst(); } return result; } } } } class ConnectionDriver { static class ConnectionHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("commit")) { TimeUnit.MILLISECONDS.sleep(100); } return null; } } public static final Connection createConnection() { return (Connection) Proxy.newProxyInstance(ConnectionDriver.class.getClassLoader(), new Class[] {Connection.class}, new ConnectionHandler()); } }
- 等待超时机制就是在等待/通知机制上增加了超时控制,这使得该模式相比原来范式更有灵活性,因为即使方法执行时间过长,也不会“永久”阻塞调用者,而是会按照调用者的要求“按时”返回。
- 待补充。