6.18Java多线程并发、同步性能分析

6.18Java多线程并发、同步性能分析

对比同步块和同步方法--->粒度更小的锁定资源,尽可能地提升性能

根据几个同步锁对象不同的实例观察线程不安全的实例

package iostudy.synchro;

/**
* 测试同步方法和同步块对粒度更小地资源锁定
* 感受性能上地差异
* @since JDK 1.8
* @date 2021/06/18
* @author Lucifer
*/
public class SynBlockTestNo3 {
   public static void main(String[] args) {

       /*资源实现类*/
       SynWeb12306 synWeb12306 = new SynWeb12306();

       /*多个线程代理对象*/
       new Thread(synWeb12306, "代劳").start();
       new Thread(synWeb12306, "一楼").start();
       new Thread(synWeb12306, "丙楼").start();

  }
}

/**
* 创建内部资源类
*/
class SynWeb12306 implements Runnable{

   /*设置资源数量*/
   private int ticketNums = 10;
   private boolean flag = true;

   /*重写接口run方法,实现具体逻辑*/
   @Override
   public void run(){
       while (flag){
           /*模拟线程等待*/
           try {
               Thread.sleep(100);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }

           /*调用内部具体实现逻辑的方法*/
           test3();

      }
  }

   /**
    * 写一个内部具体实现逻辑的方法
    * 同步方法
    */
   public synchronized void test1(){
       if (ticketNums<0){
           /*开关改变*/
           flag = false;
           /*结束方法*/
           return;
      }

       /*模拟延时*/
       try {
           Thread.sleep(200);
      }catch (InterruptedException e){
           System.out.println(e.getMessage());
           e.printStackTrace();
      }

       /*获取线程名称、资源数量减少*/
       System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

  }

   /**
    * 写另一个一样逻辑的实现方法
    * 同步块
    */
   public void test2(){

       /*明确锁的对象为资源对象*/
       synchronized (this){
           if (ticketNums<0){
               /*开关改变*/
               flag = false;
               /*结束方法*/
               return;
          }

           /*模拟延时等待*/
           try {
               Thread.sleep(200);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }

           /*获取当前线程名称和资源数量*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
  }

   /*只锁定资源*/

   /**
    * 这样锁会不安全,导致数据不准确
    * 原因是因为ticketNums这个资源是会变的,在锁资源的时候大的对象不能变动,里面的资源随便变化
    * 大的对象不变,小的资源对象变。
    * 搞清楚对象在变和对象的属性在变的区别
    */
   public void test3(){

       /*明确锁的对象为资源对象*/
       synchronized ((Integer) ticketNums){
           if (ticketNums<0){
               /*开关改变*/
               flag = false;
               /*结束方法*/
               return;
          }

           /*模拟延时等待*/
           try {
               Thread.sleep(200);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }

           /*获取当前线程名称和资源数量*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
  }
}

粒度更小的锁提升性能

/*只锁定对象的属性--->缩小区域--->还是会有线程不安全问题。范围太小锁不住*/
   /*小粒度锁定资源观察数据是否安全*/
   public void test4(){

       /*只锁定this的ticketNums属性*/
       synchronized (this){
           if (ticketNums<0){
               /*开关变化*/
               flag = false;
               /*结束方法*/
               return;
          }
      }

       /*模拟网络延迟*/
       try {
           Thread.sleep(200);
      }catch (InterruptedException e){
           System.out.println(e.getMessage());
           e.printStackTrace();
      }

       /*获取当前线程名称以及资源数量*/
       System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

  }
/*锁定范围太大--->损耗性能资源*/
   /**
    * 写另一个一样逻辑的实现方法
    * 同步块
    */
   public void test2(){

       /*明确锁的对象为资源对象*/
       synchronized (this){
           if (ticketNums<0){
               /*开关改变*/
               flag = false;
               /*结束方法*/
               return;
          }

           /*模拟延时等待*/
           try {
               Thread.sleep(200);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }

           /*获取当前线程名称和资源数量*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
  }

double checking保证数据安全

    /**
    * 写另一个一样逻辑的实现方法
    * 同步块,缩小锁定范围
    */
   public void test5(){

       /*明确锁的对象为资源对象*/
       if (ticketNums<0){
           /*开关改变*/
           flag = false;
           /*结束方法*/
           return;
      }
       /*
       上面的部分考虑的是没有票的情况
       如果有多个线程进来最开始拦截不住还是会出现数据不安全的情况
        */

       /*只锁定下面部分的范围*/
       synchronized (this){
           if (ticketNums<0){
               /*开关改变*/
               flag = false;
               /*结束方法*/
               return;
          }
           /*
           这里加入判断考虑最后一张票的情况
            */

           /*模拟延时等待*/
           try {
               Thread.sleep(200);
          }catch (InterruptedException e){
               System.out.println(e.getMessage());
               e.printStackTrace();
          }

           /*获取当前线程名称和资源数量*/
           System.out.println(Thread.currentThread().getName() + "--->" + ticketNums--);

      }
       /*
       尽可能地考虑锁合理地范围不是指代码地数量而是指数据地完整性
       --->双重检测(double checking)
       线程安全
       两个检查,第一个检查不用锁定,第二个检查需要锁定
        */
  }

 

posted @ 2021-06-18 19:26  俊king  阅读(117)  评论(0编辑  收藏  举报