用生活例子来解释Java synchronized块

      今天满世界的微信小程序的新闻,大家都说对于Android原生程序有构成危险了,我也不想了,以后的事谁知道呢, 我还是好好执行一下今年的计划吧。  项目刚刚上线,最近没啥事,我一直感觉自己的Java基础不够扎实,于是就想恶补下一下基础。记得大学英语老师告诉我们,学习要学会炒冷饭,多重复。 我就先从Java多线程的知识再拿出来巩固下,话说无论是从事J2EE还是Android开发,如果对多线程的知识掌握的不好,无论是面试还是工作都说不过去的,至少你不敢说熟悉Java.

     我们都知道synchronized关键字可以让线程同步,但是如果用的位置不好,导致运行效率要降低很多。废话不多说,先上代码。

public class Worker {
    
    private Random random=new Random();
    private ArrayList<Integer>  list1=new ArrayList<>();
    private ArrayList<Integer>  list2=new ArrayList<>();

    private synchronized void stageOne(){
        
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            list1.add(random.nextInt(100));
        
        
    }
    
    private  synchronized void stageTwo(){
        
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            list2.add(random.nextInt(100));
        
        
    }
    
    
    
    private void process(){
        for(int i=0;i<1000;i++){
            stageOne();
            stageTwo();
        }
    }
    
    public void test(){
          long start=System.currentTimeMillis();
          
          Thread t1=new Thread(new Runnable() {
            
            @Override
            public void run() {
              process();                
            }
        });
          t1.start();
          
          Thread t2=new Thread(new Runnable() {
              
              @Override
              public void run() {
                  process();
                  
              }
          });
           t2.start();
            
          try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
          
          long end = System.currentTimeMillis();
          
          System.out.println("耗时:"+(end-start));
          System.out.println("list1 size:"+list1.size()+"  list2 size:"+list2.size());
    }
}

   耗时:5057
   list1 size:2000 list2 size:2000

 

把代码改进后,代码如下:

package cave;

import java.util.ArrayList;
import java.util.Random;

public class Worker {
    
    private Random random=new Random();
    private ArrayList<Integer>  list1=new ArrayList<>();
    private ArrayList<Integer>  list2=new ArrayList<>();
    
    private Object lock1=new Object();
    private Object lokck2=new Object();
    
    
    
    
    private  void stageOne(){
        synchronized (lock1) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            list1.add(random.nextInt(100));
        }
            
        
        
    }
    
    private   void stageTwo(){
        synchronized (lokck2) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            list2.add(random.nextInt(100));
        }    
        
    }
    
    
    
    private void process(){
        for(int i=0;i<1000;i++){
            stageOne();
            stageTwo();
        }
    }
    
    public void test(){
          long start=System.currentTimeMillis();
          
          Thread t1=new Thread(new Runnable() {
            
            @Override
            public void run() {
              process();                
            }
        });
          t1.start();
          
          Thread t2=new Thread(new Runnable() {
              
              @Override
              public void run() {
                  process();
                  
              }
          });
           t2.start();
            
          try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
          
          long end = System.currentTimeMillis();
          
          System.out.println("耗时:"+(end-start));
          System.out.println("list1 size:"+list1.size()+"  list2 size:"+list2.size());
    }
            
}

耗时:2536
list1 size:2000 list2 size:2000


从耗时来看,改进后代码时间比之前少了近一半. 我这里stageOne和stageTwo方法代码比较简单,只有一个for循环。如果这个方法里代码有很多,其中一部分是耗时操作,你还把 synchronized关键字来修饰方法,那么耗时将会更多。所以涉及到的线程同步,synchronized所约束的范围越小越好,性能才会越高。这样说可能有点难以理解。我来打个比方吧,如果你去一个店里买衣服,看到一件衣服非常满意,就想去试一下衣服,这个时候正常情况下导购会告诉你试衣间在哪,让你过去试试衣服,你去了试衣间,然后把门关上,这个就相当于加synchronized关键字了。当然了,有的人进去不是去试衣服的,比如2015年的北京三里屯优衣库试衣间那一对男女是在玩耍,我们也管不了,对吧。不过,那个视频看得真是带劲,我还发给一个女生看了。试衣完毕,肯定出来了,其他人会进去锁上门,试衣服。这样进啊出啊进啊出啊,这个试衣间的锁就是相当于synchronized关键字。那么试想有一种情景:你去买衣服,当你要试衣服的时候,你告诉导购要把店面的大门关上(这个时候在外面大门加锁,相当于synchronized),其他人在外面等候,而且店面是上海南京东路步行街的一家店,人流量巨大。如果每个人试衣服都要求导购把店面大门关上,估计你下个月看不到这个店了,因为它关门倒闭了。你换个衣服这样折腾,这生意还能做下去吗?我估计是来了一个大明星,比如汤唯来到南京东路步行街买衣服,那大门估计真的要关上,不然多少男生要流口水啊,这个生意也没法做啊。总之,试衣服只要把试衣间的门锁上就好了,不用关上大门了。 

     好了,这么一说,你应该理解了为什么synchronized块比synchronized修饰方法性能高了吧,如果还想不通,就想一想你在店外面等着人家试衣服,如果一对男女进去试衣服,你想偷拍都不行啊,这多着急啊,我能理解你的心情!恩,记得只要在试衣间试衣服,坚决不出去。

 

posted on 2017-01-09 16:26  催夜凉风  阅读(497)  评论(6编辑  收藏  举报

导航