JVM 锁
1、JAVA 为了实现在多线程环境灰姑娘下的线程安全,提供了诸如 synchronized
, ReentrantLock
等工具类来解决我们在多线程环境下的线程安全问题。
synchronized 锁
1、上面是 synchronized锁
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
- 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
线程不安全的实例
public class UnSafeExample {
// frequency
private static final int FREQUENCY = 20;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(Performance.INSTANCE , "thread-1");
Thread thread1 = new Thread(Performance.INSTANCE , "thread-2");
thread.start();
thread1.start();
try {
// 等待两个线程都运行结束后,再打印结果
thread.join();
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Performance.INSTANCE.cnt);
}
enum Performance implements Runnable {
INSTANCE;
int cnt = 0;
@Override
public void run() {
for (int i = 0 ; i < FREQUENCY ; ++i) {
cnt ++ ;
}
}
}
}
1、我们通过 Thread开启了两个线程每个线程的
run方法执行 20 次
cnt ++ 操作,但是实际上当两个线程执行完之后我们发现
assertEquals(cnt , 40)等于
false2、在并发环境下
cnt ++这个操作并不是一个原子性操作,他包含三条指令:读值、+1、写值 3、通过
join方法我们可以实现对
thread1和
thread2线程的阻塞等价于
countdownlatch`
synchronized 锁的使用
public class SafeExample {
// frequency
private static final int FREQUENCY = 20;
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(Performance.INSTANCE , "thread-1");
Thread thread1 = new Thread(Performance.INSTANCE , "thread-2");
thread.start();
thread1.start();
// 模拟延迟
Thread.sleep(100);
System.out.println(Performance.INSTANCE.cnt);
}
enum Performance implements Runnable {
// 单例对象
INSTANCE;
/**
* 1、使用枚举类 INSTANCE 设计单例模式
* 2、使用 volatile 对属性进行修饰---确保多线程环境下的可见性
*/
volatile int cnt = 0;
@Override
public synchronized void run() {
for (int i = 0 ; i < FREQUENCY ; ++i) {
cnt ++ ;
}
}
}
}
1、synchronized
修饰方法锁的是当前对象
2、synchronized
修饰代码块锁的是 synchronized(this)
对象
3、synchronized
所锁的是 Class
对象相当于多个该对象的实例
4、volatile
关键字保证了属性的可见性