引入:

在多线程程序中往往会出现这么一个情况:多个线程同时访问某个线程间的共享变量
案例:
假设银行存款业务写了两个方法,
一个是存钱 store() 方法 ,一个是查询余额 get() 方法。
假设初始客户小明的账户余额为 0 元。

  1. public void store(int money){
  2. int newAccount=account+money;
  3. account=newAccount;
  4. }
  5. public void get(){
  6. System.out.print("小明的银行账户余额:");
  7. System.out.print(account);
  8. }

如果小明为自己存款 1 元,我们期望的线程调用情况如下:

  1. 首先会启动一个线程调用 store() 方法,为客户账户余额增加 1;
  2. 再启动一个线程调用 get() 方法,输出客户的新余额为 1。

但实际情况可能由于线程执行的先后顺序,出现如图所示的错误:
image.png

这就是一个典型的由共享数据引发的并发数据冲突问题

解决方式也很简单,让并发执行会产生问题的代码段不并发行了。
如果 store() 方法 执行完,才能执行 get() 方法,而不是像上图一样并发执行,自然不会出现这个问题。那如何才能做到呢?
答案就是使用 synchronized 关键字。

我们可以设置某个锁,锁会有两种状态,分别是上锁解锁。在 store() 方法执行之前,先观察这个锁的状态,如果是上锁状态,就进入阻塞,代码不运行;
对于 get() 方法也是如此。

synchronized 使用方法

synchronized 一共有三种使用方法:

1、直接修饰某个实例方法

**
在这种情况下多线程并发访问实例方法时,如果其他线程调用同一个对象的被 synchronized 修饰的方法,就会被阻塞。相当于把锁记录在这个方法对应的对象上。

  public synchronized void store(int money){
        int newAccount=account+money;
        account=newAccount;
    }
    public synchronized void get(){
        System.out.print("小明的银行账户余额:");
        System.out.print(account);
    }

2、直接修饰某个静态方法

在这种情况下进行多线程并发访问时,如果其他线程也是调用属于同一个类的被 synchronized 修饰的静态方法,就会被阻塞。相当于把锁信息记录在这个方法对应的类上。

    public synchronized static void get(){
        ···
    }

3、修饰代码块

如果此时有别的线程也想访问某个被synchronized(对象0)修饰的同步代码块时,也会被阻塞。

  public static void get(){
        synchronized(对象0){
            ···
        }
    }