目录
2.2系统角度分析线程不安全的原因------内存可见性问题(memory visiable)
2.3 系统角度分析线程不安全的原因------代码重排序导致的问题
1.什么是线程安全?
就是运行结果100%符合预期。
线程不安全就无法得到100%预期的结果
2线程安全出现的原因。
1.站在开发者的角度
a:多个线程之间操作了同一块数据(共享数据),不仅仅是内存数据
b.至少有一个线程在修改这块共享数据,也就是多个线程中至少有一个对共享数据做修改的线程。
而在多线程中不需要考虑线程安全问题:
a.几个线程之间互相没有任何数据共享的情况下,天生是线程安全的。
b.几个线程之间即使有共享数据,但都是做读数据,没有写操作,也是天生线程安全的。
2.系统角度解释
a.破环了原子性(最常见的原因)
b.内存可见性问题
一些线程对数据的操作,很可能其他线程无法感知,甚至某些情况下,会被优化成完全看不到的结果(也就是程序读到了脏数据)
c.代码重排序导致的问题
我们写的程序经过中间很多环节的结果,并不保证最终执行的语句和我们最初的语句一模一样
2.1系统角度分析线程不安全的原因------原子性被破坏
代码展示:
问题:
2.2系统角度分析线程不安全的原因------内存可见性问题(memory visiable)
学习委员拿到了脏数据
2.3 系统角度分析线程不安全的原因------代码重排序导致的问题
2.4JVM中的代码重排序的基本原则
3.常见类的线程安全问题
ArrayList、LinkedList、PriorityQueue、TreeMap、TreeSet、HashMap、HashSet、StringBuilder都不是线程安全的
线程安全的类有Vector、Stack、Dictionary、StringBuffer。
这几个类都是Java设计的失败品,以后代码尽量不要用。
StringBuilder与StringBuffer的区别是:一个线程安全一个线程不安全
4.保护线程安全的措施------锁(Lock)
所谓的锁:就是一段数据(一段被多个线程之间共享的数据)
释放锁的操作(纯理论):
这个系统由系统保证了原子性。
当多个线程都有加锁操作时,并且申请的是同一把锁时:
synchronize 锁、同步锁、monitor锁(监视器锁)
语法:
1.修饰方法(普通方法、静态方法)=》同步方法
synchronize int add(...){...}
2.同步代码块
synchornize (引用){...}
5.关于锁的应用
6.锁的介绍
synchronized是一种非常早期存在的锁,后期重新设计过,以类、对象的形式给我们使用。
现在我们java并发编程尽量用这个包下面的提供的工具,java.util.cincurrent.*(JUC包)
java.util.concurrent.locks.Lock (相比于sync锁,加锁策略更灵活)
lock(); //等同于sync锁
lockInterruptibly(); //加锁,允许被中断
tryLock(); //加锁失败后返回false
tryLock(long time,TimeUnit unit); //加锁失败后返回false,尝试等待一段时间,这段时间过去之后,加锁失败。
unlock(); //解锁操作
7.synchronized锁与juc下锁的比较
1.保证了临界区的原子性(可以加锁使得原子性的代码之间互斥来实现)(90%,最重要)
加锁的粒度可以不同。
2.可以保证内存的可见性(在有限程度上)(5%)
3.可以给代码重排序增加一定的约束(5%)
8.死锁
即主线程在调用t.join()在等待结束,子线程在调用lock.lock()等待主线程(所持有的线程)释放锁。
这里的死锁和操作系统不同,操作系统是死锁与资源分配有关
9. 更加细化的线程状态
我们可以看出,当线程处于waiting状态的时候。说明线程正在等待锁,或者等待子线程执行结束。
blocked专指请求sync锁失败的状态。
更加细致的状态转移图:
问:操作系统的死锁是什么?
还有个问题?
t.join()等待的是子线程吗?