精华内容
下载资源
问答
  • 并发编程实战

    2017-12-27 21:46:18
    java 多线程 并发 实战,经典的并发编程实战,可以i深入理解多线程相关技术
  • C++并发编程实战

    2017-09-10 14:27:18
    C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战C++并发编程实战
  • Java并发编程实战

    2016-11-08 09:59:14
    并发编程实战
  • C++并发编程实战 pdf

    2017-11-02 18:28:25
    C++并发编程实战 C++并发编程实战 C++并发编程实战 C++并发编程实战
  • JAVA并发编程实战

    2019-02-07 21:16:26
    JAVA并发编程实战,JAVA并发编程实战2本打包!
  • Java 并发编程实战

    2018-01-02 19:31:43
    Java 并发编程实战 Java 并发编程实战
  • Erlang OTP并发编程实战

    2017-10-27 16:51:46
    Erlang并发编程实战Erlang并发编程实战Erlang并发编程实战Erlang并发编程实战
  • Java并发编程文章系列Java并发编程实战 01并发编程的Bug源头Java并发编程实战 02Java如何解决可见性和有序性问题Java并发编程实战 03互斥锁 解决原子性问题前提在第三篇文章最后的例子当中,需要获取到两个账户的锁...

    Java并发编程文章系列

    Java并发编程实战 01并发编程的Bug源头
    Java并发编程实战 02Java如何解决可见性和有序性问题
    Java并发编程实战 03互斥锁 解决原子性问题

    前提

    在第三篇文章最后的例子当中,需要获取到两个账户的锁后进行转账操作,这种情况有可能会发生死锁,我把上一章的代码片段放到下面:

    public class Account {
        // 余额
        private Long money;
        public synchronized void transfer(Account target, Long money) {
            synchronized(this) {           (1)
                synchronized (target) {    (2)
                    this.money -= money;
                    if (this.money < 0) {
                        // throw exception
                    }
                    target.money += money;
                }
            }
        }
    }

    账户A转账给账户B100元,账户B同时也转账给账户A100元,当账户A转帐的线程A执行到了代码(1)处时,获取到了账户A对象的锁,同时账户B转账的线程B也执行到了代码(1)处时,获取到了账户B对象的锁。当线程A和线程B执行到了代码(2)处时,他们都在互相等待对方释放锁来获取,可是synchronized是阻塞锁,没有执行完代码块是不会释放锁的,就这样,线程A和线程B死死的对着,谁也不放过谁。等到了你去重启应用的那一天。。。这个现象就是死锁死锁的定义:一组互相竞争资源的线程因互相等待,导致“永久”阻塞的现象。
    如下图:

    816771db54d8e6dfacdfae638a2f4abf.png

    查找死锁信息

    这里我先以一个基本会发生死锁的程序为例,创建两个线程,线程A获取到锁A后,休眠1秒后去获取锁B;线程B获取到锁B后 ,休眠1秒后去获取锁A。那么这样基本都会发生死锁的现象,代码如下:

    public class DeadLock extends Thread {
        private String first;
        private String second;
        public DeadLock(String name, String first, String second) {
            super(name); // 线程名
            this.first = first;
            this.second = second;
        }
    
        public  void run() {
            synchronized (first) {
                System.out.println(this.getName() + " 获取到锁: " + first);
                try {
                    Thread.sleep(1000L); //线程休眠1秒
                    synchronized (second) {
                        System.out.println(this.getName() + " 获取到锁: " + second);
                    }
                } catch (InterruptedException e) {
                    // Do nothing
                }
            }
        }
        public static void main(String[] args) throws InterruptedException {
            String lockA = "lockA";
            String lockB = "lockB";
            DeadLock threadA = new DeadLock("ThreadA", lockA, lockB);
            DeadLock threadB = new DeadLock("ThreadB", lockB, lockA);
            threadA.start();
            threadB.start();
            threadA.join(); //等待线程1执行完
            threadB.join();
        }
    }

    运行程序后将发生死锁,然后使用jps命令(jps.exe在jdk/bin目录下),命令如下:

    C:Program FilesJavajdk1.8.0_221bin>jps -l
    24416 sun.tools.jps.Jps
    24480 org.jetbrains.kotlin.daemon.KotlinCompileDaemon
    1624
    20360 org.jetbrains.jps.cmdline.Launcher
    9256
    9320 page2.DeadLock
    18188

    可以发现发生死锁的进程id 9320,然后使用jstack(jstack.exe在jdk/bin目录下)命令查看死锁信息。

    C:Program FilesJavajdk1.8.0_221bin>jstack 9320
    "ThreadB" #13 prio=5 os_prio=0 tid=0x000000001e48c800 nid=0x51f8 waiting for monitor entry [0x000000001f38f000]
       java.lang.Thread.State: BLOCKED (on object monitor)
            at page2.DeadLock.run(DeadLock.java:19)
            - waiting to lock <0x000000076b99c198> (a java.lang.String)
            - locked <0x000000076b99c1d0> (a java.lang.String)
    
    "ThreadA" #12 prio=5 os_prio=0 tid=0x000000001e48c000 nid=0x3358 waiting for monitor entry [0x000000001f28f000]
       java.lang.Thread.State: BLOCKED (on object monitor)
            at page2.DeadLock.run(DeadLock.java:19)
            - waiting to lock <0x000000076b99c1d0> (a java.lang.String)
            - locked <0x000000076b99c198> (a java.lang.String)

    这样我们就可以看到发生死锁的信息。虽然发现了死锁,但是解决死锁只能是重启应用了。

    如何避免死锁的发生

    1.固定的顺序来获得锁

    如果所有线程以固定的顺序来获得锁,那么在程序中就不会出现锁顺序死锁问题。(取自《Java并发编程实战》一书)
    要想验证锁顺序的一致性,有很多种方式,如果锁定的对象含有递增的id字段(唯一、不可变、具有可比性的),那么就好办多了,获取锁的顺序以id由小到大来排序。还是用转账的例子来解释,代码如下:

    public class Account {
        // id (递增)
        private Integer id;
        // 余额
        private Long money;
        public synchronized void transfer(Account target, Long money) {
            Account account1;
            Account account2;
            if (this.id < target.id) {
                account1 = this;
                account2 = target;
            } else {
                account1 = target;
                account2 = this;
            }
    
            synchronized(account1) {
                synchronized (account2) {
                    this.money -= money;
                    if (this.money < 0) {
                        // throw exception
                    }
                    target.money += money;
                }
            }
        }
    }

    若该对象并没有唯一、不可变、具有可比性的的字段(如:递增的id),那么可以使用 System.identityHashCode() 方法返回的哈希值来进行比较。比较方式可以和上面的例子一类似。System.identityHashCode()虽然会出现散列冲突,但是发生冲突的概率是非常低的。因此这项技术以最小的代价,换来了最大的安全性。提示: 不管你是否重写了对象的hashCode方法,System.identityHashCode() 方法都只会返回默认的哈希值。

    2.一次性申请所有资源

    只要同时获取到转出账户和转入账户的资源锁。执行完转账操作后,也同时释放转入账户和转出账户的资源锁。那么则不会出现死锁。但是使用synchronized只能同时锁定一个资源锁,所以需要建立一个锁分配器LockAllocator。代码如下:

    /** 锁分配器(单例类) */
    public class LockAllocator {
        private final List<Object> lock = new ArrayList<Object>();
        /** 同时申请锁资源 */
        public synchronized boolean lock(Object object1, Object object2) {
            if (lock.contains(object1) || lock.contains(object2)) {
                return false;
            }
    
            lock.add(object1);
            lock.add(object2);
            return true;
        }
        /** 同时释放资源锁 */
        public synchronized void unlock(Object object1, Object object2) {
            lock.remove(object1);
            lock.remove(object2);
        }
    }
    
    public class Account {
        // 余额
        private Long money;
        // 锁分配器
        private LockAllocator lockAllocator;
        
        public void transfer(Account target, Long money) {
            try {
                // 循环获取锁,直到获取成功
                while (!lockAllocator.lock(this, target)) {
                }
    
                synchronized (this){
                    synchronized (target){
                        this.money -= money;
                        if (this.money < 0) {
                            // throw exception
                        }
                        target.money += money;
                    }
                }
            } finally {
                // 释放锁
                lockAllocator.unlock(this, target);
            }
        }
    }

    使用while循环不断的去获取锁,一直到获取成功,当然你也可以设置获取失败后休眠xx毫秒后获取,或者其他优化的方式。释放锁必须使用try-finally的方式来释放锁。避免释放锁失败。

    3.尝试获取锁资源

    在Java中,Lock接口定义了一组抽象的加锁操作。与内置锁synchronized不同,使用内置锁时,只要没有获取到锁,就会死等下去,而显示锁Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁操作都是显示的(内置锁synchronized的加锁和解锁操作都是隐示的),这篇文章就不展开来讲显示锁Lock了(当然感兴趣的朋友可以先百度一下)。

    总结

    在生产环境发生死锁可是一个很严重的问题,虽说重启应用来解决死锁,但是毕竟是生产环境,代价很大,而且重启应用后还是可能会发生死锁,所以在编写并发程序时需要非常严谨的避免死锁的发生。避免死锁的方案应该还有更多,鄙人不才,暂知这些方案。若有其它方案可以留言告知。非常感谢你的阅读,谢谢。

    参考文章:
    《Java并发编程实战》第10章
    极客时间:Java并发编程实战 05:一不小心死锁了,怎么办?
    极客时间:Java核心技术面试精讲 18:什么情况下Java程序会产生死锁?如何定位、修复?

    个人博客网址: https://colablog.cn/

    如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您

    a7da066cf59efe4ac51c14bf9d2828b8.png
    展开全文
  • java 并发编程实战

    2018-07-30 01:04:23
    java 并发编程实战高清pdf java 并发编程实战高清pdf
  • 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章 并发Bug源头在计算机系统中,程序的执行速度为: CPU > 内存 > I/O设备 ,为了平衡这三者的速度差异,计算机体系机构、操作系统、...

    摘要

    编写正确的并发程序对我来说是一件极其困难的事情,由于知识不足,只知道synchronized这个修饰符进行同步。 本文为学习极客时间:Java并发编程实战 01的总结,文章取图也是来自于该文章

    并发Bug源头

    在计算机系统中,程序的执行速度为: CPU > 内存 > I/O设备 ,为了平衡这三者的速度差异,计算机体系机构、操作系统、编译程序都进行了优化:

    1.CPU增加了缓存,以均衡和内存的速度差异
    2.操作系统增加了进程、线程,已分时复用CPU,以均衡 CPU 与 I/O 设备的速度差异
    3.编译程序优化指令执行顺序,使得缓存能够更加合理的利用。

    但是这三者导致的问题为:可见性、原子性、有序性

    源头之一:CPU缓存导致的可见性问题

    一个线程对共享变量的修改,另外一个线程能够立即看到,那么就称为可见性。 现在多核CPU时代中,每颗CPU都有自己的缓存,CPU之间并不会共享缓存;

    如线程A从内存读取变量V到CPU-1,操作完成后保存在CPU-1缓存中,还未写到内存中。
    此时线程B从内存读取变量V到CPU-2中,而CPU-1缓存中的变量V对线程B是不可见的
    当线程A把更新后的变量V写到内存中时,线程B才可以从内存中读取到最新变量V的值

    上述过程就是线程A修改变量V后,对线程B不可见,那么就称为可见性问题。

    3a11c3f7a7b6f9468acc6ffc75b04dc3.png

    源头之二:线程切换带来的原子性问题

    现代的操作系统都是基于线程来调度的,现在提到的“任务切换”都是指“线程切换”
    Java并发程序都是基于多线程的,自然也会涉及到任务切换,在高级语言中,一条语句可能就需要多条CPU指令完成,例如在代码 count += 1 中,至少需要三条CPU指令。

    指令1:把变量 count 从内存加载到CPU的寄存器中
    指令2:在寄存器中把变量 count + 1
    指令3:把变量 count 写入到内存(缓存机制导致可能写入的是CPU缓存而不是内存)

    操作系统做任务切换,可以发生在任何一条CPU指令执行完,所以并不是高级语言中的一条语句,不要被 count += 1 这个操作蒙蔽了双眼。假设count = 0,线程A执行完 指令1 后 ,做任务切换到线程B执行了 指令1、指令2、指令3后,再做任务切换回线程A。我们会发现虽然两个线程都执行了 count += 1 操作。但是得到的结果并不是2,而是1。

    98b7bf85a4db506b5458f3a7d27287e8.png

    如果 count += 1 是一个不可分割的整体,线程的切换可以发生在 count += 1 之前或之后,但是不会发生在中间,就像个原子一样。我们把一个或者多个操作在 CPU 执行的过程中不被中断的特性称为原子性

    源头之三:编译优化带来的有序性问题

    有序性指的是程序按照代码的先后顺序执行。编译器为了优化性能,可能会改变程序中的语句执行先后顺序。如:a = 1; b = 2;,编译器可能会优化成:b = 2; a = 1。在这个例子中,编译器优化了程序的执行先后顺序,并不影响结果。但是有时候优化后会导致意想不到的Bug。
    在单例模式的双重检查创建单例对象中。如下代码:

    public class Singleton {
        private static Singleton instance;
        private Singleton() {}
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    问题出现在了new Singletion()这行代码,我们以为的执行顺序应该是这样的:

    指令1:分配一块内存M
    指令2:在内存M中实例化Singleton对象
    指令3:instance变量指向内存地址M

    但是实际优化后的执行路径确实这样的:

    指令1:分配一块内存M
    指令2:instance变量指向内存地址M
    指令3:在内存M中实例化Singleton对象

    这样的话看出来什么问题了吗?当线程A执行完了指令2后,切换到了线程B,
    线程B判断到 if (instance != null)。直接返回instance,但是此时的instance还是没有被实例化的啊!所以这时候我们使用instance可能就会触发空指针异常了。如图:

    f7594d544745cd8438d70d453a492d63.png

    总结

    在写并发程序的时候,需要时刻注意可见性、原子性、有序性的问题。在深刻理解这三个问题后,写起并发程序也会少一点Bug啦~。记住了下面这段话:CPU缓存会带来可见性问题、线程切换带来的原子性问题、编译优化带来的有序性问题。

    参考文章:极客时间:Java并发编程实战 01 | 可见性、原子性和有序性问题:并发编程Bug的源头

    个人博客网址: https://colablog.cn/

    如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您

    9fc1a6e538c13fcede9c0dc4848a46ba.png
    展开全文
  • Java 并发 编程 实战

    2018-02-03 16:11:09
    Java并发编程实战, Java并发编程实战,高清,可以使用
  • Java 并发编程实战PDF

    2017-11-02 16:59:18
    Java 并发编程实战PDF,Java 并发编程实战PDF,Java 并发编程实战PDF
  • java并发编程实战

    2019-04-23 13:47:10
    java并发编程实战..
  • 书籍信息书名:《Java并发编程实战》 原作名:Java Concurrency in Practice 作者:Brian Goetz / Tim Peierls / Joshua Bloch / Joseph Bowbeer / David Holmes / Doug Lea 豆瓣评分:9分 内容介绍本书深入浅出地...

    书籍信息

    • 书名:《Java并发编程实战》
    • 原作名:Java Concurrency in Practice
    • 作者:Brian Goetz / Tim Peierls / Joshua Bloch / Joseph Bowbeer / David Holmes / Doug Lea
    • 豆瓣评分:9分

    66d1e5b4e73f5c69e88dac8f06a16922.png

    内容介绍

    • 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则,如何将小的线程安全类组合成更大的线程安全类,如何利用线程来提高并发应用程序的吞吐量,如何识别可并行执行的任务,如何提高单线程子系统的响应性,如何确保并发程序执行预期任务,如何提高并发代码的性能和可伸缩性等内容,最后介绍了一些高级主题,如显式锁、原子变量、非阻塞算法以及如何开发自定义的同步工具类。

    作者简介

    • 本书作者都是Java Community Process JSR 166专家组(并发工具)的主要成员,并在其他很多JCP专家组里任职。Brian Goetz有20多年的软件咨询行业经验,并著有至少75篇关于Java开发的文章。Tim Peierls是“现代多处理器”的典范,他在http://BoxPop.biz、唱片艺术和戏剧表演方面也颇有研究。Joseph Bowbeer是一个Java ME专家,他对并发编程的兴趣始于Apollo计算机时代。David Holmes是《The Java Programming Language》一书的合著者,任职于Sun公司。Joshua Bloch是Google公司的首席Java架构师,《Effective Java》一书的作者,并参与著作了《Java Puzzlers》。Doug Lea是《Concurrent Programming》一书的作者,纽约州立大学 Oswego分校的计算机科学教授。

    下载地址

    • https://tc5.us/file/18765121-422948429
    展开全文
  • go 并发编程实战 mobi

    2018-09-08 17:05:18
    go 并发编程实战 ,go 并发编程实战 mobi kindle 实测,放心下载
  • 下载地址:内容简介 · · · · · ·《C++并发编程实战》是一本基于C++11新标准的并发和多线程编程深度指南。内容包括从std::thread、std::mutex、std::future和std::async等基础类的使用,到内存模型和原子操作、...

    下载地址:

    0f8ba08504f2eaba4ecbbf7f964cbaee.png

    内容简介 · · · · · ·

    《C++并发编程实战》是一本基于C++11新标准的并发和多线程编程深度指南。内容包括从std::thread、std::mutex、std::future和std::async等基础类的使用,到内存模型和原子操作、基于锁和无锁数据结构的构建,再扩展到并行算法、线程管理,最后还介绍了多线程代码的测试工作。本书的附录部分还对C++11新语言特性中与多线程相关的项目进行了简要的介绍,并提供了C++11线程库的完整参考。 《C++并发编程实战》适合于需要深入了解C++多线程开发的读者,以及使用C++进行各类软件开发的开发人员、测试人员。对于使用第三方线程库的读者,也可以从本书后面的章节中了解到相关的指引和技巧。同时,本书还可以作为C++11线程库的参考工具书。

    补充说明 · · · · · ·

    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。如需删除,请联系 kefu@yanxishe.com

    AI 研习社已经和阿里大文娱、旷视、搜狗搜索、小米等知名公司达成联系,帮助大家更好地求职找工作,一键投递简历至 HR 后台,准备了一些内推渠道群。

    欢迎大家添加研习社小学妹微信(aiyanxishe),小学妹拉你加入(备注求职)。

    fb3b769c40da9d5a91f702e8337008be.png

    雷锋网雷锋网雷锋网

    展开全文
  • 文章系列Java并发编程实战 01并发编程的Bug源头Java并发编程实战 02Java如何解决可见性和有序性问题摘要在上一篇文章02Java如何解决可见性和有序性问题当中,我们解决了可见性和有序性的问题,那么还有一个原子性...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,093
精华内容 2,837
关键字:

并发编程实战