内置的锁

  • 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…