多个线程同时访问 同一个共享变量 时 , 只要能保证 数据一致性 , 那么该变量是线程安全的 ; 这里的数据是指主内存中的共享变量以及各个线程中的变量副本 , 保证这些变量一致 , 就是线程安全 ;
线程安全 就是保证 线程操作的 原子性 , 可见性 , 有序性 ;
volatile 关键字可以保证 可见性 与 有序性 ;
synchronized 关键字可以保证 原子性 ;
synchronized
是 Java 提供的一种锁机制 ;
在普通方法上加锁 , 相当于对 this
进行加锁 ; 下面两个类的 fun 方法的线程锁是等效的 ;
public class Student {
private synchronized void fun() {
}
}
public class Student {
private void fun() {
synchronized(this){
}
}
}
加锁的代码块 , 在同一个时间 , 只能由
个线程访问 ;
对象锁 : synchronized()
代码块中 , 括号中的参数是 作用范围 ; synchronized(this)
表示作用范围只针对当前对象 , 如果 创建了多个对象 , 这几个对象中的锁都是 不同的锁 , 相互之间没有任何关系 ;
Student s1 = new Student();
Student s2 = new Student();
只有当多个线程 , 访问同一个对象时 , 锁才有意义 ;
如 : 线程 A 访问 s1 对象的 fun 方法 , 线程 B 访问 s2 对象的 fun 方法 , 两个方法之间 没有互斥效果 ; 线程 A 访问 s1 对象的 fun 方法 , 线程 B 也想访问 s1 对象的 fun 方法 , 此时必须 等待线程 A 访问完毕 , 释放锁之后 , 才能由线程 B 访问 s1 ;
类锁 : 如果加锁的对象是静态方法 , 那么相当于在 Student.class 类上进行加锁 ; Student.class 对象全局只有
个 , 调用所有对象的 fun 方法 , 都是互斥的 ;
public class Student {
private synchronized static void fun() {
}
}
等价于
public class Student {
private static void fun() {
synchronized(Student.class){
}
}
}
如果线程 A 获得锁之后 , 执行线程内容 , 其它线程等待解锁时有两种情况 :
轻量级锁弊端 : 轻量级锁 不一定 比重量级锁 更好 ; 轻量级锁 等待过程中 , 高速执行循环代码 , 如果循环的时间很短 , 时间效率上很高 ; 但是一旦执行时间很长 , 比如连续执行十几秒甚至几分钟 , 浪费了大量的 CPU 资源 ;
使用场景 :