内置的锁
- Java 提供了一种内置的锁机制来支持原子性。
- 每一个 Java 对象都可以用作一个实现同步的锁,称为内置锁,线程进入同步代码块之前自动获取到锁,代码块执行完成正常退出或代码块中抛出异常退出时会释放掉锁。
- 内置锁为互斥锁,即线程 A 获取到锁后,线程 B 阻塞直到线程 A 释放锁,线程 B 才能获取到同一个锁。
- 内置锁使用 synchronized 关键字实现,synchronized 关键字有两种用法:
1、修饰需要进行同步的方法(所有访问状态变量的方法都必须进行同步),此时充当锁的对象为调用同步方法的对象。
2、同步代码块和直接使用 synchronized 修饰需要同步的方法是一样的,但是锁的粒度可以更细,并且充当锁的对象不一定是 this,也可以是其它对象,所以使用起来更加灵活。
同步代码块
/*
就是将可能会发生线程安全问题的代码,给包裹起来。
**/
synchronized(对象){ //这里对象可以是任意对象
需要被同步的代码
}
- 对象如同锁,持有锁的线程可以在同步中执行。
- 没持有锁的线程即使获取 CPU 的执行权,也进不去,因为他没有持有锁。
- 同步的前提:
1、必须要有两个或者两个以上的线程。
2、必须是多个线程使用同一个锁。
3、必须保证同步中只能有一个线程在运行。
好处:解决了多线程安全问题。
坏处:多个线程需要判断锁,比较消耗资源,比较效率低下。
例子
private void sale() {
// 有票继续卖
synchronized(this){
if (fare > 0) {
fare--;
System.out.println(Thread.currentThread().getName() + ":卖出了" + (30 - fare) + "张,还剩:" + fare + "张。");
}
}
}
同步方法
- 什么是同步方法?
答:在方法上修饰 synchronized 称为同步方法。
例子
private synchronized void sale() {
// 有票继续卖
if (fare > 0) {
fare--;
System.out.println(Thread.currentThread().getName() + ":卖出了" + (30 - fare) + "张,还剩:" + fare + "张。");
}
}
静态同步函数
- 什么是静态同步函数?
答:方法上加上 static 关键字,使用 synchronized 关键字修饰或者使用 类.class 。 - 静态的同步函数使用的锁是该函数所属的字节码文件对象。
可以用 getClass 方法获取,也可以用当前 类名.class 表示。
例子
private static void sale() {
synchronized (ThreadDemo103.class) {
// 有票继续卖
if (fare > 0) {
fare--;
System.out.println(Thread.currentThread().getName() + ":卖出了" + (10 - fare) + "张,还剩:" + fare + "张。");
}
}
}
总结
synchronized 修饰方法使用锁是当前 this 锁。
synchronized 修饰静态方法使用的锁是当前类的字节码文件。
END…