zoukankan      html  css  js  c++  java
  • java并发-重读笔记整理

    java并发有两本很好的书《java并发编程的艺术》、《java并发实战》。本文是重读前者把旧笔记再整理一遍而成。

                                            ——引言

    (1)volatile与synchronized

    以读一段代码的方式看下:

    /** 单例模式下,如何保证并发安全:两个方案:synchronized;双重锁。
     * Created by baimq on 2019/3/16.
     */
    public class singleton {
        //volatile保证在缓存行和内存层面的读一致。
        private static volatile singleton instance;
        
        public  static  singleton getInstance(){
            if(instance==null){
                synchronized (singleton.class){//synchronized保证应用层面的代码访问原子性。
                    if(instance==null){
                    instance=new singleton();//因为创建对象,并不原子。所以必须保证可见性。
                     }
                }
            }
            return  instance;
        }
        
    }
    

    volitile:保证在多线程环境下读的可见性和读的一致性。如果多线程共享的变量不实现volitile,则会导致在A-CPU中读取到M值,随后在B-CPU中读取到N值。但因其不能保证写的原子性,所以不能用来实现内存计数器。

    通过lock命令执行MESI协议,保证在CPU和内存层面的读写一致性。其在应用层面不能保证读写一致性。所以使用场景受限:

    A、对变量的写操作不依赖于当前值。

    B、该变量没有包含在具有其他变量的不变式中。

    [MESI:缓存一致性协议。CPU缓冲行与内存之间的数据一致性协议。实现LOCK命令,总线锁或者缓存锁。]

    synchronized:用来锁代码块或者方法,保证同一时间只有一个线程能访问到代码块。

    Synchronized锁的升级:偏向锁,轻量级锁,重量级锁。

    偏向锁:在对象头中写入线程号。

    轻量级锁:通过CAS操作实现,如果不能获得锁,则自旋。自旋次数过多,则升级为重量级锁。

    重量级锁:通过线程阻塞和唤醒等待线程的方式实现。

    解读代码:

    利用synchronized来保证只有一个线程可以执行创建对象的动作。

    但是漏洞是创建对象并且赋值的过程不是原子的,所以可能发生还没创建好对象,就执行到了return instance阶段。

    所以要再利用volitile来保证多个线程的读一致,保证不会在多个线程中读到不一致的结果。

    (2)线程的生命周期、对象的锁

    thread.join:等待线程执行完毕。

    thead.start:开始执行。

    thead. Interrupt::安全中断(并没有真中断,而是使线程的isInterrupted返回false)。

    thead. isInterrupted()[常用语法:Thread.currentThread().isInterrupted(),用作安全终止线程]。

    thread.run,仅执行runnable的run方法一次,与开启线程无关。

    对象.wait();//让持有对象线程等待

    对象.notify();//随机唤醒持有对象的线程之一。

    对象notifyAll();//唤醒持有对象的多个线程。

    (3)Lock

    Lock接口,四种锁的方式:

    lock()阻塞性的锁;

    尝试非阻塞地获取锁;

    能被中断的获取锁;

    超时获取锁。

    重入锁:可以多次锁,释放时也需要多次释放。

    读写锁:在写的过程中阻塞所有读和写。适合读多写少的场景。

    (4)ThreadLocal

    为对象的共享变量创建本线程的副本。SimpleDateFormat是非线程安全的类,包装下就安全了,原理见我其他博客:

    public class SafeDateUtil {
    
        private static  ThreadLocal<SimpleDateFormat> threadLocalSDF = new ThreadLocal<SimpleDateFormat>() {
            @Override
            protected SimpleDateFormat initialValue() {
                return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            }
        };
    
        public static  String formatDate(Date date)throws ParseException{
            return threadLocalSDF.get().format(date);
        }
    
    }

    (5)CountDownLatch、CyclicBarrier(同步屏障)、semaphore(信号量、限流器)

    CountDownLatch是等所有线程执行完;

    CyclicBarrier是等待在线程内屏障点上。

    CyclicBarrier更强大,可以完全hold住CountDownLatch的功能。

    一个简单的例子把:

    /**
     * Created by baimq on 2019/3/16.
     */
    public class CountDownDemo {
    
        public static void main(String[] args) {
            CountDownLatch countDownLatch=new CountDownLatch(3);
            new Thread(new WorkerRunnable(countDownLatch,30)).start();
                new Thread(new WorkerRunnable(countDownLatch,1000)).start();
                new Thread(new WorkerRunnable(countDownLatch,1)).start();
                try {
                    Thread.sleep(20);
                System.out.println("在计数中,干完活的有:"+countDownLatch.getCount()+"个");
                countDownLatch.await();
                System.out.println("全部干完活了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        public static class  WorkerRunnable implements Runnable{
            private CountDownLatch countDownLatch;
            private int sleep;
            public WorkerRunnable(CountDownLatch countDownLatch,int sleep){
                this.countDownLatch=countDownLatch;
                this.sleep=sleep;
            }
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"在干活");
                try {
                    Thread.sleep(sleep);
                    System.out.println(Thread.currentThread().getName()+"干完活了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            }
        }
    }

    semaphore是限流器保证只有N个线程在同时执行。

    (6)Fork/join、并发容器、executor框架

    这些很早就在用了,所以比较熟。

    fork/join是为了解决任务切割执行。

    并发容器如concurrentHashMap等是分区思路,切割为较小的segment,并发写时对segment锁住。

    executor是线程池执行框架,支持设置线程容量、设置拒绝策略,设置队列延后执行等。

  • 相关阅读:
    running Android Studio on Windows 7 fails, no Android SDK found
    Dalvik虚拟机简要介绍和学习计划
    免费HTTP上传文件控件WebUploadFile.ocx发布,让实现Word在线编辑器变得容易
    ASP.NET生成缩略图的代码
    C++ MFC 关于SelectObject,请解释下,谢谢
    Visual C++中MFC消息的分类
    深入思考全局静态存储区、堆区和栈区
    coredump简介与coredump原因总结
    c++函数中的 指针参数跟地址参数区别
    解决SWFUpload在Chrome、Firefox等浏览器下的问题
  • 原文地址:https://www.cnblogs.com/baimingqian/p/10544954.html
Copyright © 2011-2022 走看看