Java高并发程序-Chapter2 Java并行程序基础 (第八讲)线程组 、守护线程、线程优先级

时间:2022-12-25 10:47:41

线程安全的概念与 synchronized

1. 指定加锁对象

指定加锁对象:对给定对象加锁,进入同步代码前要获得给定对象的锁

public class AccountSyncByInstance implements Runnable {

  public void run() {

    for (int j = 0; j < 100000; j++) {

     synchronized (this) {
       i++;
     }
     
     
    }

  }

  public int getSummary() {

    return i;
  }

  private int i = 0;

  public static void main(String[] args) throws InterruptedException {

    AccountSyncByInstance instance = new AccountSyncByInstance();

    Thread t1 = new Thread(instance);
    Thread t2 = new Thread(instance);
    Thread t3 = new Thread(instance);
    
    t1.start();
    t2.start();
    t3.start();
    
    t1.join();
    t2.join();
    t3.join();
    
    System.out.println(instance.getSummary());

  }
}

2. 直接作用于实例方法

相当于对当前实例加锁,进入同步代码前要获得当前实例的锁

public class AccountSyncByMethod implements Runnable {

  public void run() {

    for (int j = 0; j < 100000; j++) {

     synchronized (this) {
       
       increase();
     }

    }

  }

  private synchronized void increase() {
    i++;
  }

  public int getSummary() {

    return i;
  }

  private int i = 0;

  public static void main(String[] args) throws InterruptedException {

    AccountSyncByMethod instance = new AccountSyncByMethod();

    Thread t1 = new Thread(instance);
    Thread t2 = new Thread(instance);
    Thread t3 = new Thread(instance);

    t1.start();
    t2.start();
    t3.start();

    t1.join();
    t2.join();
    t3.join();

    System.out.println(instance.getSummary());

  }
}

3. 直接作用于静态方法

直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁

一种错误的同步方式如下:

public class AccountSyncByBad implements Runnable {

  public void run() {

    for (int j = 0; j < 100000; j++) {

     synchronized (this) {

       increase();
     }

    }

  }

  private synchronized void increase() {
    i++;
  }

  public int getSummary() {

    return i;
  }

  private static int i = 0;

  public static void main(String[] args) throws InterruptedException {

    Thread t1 = new Thread(new AccountSyncByBad());
    Thread t2 = new Thread(new AccountSyncByBad());
    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(i);

  }
}

虽然在第3行的 Increase()方法中,申明这是一个同步方法。

但很不幸的是,执行这段代码的两个线程都指向了不同的 Runnable实例。

   Thread t1 = new Thread(new AccountSyncByBad());
   Thread t2 = new Thread(new AccountSyncByBad());
由第13、14行可以看到,这两个线程的 Runnable实例并不是同一个对象。
因此,线程t1会在进入同步方法前加锁自己的 Runnable实例,而线程t2也关注于自己的对象锁。
换言之,这两个线程使用的是两把不同的锁。因此,线程安全是无法保证的。

但我们只要简单地修改上述代码,就能使其正确执行。

那就是使用 synchronized的第三种用法,将其作用于静态方法

private static synchronized void increase() {
    i++;
}
除了用于线程同步、确保线程安全外, synchronized还可以保证线程间的可见性和有序性。