zoukankan      html  css  js  c++  java
  • Java并发编程的艺术-读书笔记

    多线程不一定比单线程快

    当累加操作少于百万次时,单线程执行的速度会比多线程执行的速度快,因为线程有创建和上下文切换的开销
    vmstat的cs表示每秒上下文切换的次数

    如何减少多线程上下文切换次数

    使用无锁并发编程,CAS算法,使用最少线程和使用协程

    死锁

    死锁样例:

    public class DeadLockDemo {
    
        /** A锁 */
    	private static String A = "A";
        /** B锁 */
    	private static String B = "B";
    
    	public static void main(String[] args) {
    		new DeadLockDemo().deadLock();
    	}
    
    	private void deadLock() {
    
    		Thread t1 = new Thread(new Runnable() {
    			@Override
    			public void run() {
    				synchronized (A) {
    					try {
    						Thread.sleep(2000);
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    					synchronized (B) {
    						System.out.println("1");
    					}
    				}
    			}
    		});
    
            Thread t2 = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (B) {
                        synchronized (A) {
                            System.out.println("2");
                        }
                    }
                }
            });
    
            t1.start();
            t2.start();
        }
    }
    

    现实场景中,比如t1拿到锁之后,因为一些异常情况没有释放锁(死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉,就会出现死锁

    避免死锁的常用办法

    1. 避免一个线程同时获取多个锁。
    2. 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
    3. 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
    4. 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

    volatile的应用

    volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性
    可见性的意思是当一个线程修改一个共享变量时,另外一个线程能读到这个修改的值

    volatile定义与实现原理

    java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。
    Java语言提供了volatile,在某些情况下比锁要更加方便。如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的

    volatile会增加Lock前缀指令

    Lock前缀指令会引起处理器缓存回写到内存
    一个处理器的缓存回写到内存会导致其他处理器的缓存无效

    synchronized

    JVM基于进入和退出Monitor对象来实现方法同步和代码块同步
    代码块同步是使用monitorenter和monitorexit指令实现的,而方法同步是使用另外一种方式实现的

    锁的4种状态

    无锁,偏向锁,轻量级锁,重量级锁
    锁可以升级不能降级,目的是为了提高获得锁和释放锁的效率

    单例模式中的双重检测锁

    public class UnsafeLazyInitialization {
      private static Instance instance;
      public static Instance getInstance() {
        if (instance == null)			// 1:A线程执行
            instance = new Instance();	// 2:B线程执行
            return instance;
      }
      static class Instance {
      }
    }
    

    这样初始化对象时会出现指令重排,导致取得instance对象时没有初始化完成
    解决办法有两个,一个是禁止指令重排序,二是让指令重排序不让外面知道
    1.instance类加上volatile关键字
    2.利用JVM在类的初始化阶段(在class被加载后,且被线程使用之前),会执行类的初始化
    在执行类的初始化期间,jvm会获取一个锁,这个锁可以同步多个线程对同一个类的初始化

    队列同步器

    AbstractQueuedSynchronizer
    用来构建锁或者其他同步组件的基础框架
    在锁的实现中聚合同步器,利用同步器实现锁的语义

    独占锁

    独占锁就是在同一时刻只能有一个线程获取到锁,而其他获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁

  • 相关阅读:
    jvm 指令 invokedynamic
    go switch
    JVM指令 bytecode invokespecial
    babel插件开发
    go 循环依赖 循环引用 最佳实践
    go module 使用入门
    搞懂gopath golang go go项目结构
    SQL Server 工具
    SQLServer Management Studio登录框中的“服务器名”填写
    win2008下安装SQL SERVER 2005出现IIS功能要求 警告解决方案
  • 原文地址:https://www.cnblogs.com/Baronboy/p/15471072.html
Copyright © 2011-2022 走看看