编程语言
首页 > 编程语言> > 049.JAVA线程_线程同步

049.JAVA线程_线程同步

作者:互联网


博主的 Github 地址


1. 线程同步的方式

有三种保证线程同步的方法:

1.1. 同步代码块

1.1.1. 语法

synchronized(/*LOCK*/){
  //TODO...
}

1.1.2. 同步锁的概念

1.1.3. 同步代码块实例

1.1.4. 注意事项


1.2. 同步方法

1.2.1. 语法

synchronized function(para){
  //TODO...
}

1.2.2. 同步方法中的同步锁

1.2.3. 同步方法的实例

class Apple implements Runnable {

    private int apple_num = 10;

    public void run() {
        while (true) {
            eatApple();
            if (apple_num == 0) break;
        }
    }

    //定义一个锁对象, 用其实现类来进行构造对象.
    private final Lock lock = new ReentrantLock();

    public void eatApple() {
        //进入方法的时候就上锁
        lock.lock();

        try {
            //再次检验苹果数量
            if (apple_num <= 0) return;

            Thread.sleep(10);
            System.out.printf("the apple[%d] is eaten by [%s]\n", apple_num--, Thread.currentThread().getName());
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            //方法执行完后必须释放锁
            lock.unlock();
        }
    }
}

1.2.4. 注意事项


1.3. 同步锁机制/Lock 接口

1.3.1. 用 Lock 机制修改后的吃苹果实例

class Apple implements Runnable {

  private int apple_num = 10;

  public void run() {
      while (true) {
          eatApple();
          if (apple_num <= 0) break;
      }
  }

  //定义一个锁对象, 用其实现类来进行构造对象. 
  private final Lock lock = new ReentrantLock();

  public void eatApple() {
    //进入方法的时候上锁
    lock.lock();

    try {
        Thread.sleep(10);
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
      //方法执行完后必须释放锁
      lock.unlock();
    }
    System.out.printf("the apple[%d] is eaten by [%s]\n", apple_num--, Thread.currentThread().getName());
  }
}

1.3.2. 同步锁池的概念

2. synchronized 的引申拓展

2.1. synchronized 的优缺点

2.1.1. 优点

保证了多线程并发访问时的同步操作, 避免线程的安全问题.

2.1.2. 缺点

使用 synchronized 修饰的代码块/方法更加耗费性能.

2.1.3. 实例

2.2. 懒加载同步的优化

2.3. 双重检查锁机制

public static TestUtils getInstance() { if(instance == null){ synchronized(TestUtils.class){ if(instance == null){ instance = new TestUtils(); } } } return instance; }


#### 2.3.1. 双重检查加锁机制的原理  
- 并不是每次进入 `getInstance` 方法都需要同步, 而是先不同步;   
- 进入方法后, 先检查实例是否存在, 如果不存在才进行下面的同步块, 这是第一重检查;  
- 进入同步块过后, 再次检查实例是否存在, 如果不存在, 就在同步的情况下创建一个实例, 这是第二重检查;  
- 这样一来, 就只需要同步一次了, 从而减少了多次在同步情况下进行判断所浪费的时间.  

#### 2.3.2. volatile 关键字
- "双重检查加锁"机制的实现会使用关键字 `volatile`, 它的作用是:  
  被 `volatile` 修饰的变量的值, 将不会被本地线程缓存,   
  所有对该变量的读写都是直接操作共享内存, 从而确保多个线程能正确的处理该变量.  

#### 2.3.3. 关于双重检查锁机制的注意事项
- 注意:  
  在 java1.4 及以前版本中, 很多 JVM 对于 `volatile` 关键字的实现的问题,  
  会导致"双重检查加锁"的失败, 因此"双重检查加锁"机制只只能用在 java5 及以上的版本. 

- 提示:  
  由于 `volatile` 关键字可能会屏蔽掉虚拟机中一些必要的代码优化, 所以运行效率并不高.   
  因此一般建议,  没有特别的需要, 不要使用. 也就是说, 虽然可以使用"双重检查加锁"机制  
  来实现线程安全的单例, 但并不建议大量采用, 可以根据情况来选用. 更推荐使用饿汉式.

标签:同步,JAVA,synchronized,代码,线程,049,方法,public
来源: https://blog.csdn.net/leon9dragon/article/details/115564195