• 关于JAVA多线程并发synchronized的测试与合理使用


    在项目开发中, 或许会碰到JAVA的多线程处理, 为保证业务数据的正常, 必须加上锁机制,  常用的处理方法一般是加上synchronized关键字, 目前JDK版本对synchronized已经做了很好的优化,  我们不用再考虑其性能,  但在实际使用中,  往往由于处理不当,  导致系统性能的严重下降, 那么该如何合理的使用synchronized,  必须对其使用方式有个全面了解, 在网上搜寻的资料, 给出的是四种使用方式, 其实可总结为两种, 一个是同步代码块, 一个是同步方法体, 那么该如何使用, 请看下面的测试

      准备两个方法,  对同一个变量做加法, 再对每个方法, 分别开十个线程执行:

      [java]

      public class ThreadUnit

      {

      private int i = 0;

      private Object obj1 = new Object();

      private Object obj2 = new Object();

      public synchronized Integer doAdd1(Long start) throws Exception

      {

      Thread.sleep(100);

      ++i;

      Thread.sleep(100);

      return i;

      }

      public Integer doAdd2(Long start) throws Exception

      {

      Thread.sleep(100);

      ++i;

      Thread.sleep(100);

      return i;

      }

      }

      相关代码:

      [java]

      // 十个线程同时执行方法2

      for (int i = 0; i < 10; i++)

      {

      new Thread(new MessageThread(1, threadUnit))。start();

      }

      // 十个线程同时执行方法2

      for (int j = 0; j < 10; j++)

      {

      new Thread(new MessageThread(2, threadUnit))。start();

      }

      线程处理:

      [java]

      public void run()

      {

      try

      {

      if (operate == 2)

      {

      long start = System.currentTimeMillis();

      int i = threadUnit.doAdd2(start);

      long takeTime = System.currentTimeMillis() - start;

      System.out.println("doAdd2() => i=" + i + ", spendTime=" + takeTime + "ms");

      spendTime += takeTime;

      }

      else

      {

      long start = System.currentTimeMillis();

      int i = threadUnit.doAdd1(start);

      long takeTime = System.currentTimeMillis() - start;

      System.out.println("doAdd1() => i=" + i + ", spendTime=" + takeTime + "ms");

      spendTime += takeTime;

      }

      }

      catch (Exception e)

      {

      e.printStackTrace();

      }

      }

      运行结果:

      1. 在两个方法体上都加上synchronized

      [java]

      public synchronized Integer doAdd1(Long start) throws Exception

      [java]

      public synchronized Integer doAdd2(Long start) throws Exception

      执行结果:

      [html]

      doAdd1() => i=1, spendTime=203ms

      doAdd2() => i=2, spendTime=406ms

      doAdd2() => i=3, spendTime=609ms

      doAdd2() => i=4, spendTime=796ms

      doAdd2() => i=5, spendTime=1000ms

      doAdd2() => i=6, spendTime=1203ms

      doAdd2() => i=7, spendTime=1406ms

      doAdd2() => i=8, spendTime=1609ms

      doAdd2() => i=9, spendTime=1812ms

      doAdd2() => i=10, spendTime=2015ms

      doAdd2() => i=11, spendTime=2218ms

      doAdd1() => i=12, spendTime=2406ms

      doAdd1() => i=13, spendTime=2609ms

      doAdd1() => i=14, spendTime=2812ms

      doAdd1() => i=15, spendTime=3015ms

      doAdd1() => i=16, spendTime=3218ms

      doAdd1() => i=17, spendTime=3421ms

      doAdd1() => i=18, spendTime=3625ms

      doAdd1() => i=19, spendTime=3828ms

      doAdd1() => i=20, spendTime=4015ms

      花费时间:42226ms

      都是有序执行, 变量值没有产生错乱, 但花费时间42226ms

      2.在doAdd1方法上加上synchronized, doAdd2不加。

      [java]

      public synchronized Integer doAdd1(Long start) throws Exception

      执行结果:

      [java]

      doAdd1方法加上synchronized:

      doAdd1() => i=9, spendTime=204ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd2() => i=9, spendTime=188ms

      doAdd1() => i=10, spendTime=391ms

      doAdd1() => i=11, spendTime=610ms

      doAdd1() => i=12, spendTime=813ms

      doAdd1() => i=13, spendTime=1016ms

      doAdd1() => i=14, spendTime=1219ms

      doAdd1() => i=15, spendTime=1422ms

      doAdd1() => i=16, spendTime=1610ms

      doAdd1() => i=17, spendTime=1813ms

      doAdd1() => i=18, spendTime=2016ms

      花费时间:12994ms

      doAdd2方法瞬间执行完成, 之后doAdd1方法则是串行有序执行。 这时doAdd2方法获取的变量值已经错乱,  doAdd1获取的正常。  花费时间:12994ms

      3. 两个方法在都没使用synchronized前的情况:

      执行结果:

      [java]

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd1() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

      doAdd2() => i=16, spendTime=203ms

    花费时间:4060ms

      可以看到, 两个方法的变量值获取已经错乱,  但花费时间最少4060ms

      4. 使用同步块, 在两个方法内采用不同的对象锁:

      doAdd1:

      [java]

      synchronized (obj1)

      {

      Thread.sleep(100);

      ++i;

      Thread.sleep(100);

      return i;

      }

      doAdd2:

      [java]

      synchronized (obj2)

      {

      Thread.sleep(100);

      ++i;

      Thread.sleep(100);

      return i;

      }

      执行结果:

      [html]

      doAdd1() => i=2, spendTime=203ms

      doAdd2() => i=2, spendTime=203ms

      doAdd1() => i=4, spendTime=406ms

      doAdd2() => i=4, spendTime=406ms

      doAdd1() => i=6, spendTime=609ms

      doAdd2() => i=6, spendTime=609ms

      doAdd1() => i=8, spendTime=812ms

      doAdd2() => i=8, spendTime=812ms

      doAdd1() => i=10, spendTime=1000ms

      doAdd2() => i=10, spendTime=1015ms

      doAdd1() => i=12, spendTime=1203ms

      doAdd2() => i=12, spendTime=1203ms

      doAdd1() => i=14, spendTime=1406ms

      doAdd2() => i=14, spendTime=1406ms

      doAdd1() => i=16, spendTime=1609ms

      doAdd2() => i=16, spendTime=1609ms

      doAdd1() => i=18, spendTime=1812ms

      doAdd2() => i=18, spendTime=1812ms

      doAdd1() => i=20, spendTime=2015ms

      doAdd2() => i=20, spendTime=2015ms

      花费时间:22165ms

      两个方法有序交替执行, 互不影响,  花费时间:22165ms, 相对加锁同一对象执行的时间缩短。

      5. 使用同步块, 使用方法参数作为对象锁:

      [java]

      public Integer doAdd1(Long start) throws Exception

      {

      synchronized (start)

      {

      Thread.sleep(100);

      ++i;

      Thread.sleep(100);

      return i;

      }

      }

      执行结果:

      [java]

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      doAdd1() => i=15, spendTime=203ms

      doAdd2() => i=15, spendTime=203ms

      花费时间:4060ms

      执行效果和第三种情况相同,  每个参数作为不同的对象, 即便加上synchronized也不能起到锁的效果。

      6. 把调用的类改为静态类, 只在一个方法上加锁:

      加锁doAdd1方法:

      [java]

      public static synchronized Integer doAdd1(Long start) throws Exception

      执行结果:

      [html]

      doAdd1() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd2() => i=9, spendTime=203ms

      doAdd1() => i=10, spendTime=406ms

      doAdd1() => i=11, spendTime=609ms

      doAdd1() => i=12, spendTime=812ms

      doAdd1() => i=13, spendTime=1015ms

      doAdd1() => i=14, spendTime=1218ms

      doAdd1() => i=15, spendTime=1406ms

      doAdd1() => i=16, spendTime=1609ms

      doAdd1() => i=17, spendTime=1812ms

      doAdd1() => i=18, spendTime=2015ms

      花费时间:13135ms

      和第二种情形类似,  没有加锁的doAdd2方法瞬间执行完成,  doAdd1方法则是串行有序执行。

      总结:

      1. synchronized关键在于锁定的对象, 如果是同一对象, 那么所有执行线程, 必须等待对象锁释放后才能执行,  如果是不同对象,  那么只对各对象所关联的线程生效。

      2. synchronized若加在方法体上,  默认锁定的是对象本身。 对于所有加锁的方法,  都是按照串行规则有序执行,  对于没有加锁的方法, 不受任何影响,  静态类同理。

      3. 合理使用synchronized, 既要保证稳定性, 又要保证性能, 需要在两者间作出权衡,  尽量把synchronized范围细度化, 合理控制业务处理流程;  对象操作原子化, 减少锁的使用;

      不要盲目在方法体上加synchronized关键字, 如果每个方法负责处理的是不同业务,  那么尽量采用第四种情形, 使用不同的对象锁处理, 而不是锁定整个对象。

  • 相关阅读:
    html图片链接不显示图片
    Mybatis总结
    IllegalArgumentException: Could not resolve resource location pattern [classpath .xml]: class path resource cannot be resolved to URL because it does not exist
    java.lang.ClassNotFoundException: com.radiadesign.catalina.session.RedisSessionHandlerValve
    sqlserver2008客户端设置主键自增
    判断手机还是电脑访问
    SSM与jsp传递实体类
    ssm打印sql语句
    SqlSession 同步为注册,因为同步未激活
    for循环取出每个i的值
  • 原文地址:https://www.cnblogs.com/azhqiang/p/3944399.html
Copyright © 2020-2023  润新知