并发编程艺术笔记

减少上下文切换   (Lmbench3 时长、vmstat 次数)
  1.无锁并发   任务分段
  2.CAS
  3.使用最少线程     任务少,线程多,大多线程处于等待状态
  4.协程   单线程实现多任务
  
避免死锁常见方法:
    1.避免一个线程同时获取多个锁。
    2.避免一个线程内在锁内占用多个资源,尽量保证每个锁占用一个
    3.尝试使用定时锁lock.tryLock
    4.对于数据库锁,加锁解锁必须在一个连接内
    
资源限制(将串行执行的部分变成并发执行,资源不足会变慢)
    硬件:带宽,硬盘,使用集群  数据id%机器数
    软件:数据库连接数,socket数   使用资源复用
   根据不同的资源限制调整程序的并发度
   
synchronized实现同步的基础:
    1.普通同步方法,锁实例对象
    2.静态            Class对象
    3.同步方法块,锁括号中对象 synchronized(对象)
    在jvm中使用Monitor对象来实现,方法同步(也可用以下方法实现这里用另外
    的一种方法)、
    代码块同步(monitorenter,monitorexit)

线程通信:1.共享内存(隐式通信)  2.消息传递(之间发送消息,显式)
            显式同步,需要指定互斥块     隐式同步发送必须在接受消息前

java使用共享内存模型

在没有  数据依赖性      的情况下,会进行指令重排
      (读写、写读、写写)

   当存在控制相关性的时候  也会进行指令重排
   if(flag){
      a = i*i;
   }
   先执行temp=i*i;(存入重排序缓冲)  然后判断flag,为真执行a=temp;
 单线程中对控制依赖的操作重排序不会改变执行结果,多线程可能会!!

顺序一致性内存模型
    1.一个线程所有操作必须按照程序的顺序进行
    2.所有线程都能看到单一操作执行顺序,每个操作原子,且立刻对其他线程可见。

JMM(java memory model)通过同步程序来达到顺序一致性效果,尽可能为编译器和处理器优化便利。

JMM 不保证
    1.单线程内操作按程序顺序执行
    2.所有线程都能看到一致的操作执行顺序
    3.对64bit的long double 写操作的无原子性 jdk5以后读为原子操作
                         

volatile的内存语义
        (可见性)
            对被该修饰的变量,单个读和写可看做是使用同一个锁对单个读写进行同步
        如果是多个volatile操作或者复合操作(volatile++)整体上不具有原子性
        (单个volatile变量的读写的原子性)
            读,,写,,写读,读写不具有原子性
        (部分有序性)
    操作1 普通读写   操作2 volatile写禁止,,其他的允许
        当第二个操作是volatile写的时候,禁止重排序
        当第一个操作是volatile读,禁止重排序
        第一位volatile写,第二为volatile读写不允许排序     普通读写可以

-------------------------------------------------------------------------------------------
普通读写                                                volatile读
StoreStore屏障  禁止普通写与下一步重排                 LoadLoad屏障    禁止后面的
volatile写                                               LoadStore屏障   读写和volatile重排
StoreLoad屏障 禁止上一步与后面可能的volatile读写重排     普通读写
----------------------------------------------------------------------------------------------
LoadLoad      确保Load1数据装载先于Load2以及所有后续装载指令
LoadStore     确保Load1数据装载先于Store一局所有后续存储指令
StoreStore      确保Store1数据对其他处理器可见(刷新到内存)先于Store2以及所有后续存储指令
StoreLoad      确保Store1数据对其他处理器可见(刷新到内存)先于Load2以及所有后续装载指令的装载。


锁的内存语义    除了让 临界区互斥执行  释放锁的线程向获取同一个锁的线程发送消息
    1.线程A释放一个锁,实质上是A向接下来获取该锁的某线程发送消息
    2.线程B获取一个锁,实质上是B获取了之前的某个线程发出的消息
    3。A释放,B获取,即A通过主内存向B发送消息

AQS(AbstractQueueSynchronizer)


final域的重排序规则
以下不能重排序
    1.构造函数内对一个final域的写入,与随后把该对象的引用赋值给一个引用变量
    2.初次读一个final域的对象的引用,与随后初次读这个final域   
 final域的写重排序规则
    1.JMM禁止编译器把final域的写重排到构造函数之外
    2.编译器会在final写之后,构造函数return之前,插入一个StoreStore屏障禁止把final写重排到构造函数之
   外
 final域的读重排序规则
    1.一个线程中初次读对象引用与初次读对该对象包含的final域,JMM禁止处理器重排序这两个操作(仅对CPU)
  会在final域读操作之前插入一个LoadLoad  两个操作之间存在间接依赖关系,禁止少数CPU重排序这两个操作。

DoubleCheck和延迟初始化    减少创建类的开销增加了耗时
    DoubleCheck
        利用volatile                         禁止 初始化对象与设置指向instance的空间重排序
        
    基于类初始化                               允许重排序,但是不被其他线程看到
        private static class InstanceHolder{
            public static Resource re = new Resource();    //会使用 Class对象初始化锁 和 state字段
        }                                                 //其中状态有noInitialization  initializing
        public static Resource getResource(){            //initialized   三个状态 ,其他检测到字段wei
            return InstanceHolder.re;                 //第二个,就释放锁,挂起等待唤醒
        }
  
Daemon线程 在start之前设置  其线程中的finally块不一定执行


第4章
Thread.interrupted()对线程的中断方法进行复位

安全线程终止应使用 中断   或者    标识位

   !!!仅仅通过flag     然后循环等待,会 难以保持及时性,难以 降低开销

通过一个object的wait(会释放锁)  notify  ,以及flag  循环等待  来对两个线程之间传递消息
ThreadA ThreadB  需要都获得 object的对象锁
    notifyAll() 会把所有等待线程全移动到同步队列  被移动的线程状态由Waiting变为Blocked
    
    static boolean flag = true;
    static Object lock = new Object();
ThreadA    
    synchronized(lock){
        while(flag){
        lock.wait();
        }
        处理逻辑
    }
ThreadB
   synchronized(lock){
        flag = false;
        lock.notifyAll();
    }

thread.join()  join()会在thread死亡后返回当前线程

ThreadLocal 可以用于计算方法时间   set(obj)   get(obj)  以一个ThreadLocal为键,任意对象为值的存储结构


线程等待超时模式 
    public synchronized Object get(long mills) throws InterruptedException{   //对当前对象加锁
        long future = System.currentTimeMills()+mills;  //   等待终止时间
        long remaining = mills;                         //   剩余等待时间
        while((result==null)&&remaining>0){             //   没有得到结果 且 仍存在等待时间 循环等待
            wait(remaining);                            //等待
            remaining = future - System.currentTimeMills();   //  等待后 剩余时间
        }
        return result;   //返回结果
    }
    


  

    
    

猜你喜欢

转载自blog.csdn.net/qmylzx/article/details/82503458