当多个线程共享一个全局变量,对其作写操做时,可能会受到其余线程的干扰,从而引起线程安全问题java
内置锁也叫互斥锁,能够保证线程的原子性,当线程进入方法时,会自动得到一个锁,一旦锁被得到,其余线程必须等待得到锁的线程执行完代码释放锁,会下降程序的执行效率缓存
使用方式:安全
同步方法markdown
public synchronized void sale() { if (trainCount > 0) { System.out.println(Thread.currentThread().getName() + "票" + (100 - trainCount + 1) + "张票"); trainCount --; } } // 非静态同步方法使用this锁 // 静态同步方法使用当前字节码文件
同步代码块多线程
private Object obj = new Object(); .... public void sale() { // 参数为任意全局对象 synchronized (obj) { if (trainCount > 0) { System.out.println(Thread.currentThread().getName() + "票" + (100 - trainCount + 1) + "张票"); trainCount --; } } }
注意事项:ide
public static void main(String[] args) { ThreadDemo1 threadDemo1 = new ThreadDemo1(); ThreadDemo1 threadDemo2 = new ThreadDemo1(); Thread t1 = new Thread(threadDemo1, "窗口1"); Thread t2 = new Thread(threadDemo2, "窗口2"); t1.start(); t2.start(); } // 这样的话也会产生线程安全问题 // 那是由于两个线程分别由不一样的线程建立的,它们之间的变量不共享,产生了两把不一样的锁 // 解决方法是在全局变量上加上 static 关键字,静态变量存在方法区,这个类中的全部对象都共享同一个变量
重入锁和不可重入锁性能
重入锁:即得到锁的线程能够进入它拥有的锁的同步代码块优化
不可重入锁:即得到锁的线程,在方法中尝试再次得到锁时,获取不到进入阻塞状态this
死锁产生的缘由线程
同步中嵌套同步,同步锁是一个重入锁,就颇有可能发生死锁
为每一个线程提供局部变量,解决线程安全问题
ThreadLocal 底层采用 Map 来实现,将当前线程做为key,将值存储到这个 map 中
class Res { private Integer count = 0; ThreadLocal<Integer> integerThreadLocal = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return 0; } }; public Integer getCount() { int count = integerThreadLocal.get() + 1; integerThreadLocal.set(count); return count; } }
什么是原子性?
即一个或一组操做,要么所有执行,执行过程当中不会被其余线程打断,要么所有不执行
什么是可见性?
多线程操做中一个线程修改了全局共享变量的值,其余线程能立马获得修改后的值
什么是有序性?
程序执行的顺序按照代码的前后顺序执行,通常来讲处理器为了提升程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行前后顺序同代码中的顺序一致,可是它会保证程序最终执行结果和代码顺序执行的结果是一致的
Java 内存模型(JMM)
JMM 决定一个线程对一个共享变量作写操做时,可否对另外一个线程可见
主内存:共享变量
本地内存:共享变量副本
多线程作修改操做时,首先从主内存中拷贝一份副本到本地内存中,当线程修改本地内存的值后,首先在本地内存修改为功,而后再将修改后的结果刷新到主内存中
可见性就是说一旦某个线程修改了被 Volatile 修饰的变量,其余线程能立马获取到修改后的新值,在 Java 中为了加快程序的运行效率,对一些变量的操做一般是在该线程的寄存器或者 CPU 缓存中进行的,以后才会同步到主存,而加了 Volatile 关键字后会直接读写内存
注意:虽然该关键字可以保证可见性,但不能保证原子性
特性:
Volatile 与 Synchronized 区别:
Volatile 虽然能保证可见性,但不能保证原子性
Synchronized 防止多个线程执行同一块代码,影响执行效率,就性能而言,Volatile 是高于 Synchronized 的。
可是 Volatile 是不能取代 Synchronized 的,由于 Volatile 不能保证原子性。
数据依赖
若是两个操做同时操做一份变量,且这两个操做其中有一个写操做,此时这两个操做之间就存在数据依赖
名称 | 代码示例 | 说明 |
---|---|---|
写后读 | a = 1;b = a; | 写一个变量以后,再读这个位置。 |
写后写 | a = 1;a = 2; | 写一个变量以后,再写这个变量。 |
读后写 | a = b;b = 1; | 读一个变量以后,再写这个变量 |
上面三种状况,只要更改操做的执行顺序,结果就会发生改变,编译器和处理器可能会作重排序,在作重排序时会尊徐数据依赖,因此编译器和处理器不能对数据依赖的操做重排序,这里的数据依赖性仅针对单个处理器中执行的指令序列和单线程的操做,不一样处理器之间和不一样线程之间的数据依赖性不被编译器和处理器考虑
as-if-serial语义
无论怎么重排,结果都不能改变
对多线程的影响
在单线程程序中,对存在数据依赖的操做作重排序不会影响结果,由于单线程尊徐 as-if-serial 语义,但 as-if-serial 语义提到"不考虑不一样处理器之间和不一样线程之间的数据依赖",因此,指令重排可能会对多线程的结果产生影响