Java多线程编程(5)-volatile和synchronized比较

volatile和synchronized比较

     1. volatile只能保证共享变量的可见性,不能保证共享变量操作的原子性;volatile不需要锁,比synchronized轻量级,不会阻塞线程。

     2.从内存可见性来说,volatile读相当于对共享变量加锁,写相当于对共享变量解锁。

     3.synchronized既可以保证共享变量的可见性,也可以保证原子性。

     4.除了用synchronized,也可以用 java.util.concurrent.locks.Lock。

几个概念

     1.共享变量: 一个变量如果在多个线程中都存有副本,那么这个变量就是这几个线程的共享变量。共享变量的访问权限用private修饰。

     2.可见性:一个线程对共享变量值的修改,能够及时地被其它线程看到。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存,所以对其他线程是可见的。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,依然可能导致线程不安全。

     3.原子性:不可分割的操作,可以保证线程运行的安全。


volatile导致线程不安全的示例

    运行程序几次程序,可以看出volatileNumber最终可能并不是200,线程不安全。

public class VolatileExample {

  // 线程共享变量须定义成private访问权限,volatile不能保证原子性
  private volatile int volatileNumber = 0;
    
  public static void main(String[] args) {
    final VolatileExample ve = new VolatileExample();
    for (int i = 0; i < 200; i++) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          ve.increase();
        }
      }).start();
    }

    // 如果还有子线程在运行,主线程就让出资源。
    // 等所有的子线程运行结束,主线程继续执行
    while (Thread.activeCount() > 1) {
      Thread.yield();
    }
    
    // 经过500个循环,发现volatileNumber输出结果可能不是200
    System.out.println("num is:\t" + ve.getVolatileNumber());
    }
  
   /**
   * 对volatileNumber进行++操作
   */
  public void increase() {
    try {
      Thread.sleep(1);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    this.volatileNumber++;
  }
  
  public int getVolatileNumber() {
    return this.volatileNumber;
  }


synchronized可以保证原子性

public class SynExample {

  // 线程共享变量须定义成private访问权限
  private int synNumber = 0;
  
  public static void main(String[] args) {
    final SynExample example = new VolatileExample();
    for (int i = 0; i < 200; i++) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          example.increaseSynNumber();
        }
      }).start();
    }

    // 如果还有子线程在运行,主线程就让出资源。
    // 等所有的子线程运行结束,主线程继续执行
    while (Thread.activeCount() > 1) {
      Thread.yield();
    }
    // 使用synchronized关键字后,是原子操作,synNumber 输出结果一定200
    System.out.println("synNumber is:\t" + example.getSynNumber());
  }

  
  /**
   * 对synNumber进行++操作,使用synchronized
   */
  public void increaseSynNumber() {
    synchronized (this) {
      this.synNumber++;
    }
  }

  public int getSynNumber() {
    return this.synNumber;
  }

}


java.util.concurrent.locks.Lock保证原子性

   java.util.concurrent.locks.Lock封装了synchronized,提供了更丰富的功能。

public class LockExample {

    private int lockNumber = 0;
    
    public static void main(String[] args) {
        final LockExample example= new LockExample();
        for (int i = 0; i < 200; i++) {
          new Thread(new Runnable() {
            @Override
            public void run() {
              example.increaseLockNumber();
           }
          }).start();
    }

    // 如果还有子线程在运行,主线程就让出资源。
    // 等所有的子线程运行结束,主线程继续执行
    while (Thread.activeCount() > 1) {
      Thread.yield();
    }
   
    // 发现使用Lock后,是原子操作,lockNumber输出结果一定200
    System.out.println("locknumber is:\t" + example.getLockNumber());
  }
  
    /**
   * 对lockNumber进行++操作,使用lock
   */
  public void increaseLockNumber() {
    lock.lock();
    try {
      this.lockNumber++;
    } finally {
      lock.unlock();
    }
  }
  
  public int getLockNumber() {
    return this.lockNumber;
  }
   
}



  

你可能感兴趣的:(java,多线程,thread,synchronized,volatile,Lock)