1.认识线程安全
怎么产生的线程安全?
- 多个线程共享同一全局变量,做写操作时,如不做其他干预,可能会受其他线程干扰,导致数据错乱。
怎么解决线程安全?
- 既然是多个线程对同一资源做写操作,那我们只要保证当A线程操作时B线程不能参与进来即可,JAVA中一般采用同步机制解决。
1.1.线程安全三大特性
1.1.1.原子性:
原子性是指操作不可分割,表现在对于共享变量的操作应该是不可分的,必须连续完成
如:a++操作,实际上JVM会分三步完成
1.读取a的值
2.a值+1
3.将值赋予变量a
- 期间任何一个操作异常都可能导致a值被篡改,出现线程安全问题。
如果业务中有需要变量++的操作,岂不是都需加锁保证原子性?在JAVA1.8中有如下原子类
jdk1.5中推荐使用AutomicInteger原子类,java1.8推荐使用LongAdder
原子类底层采用CAS无锁机制,天生免疫死锁。
1.1.2.有限性:
有序性是指程序在执行的时候,程序的代码执行顺序和语句的顺序是一致的。
疑问:那为什么会出现不一致现象呢?- 由于重排序的缘故,执行程序时,为提高性能,编译器和处理器会对指令进行重排序
- 重排序不会影响单线程的执行结果,但是在并发情况下,可能会出现诡异的BUG。
1.1.3.可见性:
可见性是一线程对共享变量的修改,另一个线程能够立即看到。
JAVA内存模型(JMM):JVM为了提高程序的执行效率,会对我们的程序进行优化,把经常需要被访问的变量存储在我们的缓存当中,也就是CPU中的寄存器(Cache)里,而避免直接去内存里读。多线程操作数据时,分为主内存和私有内存,如下图

1.2.锁机制(JVM锁)
synchronized 关键字:
- 用法:可在代码块和方法函数中加上改关键字。
- 静态同步函数使用的是当前字节码文件,而非静态同步函数是this锁。
- 线程执行成功后或执行过程中发生异常都会自动释放锁
- 在保证线程安全的同时会导致其它线程的阻塞
缺点:效率较低,扩展性不高,不能自定义。
lock锁:
- 用法:
ReentrantLock lock= new ReentrantLock();- 使用
lock.lock();上锁,使用lock.unlock();释放锁。 - 1、线程取锁失败后会进入等待状态,超过指定时间后会直接返回false,而不会像synchronized一样阻塞其它线程
2、程序执行完毕或者出现异常时需要手动释放锁,否则会出现死锁
3、可中断锁
4、默认采用非公平锁,根据需求可以设置成公平锁,而synchronized只能是非公平锁
公平锁:线程取锁失败后会进入等待队列,先进入队列的线程会先获得锁
非公平锁:线程取锁失败后会进入等待队列,但等待线程取锁的概率是随机的
- 使用
1.3.总结
了解线程安全是什么
了解原子类
了解synchronized 关键字
了解lock锁