关于多线程程序中使用volatile关键字的一个小例子

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28352347/article/details/54633338

在公司分配给我的爬虫任务中,具体的信息又写需要在详情页中取得,所以需要在加入待抓取链接


我们用的框架是基于java 的webmagic ,这个框架可以在启动时设定多个线程抓取,所以待抓取的多个链接可能是跑在不同线程上的,但是最后需要统计,一共抓取了多少条信息,这就需要线程同步了。

我思考了一下,觉得如果是加锁还不如一个线程跑到底,而且这个计数相对于抓取来说仅仅起统计的作用,于是我想到了学习多线程时的一个修饰符:volatile

刚才说了,线程同步,其实就是对共享对象加锁,而volatile起什么作用呢,那就是这个修饰符修饰的变量,在每次读取时,都会直接从主存中刷新,也就是即时同步。这样,我们就不用担心计数的问题了,每次i++时,都会从主存中读取i的值,不会造成计数的错误。


但是volatile使用时要注意什么呢?首先,在多线程中同步中动作应当时原子性的,而被volatile修饰的变量不是原子性的,仅仅是使用时会从主存中即时读取罢了,其安全性肯定不如锁机制,但也因此获得了较高的执行效率。在这个程序中,计数仅仅需要数字的正确性,不涉及其他复杂操作,所以适宜使用volitile


下面是源代码,其中一些为配置性代码

@Slf4j
public class MulberryPageProcessor implements PageProcessor {


    public volatile int i=0;
//http://www.mulberry.com/cn/stores-list
    private static final String  List_Url_Regex="http://www\\.mulberry\\.com/cn/stores-list";
    private Site site = Site
            .me()
            .setSleepTime(1000)
            .setRetryTimes(3)
            .setRetrySleepTime(5000)
            .setTimeOut(30000)
            .addHeader("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6")
            .setUserAgent(
                    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36");


    //定义静态hashmap进行数据的存储
    public static HashMap<String,String[]> list=new HashMap<String, String[]>();

    public  List<BrandPoiDto> brandPoiDtos = new ArrayList<BrandPoiDto>();

    //对url进行正则提取
    private Pattern urlPattern=Pattern.compile("(?<=href=\").*(?=\" title)");




    //利用List存储要入库的数据

    @Override
    public void process(Page page){


        //判断是否为列表页
        if(page.getUrl().regex(List_Url_Regex).match())
        {

//           System.out.print(page.getHtml().toString());

            //分国家爬取列表
            List<Selectable> CountryList=page.getHtml().xpath("li[@class='store-country']").nodes();


            String country="";
            String branchname="";


            //循环,将国家名称,分店名截取出来,剩下的详细信息在详情页截取
            for(Selectable a:CountryList){
                //截取国家名称
                country=a.xpath("//span[@class='store-country__name']/text()").toString();

                //截取分店列表
                List<Selectable> ShopList =a.xpath("//li[@class='stores-list__item']").nodes();

                //对分店列表做循环
                for(Selectable b:ShopList)
                {

                    //提取分店名称
                    branchname=b.xpath("//a[@class='stores-list__link']/text()").toString();
                    //提取分店链接url,因为xpath提供的正则有些东西换转码,所以用自己写的
//                    String BranchUrl=b.xpath("//a[@class='stores-list__link']/@href/text()").toString();
                    String BranchUrl="";
                    Matcher urlMatcher=urlPattern.matcher(b.toString());
                    if(urlMatcher.find())
                    {
                        BranchUrl=urlMatcher.group();
                    }
                    //将分店的url作为map中的key,将分店名和所在国家存储进去
                    if(BranchUrl!=null)
                    {
                        //将国家和分店信息存储进value里,将分店详情链接作为key
                        String [] value={country,branchname};
                        list.put(BranchUrl,value);
//                        System.out.println("url["+i+"] =\""+BranchUrl+"\";");


//                        将详情界面添加到爬取的界面当中
                        page.addTargetRequest(BranchUrl);
                       //对分店名,url初始化
                        branchname="";
                        BranchUrl="";
                    }

                    else
                    {
                        //此时应当记录日志
                        System.out.print("详情页url没获取到");
                    }

                }

                //将国家初始化
                country="";

            }

        }
        //若为详情页
        else
        {

           try{
               String BranchName="";

               //使用线程安全的Buffer
               StringBuffer Opentime=new StringBuffer();
               String Phone="";
               String CrawUrl=page.getUrl().toString();
               StringBuilder Address=new StringBuilder();
               String City="";
               String Country="";
               String Lat="";
               double latitude=0.0;//截取出来的为字符串,必须转化为数字进行存储
               String Lng="";
               double longitude=0.0;
               String CrawlUrl=page.getUrl().toString();
               String Region="";
               int CategoryId=658;

               //经纬度的截取,并转化为double
               Lat=page.getHtml().xpath("div[@id='storeGeoDetails']//@data-store-lat").toString();
               Lng=page.getHtml().xpath("div[@id='storeGeoDetails']//@data-store-long").toString();

               latitude=Double.parseDouble(Lat);
               longitude=Double.parseDouble(Lng);

               //电话号码的获取
               Phone=page.getHtml().xpath("li[@class='tel']/text()").toString();

               //城市名称的截取
               //先将li标签循环存储
               List<Selectable> LiList =page.getHtml().xpath("ul[@class='contact']/li/text()").nodes();
               //城市名在倒数第4个li标签中
               try{

                   for(int i=0;i<LiList.size()-3;i++)
                   {
                       if(i<LiList.size()-4)
                       {
                           //已测试成功
                           Address.append(LiList.get(i).toString());
                       }
                       //在之前进行字符串的拼接,拼接结果为地址 选用StringBuild

                       if(i==LiList.size()-4)
                           City=LiList.get(i).toString();
                   }



               }

               catch(Exception e)
               {

               }

               //营业时间的截取,需要去除标签
//            Opentime=page.getHtml().xpath("table[@class='store-times']//text()").toString();

               List<Selectable> OpenTimeList=page.getHtml().xpath("table[@class='store-times']/tbody/tr/td/text()").nodes();

               //循环输出
               for(Selectable a:OpenTimeList)
               {
                   Opentime=Opentime.append(a.toString());
               }




               //从hashmap中取出国家与分店信息,key为value值
               String[] contain =new String[2];
               contain=list.get(page.getUrl().toString());
               Country=contain[0];
               BranchName=contain[1];

               //获取工作完成,存入dto中

               BrandPoiDto dto = new BrandPoiDto();
//                dto.setShopId(ShopId);
               dto.setBranchName(BranchName);
               dto.setAddress(Address.toString());
               dto.setPhone(Phone);
               dto.setOpenTime(Opentime.toString());
               dto.setCrawlUrl(CrawlUrl);
               dto.setCountry(Country);
               dto.setCity(City);
               dto.setRegion(Region);
               dto.setLat(latitude);
               dto.setLng(longitude);
               dto.setSourceName("Mulberry");
             i++;

               System.out.println(i);

           brandPoiDtos.add(dto);

               page.putField("brandPoiList", brandPoiDtos);


            brandPoiDtos.clear();
           }
           catch(Exception e)
           {
               log.error("method:TripAdvisorPoiReviewPageProcessor.process,parse url[{}] exception[{}]", page.getUrl(), e);
//            System.out.println(page.getUrl());


           }




        }





猜你喜欢

转载自blog.csdn.net/qq_28352347/article/details/54633338