1.线程安全的概念
想要给出一个确切的线程安全定义是复杂的,但我们可以这样认为:如果多线程环境下代码运行的结果是符合我们预期的,即在单线程环境应该的结果,则说这个程序是线 程安全的。
2.线程不安全的原因
1.修改共享数据
通俗一点来说就是多个线程修改同一个数据
2.原子性
简单来说原子性就是叫做同步互斥,表示操作是互相排斥的。
不保证原子性会给多线程带来什么问题呢
如果一个线程正在对一个变量操作,中途其他线程插入进来了,如果这个操作被打断了,结果就可能是 错误的。
3.可见性
可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.
线程之间的共享变量存在 主内存 (Main Memory).
每一个线程都有自己的 "工作内存" (Working Memory) .
当线程要读取一个共享变量的时候, 会先把变量从主内存拷贝到工作内存, 再从工作内存读取数据. 当线程要修改一个共享变量的时候, 也会先修改工作内存中的副本, 再同步回主内存.
由于每个线程有自己的工作内存, 这些工作内存中的内容相当于同一个共享变量的 "副本". 此时修改线程1 的工作内存中的值, 线程2 的工作内存不一定会及时变化.这个时候代码中就容易出现问题.
此外 代码的顺序在多线程中也会影响到线程安全
3.解决方案
相关思路就是给线程加锁,比如说:在多个线程修改同一个变量时 给线程加一把锁 确保当前的执行线程在修改变量这个操作上不会被其他线程 打断 等该线程执行完后其他线程才能开始.
synchronized 关键字-监视器锁monitor lock
synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待.
进入 synchronized 修饰的代码块, 相当于 加锁
退出 synchronized 修饰的代码块, 相当于 解锁
volatile 关键字
volatile 修饰的变量, 能够保证 "内存可见性".加上 volatile , 强制读写内存. 速度是慢了, 但是数据变的更准确了.
volatile 不保证原子性
volatile 和 synchronized 有着本质的区别. synchronized 能够保证原子性, volatile 保证的是内存可见性.
wait 和 notify
由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.
完成这个协调工作, 主要涉及到三个方法
wait() / wait(long timeout): 让当前线程进入等待状态.
notify() / notifyAll(): 唤醒在当前对象上等待的线程.