公司的项目开始用到多线程。有用过多线程,但是了解不深 又去学习了下  学习资料:王宝令《java并发编程实战》 看完且搞懂前15节基本就都明白了

大概总结一下:

并发编程会导致线程不安全,常说的线程不安全指的是  多个线程操作一个共享数据,导致线程之间的读取到的数据不一致。

1.并发编程导致线程不安全的根源  

 1 可见性      cpu缓存导致。 一般cpu缓存中进行操作之后再将数据写到内存,在多核服务器中  每个线程都会分配一个cpu  都会在各自的cpu中进行处理再将数据统一写到内存中。每个cpu缓存中的数据对于其他的线程,都是不可见的。导致最后写入内存,然后再从内存中读出来时候数据不一致。

  解决办法:禁用缓存,使用volidate 关键字和 happen-before 规则就可以避免

  volidate:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入

  happen-before :前面一个操作的结果对后续操作是可见的

 2 原子性      线程切换导致,在java语句中一行语句在计算机底层进行了多次操作。比如创建一个对象,new 在堆中开辟一块空间  将地址赋予这个对象  最后才会在这个空间上初始化对象。如果只是进行到地址值赋予对象,并未创建出对象,此时发生线程的切换  在运行的时候就会出翔空指针的情况。就可能会报错

  解决办法:用synchornized

 3有序性       编译优化产生。代码在JVM上执行的时候都会进行一定的编译优化,我们所写的代码在优化之后  执行顺序可能变换,可能在创建对象的时候  先是将地址指向了对象然后再分配的内存空间。

  解决办法:禁用编译优化

理论上 那全部用synchornized,禁用编译优化和缓存,完美解决所有问题。但是在实际开发工程中,显然不现实,我们需要考虑到性能。

 

大家也许都知道,实际上所有的并发问题都可以用信号量来解决。这个信号量实际上和管程是一样的 ,都能用来处理并发问题。

管程是一种模型,是一把解决并发的万能钥匙,Java中的synchronized时间上就是采用了管程的思想。配合 wait() notify().notifyAll().

在synchronized下面会自动的加上  lock    unlock的操作,必须要注意的是  加锁的资源一定要不变。对象是this,整个类为字节码对象。

比如 a转账给b 两个对象的账户都要被锁定。怎么办?

这个时候先锁定  a   然后再锁定b.如果在a转给 b,a账户被锁定,等待锁定b账户,b此刻也要转给a,b锁定等待锁定a.此刻,就出现了一组资源相互等待各自所需资源,而出现的一组永久循环等待的现象。

这就是死锁!

一般发生死锁没有什么好的办法  一般只有重启服务器。所以只能去避免死锁的发生。

首先要搞清楚  发生死锁的必要条件

1  互斥

2 占用且等待

3 不可抢占

4 循环等待 

 理论上只要打破其中的一个条件就能避免死锁,但是  我们 用锁就是希望互斥,所以这个没办法去处理  不可抢占的特性可以使用第三方的sdk来解决。

现在讲一下占用且等待。实际上 ,我们不要一个一个去锁定,而是等到拿到两个账户了一起锁定。比如我们可以创建一个单例的工具类去获取这两个对象,如果只拿到其中的一个的话都不会锁定。

对于循环等待的话  我们可以定一个一定的规则来进行排序 如果需要一组细粒度锁 会将两个锁进行排序 从小到大的顺序进行加锁 这样也能避免这个循环等待的问题

总之,在使用细粒度锁一定要注意死锁的问题。

 其实不只是死锁,也会有活锁和饥饿两种情况也会导致线程执行不下去的情况。

活锁指的是当

posted on 2019-04-09 12:10  Mr.xiab  阅读(594)  评论(0编辑  收藏  举报