zoukankan      html  css  js  c++  java
  • 浅谈Java三种实现线程同步的方法

    浅谈Java三种实现线程同步的方法

    一、概述

    无论是什么语言,在多线程编程中,常常会遇到多个线同时操作程某个变量(读/写),如果读/写不同步,则会造成不符合预期的结果。

    例如:线程A和线程B并发运行,都操作变量X,若线程A对变量X进行赋上一个新值,线程B仍然使用变量X之前的值,很明显线程B使用的X不是我们想要的值了。

    Java提供了三种机制,解决上述问题,实现线程同步:

    • 同步代码块

      synchronized(锁对象){
      	// 这里添加受保护的数据操作
      }
      
    • 同步方法

      • 静态同步方法:synchronized修饰的静态方法,它的同步锁是当前方法所在类的字节码对象
      public static synchronized void staticMethod(){
          
      }
      
      • 非静态同步方法:synchronized修饰的非静态方法,它的同步锁即为this
      public synchronize void method(){
      
      }
      
    • 锁机制

      // 以可重入锁举例
      Lock lock = new ReentrantLock(/*fail*/);  
      // fail: 
      // true表示使用公平锁,即线程等待拿到锁的时间越久,越容易拿到锁
      // false表示使用非公平锁,线程拿到锁全靠运气。。。cpu时间片轮到哪个线程,哪个线程就能获取锁
      lock.lock();
      // 这里添加受保护的数据操作
      lock.unlock();
      

    个人理解:其实无论哪种机制实现线程同步,本质上都是加锁->操作数据->解锁的过程。同步代码块是针对{}中,同步方法是针对整个方法。其ReentrantLock类提供的lock和unlock和C++的std::mutex提供lock和unlock类似

    二、测试用例

    image-20200531205114538

    1. 同步代码块测试类

      package base.synchronize;
      
      
      public class SynchronizeBlock implements Runnable {
          private int num = 100;
      
          @Override
          public void run() {
              while (num > 1) {
                  synchronized (this) {
                      // 同步代码块,只有拿到锁,才有cpu执行权
                      System.out.println("Thread ID:" +  Thread.currentThread().getId() + "---num:" + num);
                      num--;
                  }
              }
              System.out.println("Thread ID:" +  Thread.currentThread().getId() + " exit");
          }
      }
      
      
    2. 同步方法测试类

      package base.synchronize;
      
      public class SynchronizeMethod implements Runnable {
          private int num = 100;
          public static int staticNum = 100;
          boolean useStaticMethod;
      
          public SynchronizeMethod(boolean useStaticMethodToTest) {
              this.useStaticMethod = useStaticMethodToTest;
          }
      
          // 对于非静态方法,同步锁对象即this
          public synchronized void method() {
              System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
              num--;
          }
      
          // 对于静态方法,同步锁对象是当前方法所在类的字节码对象
          public synchronized static void staticMethod() {
              System.out.println("Static Method Thread ID:" + Thread.currentThread().getId() + "---num:" + staticNum);
              staticNum--;
          }
      
          @Override
          public void run() {
              if (useStaticMethod) {  // 测试静态同步方法
                  while (staticNum > 1) {
                      staticMethod();
                  }
              }else{  // 测试非静态同步方法
                  while (num > 1){
                      method();
                  }
              }
              System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
          }
      }
      
    3. ReentrantLock测试类

      package base.synchronize;
      
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      public class SynchronizeLock implements Runnable {
          private Lock lock = null;
          private int num = 100;
      
          public SynchronizeLock(boolean fair){
              lock = new ReentrantLock(fair); // 可重入锁
          }
      
          @Override
          public void run() {
              while (num > 1) {
                  try {
                      lock.lock();
                      System.out.println("Thread ID:" + Thread.currentThread().getId() + "---num:" + num);
                      num--;
                  } catch (Exception e) {
                      e.printStackTrace();
                  }finally {
                      lock.unlock();
                  }
              }
              System.out.println("Thread ID:" + Thread.currentThread().getId() + " exit");
          }
      }
      
      

    测试三种机制的Demo

    package base.synchronize;
    
    public class Demo {
        public static void main(String[] args) {
            synchronizeBlockTest();     // 同步代码块
            synchronizeMethodTest();    // 同步非静态方法
            synchronizeStaticMethodTest();  // 同步静态方法
            synchronizeLockTest();    // 可重入锁机制
        }
    
        public static void synchronizeBlockTest(){
            Runnable run = new SynchronizeBlock();
            for(int i = 0; i < 3; i++){
                new Thread(run).start();
            }
        }
    
        public static void synchronizeMethodTest(){
            Runnable run = new SynchronizeMethod(false);
            for(int i = 0; i < 3; i++){
                new Thread(run).start();
            }
    
        }
        public static void synchronizeStaticMethodTest() {
            Runnable run = new SynchronizeMethod(true);
            for(int i = 0; i < 3; i++){
                new Thread(run).start();
            }
        }
    
    
        public static void synchronizeLockTest(){
            Runnable run = new SynchronizeLock(false);  // true:使用公平锁  false:使用非公平锁
            for(int i = 0; i < 3; i++){
                new Thread(run).start();
            }
        }
    }
    
    

    无论哪种机制,都得到预期的效果,打印100-0

  • 相关阅读:
    QML中 Webview 元素的runJavaScript() 函数的使用方法
    Thinkphp 第二篇:如何将一个外部项目导入到Thimkphp环境中
    AMD电脑装完Winsows10后开机蓝屏,报错代码:cdmsnroot_s.sys
    JVM02——JVM运行时内存
    JVM01——JVM内存区域的构成
    JVM04——七个GC垃圾收集器
    JVM03——四种垃圾回收算法
    Mybatis的缓存机制详解
    mybatis插入insert操作,返回自增id
    mybatis批量查询-插入-删除
  • 原文地址:https://www.cnblogs.com/main404/p/13020726.html
Copyright © 2011-2022 走看看