好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

memcached的学习

memcached的学习

memcached的学习感悟!

近几天,浏览了大量的memcached相关文章,又自己动手实践了一番
至此,对memcached有了更加深入的了解
在继续编写memcached操作类(基于java_memcached-release)的同时
留下一些自认为比较重要的一些知识,算是Make一下吧
如果其中有理解不当的,请高手给予指点,万分感谢!

一、常用命令

将memcached.exe安装为Windows服务: memcached.exe -d install 启动memcached服务: memcached.exe -d start 启动memcached服务(windows命令): net start "memcached Server" 停止memcached服务(windows命令): net stop "memcached Server" 连接到memcached控制台: telnet ServerIP 11211 打印当前memcached服务器状态: stats 打印当前memcached服务器Items(记录)的统计信息: stats items 打印当前memcached服务器Slab(分区)及Chunk(块)的统计信息: stats slabs 添加新记录: add KeyName 0 0 ValueByteLength [回车] ValueContent 删除记录 :  delete KeyName 添加或更新记录 :  set KeyName 0 0 ValueByteLength [回车] ValueContent 更新记录 :  replace KeyName 0 0 ValueByteLength [回车] ValueContent

二、Think in memcached

memcached预先将可支配的内存空间进行分区( Slab ),每个分区里再分成多个块( Chunk ),但同一个分区里: 块的长度(bytes)是固定的 。 可以将memcached的这种内存管理方式理解为 Table ,分区( Slab )就是列( Col ),而块( Chunk )就是这列下面相同宽度的单元格( Td ) 虽然各个 Slab 的长度不同, 但容量却是相同的 。例如:Slab Class1的Chunk长度:128,个数:8192,Slab Class1的内存容量:128 * 8192 = 1M;Slab Class2的Chunk长度:256,个数:4096,Slab Class2的内存容量:256 * 4096 = 1M。 有新的记录要存入memcached时,首先根据记录的长度查找刚好能够容纳下它的Slab,再找到这个分区里闲置的Chunk,存入即可。 内存浪费的一个小场景:要存入一个 100bytes 的记录,但最合适的Slab却是 128bytes ,那么每存入一个这样的记录,就会产生 28bytes 的闲置内存 将记录从memcached删除后,已经分配的内存(即Chunk),也不会被释放,而是会重复利用,这样就彻底解决了内存碎片的问题 memcached采用“ 惰性 ”方式来应对记录的超期问题,就是它不会主动去监视记录是否超期,而是在 每次get时查看记录的时间戳,如果已超期就会扔掉 。这样的好处就是不会占用宝贵的CPU资源。 有新记录入住时,memcached为它推荐房间(Slab)的优先级是: 闲置的  >  该退房的 ( 超期的 ) >  已有人却很少过来住的 ( get命中数最少的 )。这种方式简称:LRU memcached最大的特点就是“ 分布式 ”,可以为一个应用部署多个memcached服务器,根据其负载权重( weight ),自动进行简单而又有效的任务均衡。 当memcached向多个Server保存一条记录时,会根据特定算法生成一个“ 键 ”,这个“键”决定了这条记录会被打发到哪台服务器;同样,当提取这条记录时,也会根据同样的算法得到这个“ 键 ”,那么也会清楚当初是哪台服务器接收的这条记录,返回就没问题了。 在将memcached用于生产环境时,最好根据存储记录的平均长度对memcached的Slab分区规则进行设置,来尽量减少内存浪费及最大化利用好内存资源。(具体方式不详) 更多待补充

---------------------------------------------------------------------------

一天一天过去了,第三天我想通了 ... ...  

 

分类:  服务器相关 ,  数据库

标签:  java ,  memcached ,  详解 ,  感悟 ,  入门 ,  技巧

Httpclient远程调用WebService示例(Eclipse+httpclient)

 

      我们将Web Service发布在Tomcat或者其他应用服务器上后,有很多方法可以调用该Web Service,常用的有两种:

      1、通过浏览器HTTP调用,返回规范的XML文件内容
      2、通过客户端程序调用,返回结果可自定义格式


      接下来,我利用Eclipse作为开发工具,演示一个Httpclient调用WebService的简单示例
       第一种调用见我的另一篇博文: http://www.cnblogs.com/lanxuezaipiao/archive/2013/05/10/3071584.html
      步骤如下: 
     
      准备工作: 用到的jar包有: 下载链接( http://download.csdn.net/detail/lanxuezaipiao/5354480 )
                  

       第一步 :新建Java Project,项目名称为HttpCallWebService

      第二步:将所需jar包导入到库中

      第三步:编写调用class,这里有两种方式调用,即GET方式和POST方式,由于POST方式较安全,故这里采用POST方式调用;请求数据的构造也有两种方式:静态和动态构造,下面分别介绍这两种方式:

      注:这里以E邮宝开放的webservice接口为例调用其中一个API函数,而E邮宝的webservice基于SOAP,故请求数据为SOAP格式,大家可根据自己情况进行修改

      静态构造请求数据:

  1 package com.http;
 2 
 3 import java.io.ByteArrayInputStream;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 
 7 import org.apache.commons.httpclient.HttpClient;
 8 import org.apache.commons.httpclient.HttpException;
 9 import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
10 import org.apache.commons.httpclient.methods.PostMethod;
11 import org.apache.commons.httpclient.methods.RequestEntity;
12 
13 public class StaticHttpclientCall {
14 
15     /**
16      * @param args
17      * @throws IOException
18      * @throws HttpException
19      */
20     public static void main(String[] args) throws HttpException, IOException {
21         // TODO Auto-generated method stub
22 
23         String soapRequestData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
24                 + "<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
25                 + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
26                 + " xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">"
27                 + " <soap12:Body>"
28                 + " <GetAPACShippingPackage xmlns=\"http://shippingapi.ebay.cn/\">"
29                 + " <GetAPACShippingPackageRequest>"
30                 + " <TrackCode>123</TrackCode>"
31                 + " <Version>123</Version>"
32                 + " <APIDevUserID>123</APIDevUserID>"
33                 + " <APIPassword>123</APIPassword>"
34                 + " <APISellerUserID>123</APISellerUserID>"
35                 + " <MessageID>123</MessageID>"
36                 + " </GetAPACShippingPackageRequest>"
37                 + " </GetAPACShippingPackage>" + "</soap12:Body>"
38                 + " </soap12:Envelope>";
39 
40         System.out.println(soapRequestData);
41 
42         PostMethod postMethod = new PostMethod(
43                 "http://epacketws.pushauction.net/v3/orderservice.asmx?wsdl");
44 
45         // 然后把Soap请求数据添加到PostMethod中
46         byte[] b = soapRequestData.getBytes("utf-8");
47         InputStream is = new ByteArrayInputStream(b, 0, b.length);
48         RequestEntity re = new InputStreamRequestEntity(is, b.length,
49                 "application/soap+xml; charset=utf-8");
50         postMethod.setRequestEntity(re);
51 
52         // 最后生成一个HttpClient对象,并发出postMethod请求
53         HttpClient httpClient = new HttpClient();
54         int statusCode = httpClient.executeMethod(postMethod);
55         if(statusCode == 200) {
56             System.out.println("调用成功!");
57             String soapResponseData = postMethod.getResponseBodyAsString();
58             System.out.println(soapResponseData);
59         }
60         else {
61             System.out.println("调用失败!错误码:" + statusCode);
62         }
63 
64     }
65 
66 } 

      动态构造数据:

   1 package com.http;
  2 
  3 import java.io.ByteArrayInputStream;
  4 import java.io.InputStream;
  5 import java.util.HashMap;
  6 import java.util.Map;
  7 import java.util.Set;
  8 
  9 import org.apache.commons.httpclient.HttpClient;
 10 import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
 11 import org.apache.commons.httpclient.methods.PostMethod;
 12 import org.apache.commons.httpclient.methods.RequestEntity;
 13 
 14 // 动态构造调用串,灵活性更大
 15 public class DynamicHttpclientCall {
 16 
 17     private String namespace;
 18     private String methodName;
 19     private String wsdlLocation;
 20     private String soapResponseData;
 21 
 22     public DynamicHttpclientCall(String namespace, String methodName,
 23             String wsdlLocation) {
 24 
 25         this.namespace = namespace;
 26         this.methodName = methodName;
 27         this.wsdlLocation = wsdlLocation;
 28     }
 29 
 30     private int invoke(Map<String, String> patameterMap) throws Exception {
 31         PostMethod postMethod = new PostMethod(wsdlLocation);
 32         String soapRequestData = buildRequestData(patameterMap);
 33 
 34         byte[] bytes = soapRequestData.getBytes("utf-8");
 35         InputStream inputStream = new ByteArrayInputStream(bytes, 0,
 36                 bytes.length);
 37         RequestEntity requestEntity = new InputStreamRequestEntity(inputStream,
 38                 bytes.length, "application/soap+xml; charset=utf-8");
 39         postMethod.setRequestEntity(requestEntity);
 40 
 41         HttpClient httpClient = new HttpClient();
 42         int statusCode = httpClient.executeMethod(postMethod);
 43         soapResponseData = postMethod.getResponseBodyAsString();
 44 
 45         return statusCode;
 46     }
 47 
 48     private String buildRequestData(Map<String, String> patameterMap) {
 49         StringBuffer soapRequestData = new StringBuffer();
 50         soapRequestData.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
 51         soapRequestData
 52                 .append("<soap12:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""
 53                         + " xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""
 54                         + " xmlns:soap12=\"http://www.w3.org/2003/05/soap-envelope\">");
 55         soapRequestData.append("<soap12:Body>");
 56         soapRequestData.append("<" + methodName + " xmlns=\"" + namespace
 57                 + "\">");
 58         soapRequestData.append("<" + methodName + "Request>");
 59 
 60         Set<String> nameSet = patameterMap.keySet();
 61         for (String name : nameSet) {
 62             soapRequestData.append("<" + name + ">" + patameterMap.get(name)
 63                     + "</" + name + ">");
 64         }
 65         
 66         soapRequestData.append("</" + methodName + "Request>");
 67         soapRequestData.append("</" + methodName + ">");
 68         soapRequestData.append("</soap12:Body>");
 69         soapRequestData.append("</soap12:Envelope>");
 70 
 71         return soapRequestData.toString();
 72     }
 73 
 74     /**
 75      * @param args
 76      * @throws Exception
 77      */
 78     public static void main(String[] args) throws Exception {
 79         // TODO Auto-generated method stub
 80 
 81         DynamicHttpclientCall dynamicHttpclientCall = new DynamicHttpclientCall(
 82                 "http://shippingapi.ebay.cn/", "GetAPACShippingPackage",
 83                 "http://epacketws.pushauction.net/v3/orderservice.asmx?wsdl");
 84 
 85         Map<String, String> patameterMap = new HashMap<String, String>();
 86 
 87         patameterMap.put("TrackCode", "123");
 88         patameterMap.put("Version", "123");
 89         patameterMap.put("APIDevUserID", "123");
 90         patameterMap.put("APIPassword", "123");
 91         patameterMap.put("APISellerUserID", "123");
 92         patameterMap.put("MessageID", "123");
 93         patameterMap.put("TrackCode", "123");
 94 
 95         String soapRequestData = dynamicHttpclientCall.buildRequestData(patameterMap);
 96         System.out.println(soapRequestData);
 97 
 98         int statusCode = dynamicHttpclientCall.invoke(patameterMap);
 99         if(statusCode == 200) {
100             System.out.println("调用成功!");
101             System.out.println(dynamicHttpclientCall.soapResponseData);
102         }
103         else {
104             System.out.println("调用失败!错误码:" + statusCode);
105         }
106         
107     }
108 
109 } 

      最终运行结果:

      可见最终返回的也是xml格式的数据,这里数据未进行格式化显示和处理

解决Mono for android的xml编辑器无法代码完成的问题

 

   这两天在给黑马程序员.Net训练营讲使用.Net开发Android的Mono for android技术 ,发现使用的开发工具在停止调试的时候特别卡,能卡上几十秒钟,而且经常弹出报错的对话框,严重影响心情,因此昨天就下载安装了最新版本的开发工具Xamarin Mono For Android 4.6。安装后也不卡了,也不谈对话框了,太爽了。但是很快发现一个很大的问题“axml编辑器中的无法自动进行标签的自动提示”,这就太降低开发效率。

    遇到问题要学会分析问题,visual studio中对于xml文件提供了自动提示、自动代码完成的功能,其原理是:visual studio会根据当前编辑xml文件的schema声明到Visual studio安装路径下的Xml\Schemas中找和当前编辑的xml文件的schema一致的xsd文件,因为xsd文件是对xml文件格式的标准约束,这样VS就如何完成自动提示了。像Web.config这类文件的自动提示功能就是这样实现的。

   既然明白了这个原理,就来分析一下,打开Xml\Schemas文件夹发现了一个monodroidcatalog.xml文件和monoandroid貌似又关系,打开这个文件发现内容如下:

 <  SchemaCatalog   xmlns  ="http://schemas.microsoft.com/xsd/catalog"  > 
   <  Schema   href  ="%ProgramFiles%/MSBuild/Novell/android-layout-xml.xsd"   /> 
   <  Schema   href  ="%ProgramFiles%/MSBuild/Novell/schemas.android.com.apk.res.android.xsd"   targetNamespace  ="http://schemas.android.com/apk/res/android"   /> 

   <  Association   extension  ="axml"   schema  ="%ProgramFiles%/MSBuild/Novell/android-layout-xml.xsd"   /> 
 </  SchemaCatalog  > 


    猜测这个文件是告诉VS“xsd文件在%ProgramFiles%/MSBuild/Novell/下的android-layout-xml.xsd等文件中”,相当于是一个“指路者”,但是我打开%ProgramFiles%/MSBuild/Novell/却发现根本没有这些文件,看来找到问题了。
    在磁盘上搜索,在C:\Program Files\Xamarin Studio\AddIns\MonoDevelop.MonoDroid\schemas文件夹下发现了:android-layout-xml.xsd、schemas.android.com.apk.res.android.xsd这两个文件,把他们拷贝到%ProgramFiles%/MSBuild/Novell/下,哇咔咔,搞定了。

    凡事搞定原理就不难,遇到问题要学会看错误提示消息和分析,不要像无头苍蝇一样乱撞

揭开ThreadPoolExecutor神秘的面纱

前提摘要:本文是基于jdk1.7的,在分析ThreadPoolExecutor代码的过程中百度时发现1.6和1.7的实现还是有一定的区别的而且还挺大的,个人感觉1.6比较简单好理解。

  为了方便大家阅读理解,我把说明以注释的形式潜入到了代码中。

  关于线程池,它不仅有效的复用了对象,更有效的复用了线程,减少了线程创建,销毁,恢复等状态切换的开销,提高了程序的性能。但是,究竟线程池是怎么复用对象的呢?它又是怎样去复用线程减少开销的呢?下面我们来一一揭开,ThreadPoolExecutor神秘的面纱。

 1.基本变量和方法

  为了能够更好的进行分析,我们先来做一些热身活动,了解下线程池的几个重要的变量吧。

  1.首先大家最好先了解下 原子变量 的概念,具体可以参考官网文档: http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/atomic/package-summary.html

  2.在这里,我们先讲讲两个会贯穿全文的单词:1> workerCount:当前活动的线程数;2> runState:线程池的当前状态

  下面我们开始分析吧。

 1.1基本变量和方法

 private   final  AtomicInteger ctl =  new  AtomicInteger(ctlOf(RUNNING, 0));

  这个是用一个int来表示workerCount和runState的,其中runState占int的高3位,其它29位为workerCount的值。
  用AtomicInteger是因为其在并发下使用compareAndSet效率非常高;
  当改变当前活动的线程数时只对低29位操作,如每次加一减一,workerCount的值变了,但不会影响高3位的runState的值。

  当改变当前状态的时候,只对高3位操作,不会改变低29位的计数值。
  这里有一个假设,就是当前活动的线程数不会超过29位能表示的值,即不会超过536870911,
  就目前以及可预见的很长一段时间来讲,这个值是足够用了。同时按照源代码中注释提供的说法,一旦未来超过了AtomicInteger承受的范围,变量类型到时候可以替换为AtomicLong类型。

 ------------------------------------------------------------------------我是神奇的分割线-----------------------------------------------------------------------------------

 private   static   final   int  COUNT_BITS = Integer.SIZE - 3;

  ------------------------------------------------------------------------ 我是神奇的分割线 -----------------------------------------------------------------------------------

 private   static   final   int  CAPACITY   = (1 << COUNT_BITS) - 1;

  1右移以为后二进制的表示是00100000000000000000000000000000,减去1之后的值就是00011111111111111111111111111111(占29位,29个1)

  CAPACITY为29位能表示的最大容量,即workerCount实际能用的最大值(536870911)

 1.2线程池的状态

  接下来的几个变来那个描述的是关于线程池的状态,分别是:

  1> RUNNING : 该状态下线程池 能接受新任务 ,并且 可以运行队列中的任务

 private   static   final   int  RUNNING    = -1 << COUNT_BITS;

  2> SHUTDOWN : 该状态下的线程池 不再接受新任务 ,但仍 可以执行队列中的任务

            0的二进制为32个0,移位后还是全0(00000000000000000000000000000000)

 private   static   final   int  SHUTDOWN   =  0 << COUNT_BITS;

 private   static   final   int  STOP       =  1 << COUNT_BITS;

  

 private   static   final   int  TIDYING    =  2 << COUNT_BITS;

  

 private   static   final   int  TERMINATED =  3 << COUNT_BITS;

   线程池各个状态间的转换:

  1>RUNNING -> SHUTDOWN : 调用了shutdown方法,线程池实现了finalize方法,在里面调用了shutdown方法,因此shutdown可能是在finalize中被隐式调用的

  2>(RUNNING or SHUTDOWN) -> STOP 调用了shutdownNow方法

  3>SHUTDOWN -> TIDYING : 当队列和线程池均为空的时候

  4>STOP -> TIDYING : 当线程池为空的时候

  5>TIDYING -> TERMINATED : terminated()方法调用完毕

 1.3基本方法  

  1> 这个方法用于取出当前活动线程的数量,也就是workerCount的值。

 /**  
 * 这个方法用于取出workerCount的值
 * 因为CAPACITY值为:00011111111111111111111111111111,所以&操作将参数的高3位置0了
 * 保留参数的低29位,也就是workerCount的值
 *   @param   c ctl, 存储runState和workerCount的int值
 *   @return   workerCount的值
   */ 
 private   static   int  workerCountOf( int  c)  {  return  c & CAPACITY; }

   

  2> 这个方法用于取出当前线程池的运行状态,也就是runState的值

 /**  
 * 这个方法用于取出runState的值
 * 因为CAPACITY值为:00011111111111111111111111111111
 * ~为按位取反操作,则~CAPACITY值为:11100000000000000000000000000000
 * 再同参数做&操作,就将低29位置0了,而高3位还是保持原先的值,也就是runState的值
 *   @param   c 该参数为存储runState和workerCount的int值
 *   @return   runState的值
   */ 
 private   static   int  runStateOf( int  c)     {  return  c & ~CAPACITY; }

   

  3> 这个方法将runState和workerCount的值通过或运算存到同一个int中

 /**  
 * 将runState和workerCount存到同一个int中
 * “|”运算的意思是,假设rs的值是101000,wc的值是000111,则他们位或运算的值为101111
 *   @param   rs runState移位过后的值,负责填充返回值的高3位
 *   @param   wc workerCount移位过后的值,负责填充返回值的低29位
 *   @return   两者或运算过后的值
   */ 
 private   static   int  ctlOf( int  rs,  int  wc) {  return  rs | wc; }

  2.关键方法的分析

   下面我们根据前面我们写的一个线程池的方法进行测试吧,此处只是摘取一点列取出来,详细完整代码请见《 小学徒成长系列—线程同步、死锁、线程池》:

executorService.execute( new  TaskThread());     //  创建任务并交给线程池进行管理 

  1> 上面的入口是execute,那么我们就从execute()方法开始进行分析吧,为了方便大家阅读,我都以注释的形式直接写在代码上,或许这个过程比较枯燥,但是只要你坚持下去,一定会受益匪浅。

  1    public   void   execute(Runnable command) {
   2           //  任务为null,则抛出异常 
  3           if  (command ==  null  )
   4               throw   new   NullPointerException();
   5  
  6           int  c = ctl.get();     //  取出记录着runState和workerCount 的 ctl的当前值
   7          
  8           //  通过workerCountOf方法从ctl所表示的int值中提取出低29位的值,也就是当前活动的线程数
   9           //  如果(当前活动的线程 < corePoolSize) 
 10           if  (workerCountOf(c) <  corePoolSize) {    
  11               //  创建新的线程
  12               //  对于该函数形参,command就是请求任务,
  13               //  而true表示需要检测当前运行的线程是否小于corePoolSize
  14               //  false表示需要检测当前运行的线程数量是否小于maxPoolSize 
 15               if  (addWorker(command,  true  ))    
  16                   return ;     //  创建线程成功,则停止该终止该方法的执行 
 17              c = ctl.get();     //  如果添加失败,则取出记录着runState和workerCount 的 ctl的当前值 
 18           }
  19           //  当前线程池处于运行状态且队列未满 && 如果线程正在运行中并且任务添加到缓冲队列成功 
 20           if  (isRunning(c) &&  workQueue.offer(command)) {
  21               int  recheck = ctl.get();     //  再次获取用于下面再次检查 
 22               if  (! isRunning(recheck) && remove(command))     //  如果线程池已经处于非运行状态,则从缓冲队列中移除任务并拒绝 
 23                  reject(command);     //  采用线程池指定的策略拒绝任务 
 24               else   if  (workerCountOf(recheck) == 0)     //  如果线程池处于运行状态 或者线程池已经处于非运行状态但是任务移除失败 
 25                  addWorker( null ,  false  );
  26           }
  27           //      1. 当前线程池并不处于Running状态
  28           //      2. 当前线程池处于Running状态,但是缓冲队列已经满了 
 29           else   if  (!addWorker(command,  false  ))
  30               reject(command);  //  采用线程池指定的策略拒绝任务 
  31    }

  关于上面的execute(Runnable command),大部分的解释都在代码的注释中啦。

  或许大家会有疑问:上面已经有了判断当前活动的线程小于corePoolSize了,那么等于和大于corePoolSize怎么处理呢?

  解答:不知道大家有没有注意到,当当前活动的线程数量 >= corePoolSize 的时候,都是优先添加到队列中,直到队列满了才会去创建新的线程,在这里第20行的if语句已经体现出来了。这里利用了&&的特性,只有当第一个条件会真时才会去判断第二个条件,第一个条件是isRunning(),判断线程池是否处于RUNNING状态,因为只有在这个状态下才会接受新任务,否则就拒绝,如果正处于RUNNING状态,那么就加入队列,如果加入失败可能就是队列已经满了,这时候直接执行第29行。

  2> 在execute()方法中,当 当前活动的线程数量 < corePoolSize 时,会执行addWorker()方法,关于addWorker(),它是用来直接新建线程用的,之所以叫addWorker而不是addThread是因为在线程池中,所有的线程都用一个Worker对象包装着,好吧,我们先来看看这个方法。

  1      /** 
  2       * 创建并执行新线程
   3       *   @param   firstTack 用于指定新增的线程执行的第一个任务
   4       *
   5       *   @param   core      true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,
   6       *                  false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize
   7       *
   8       *   @return   是否成功新增一个线程
   9       */ 
 10      private   boolean  addWorker(Runnable firstTask,  boolean   core) {
  11           retry:
  12           for   (;;) {
  13               int  c = ctl.get();     //  获取记录着runState和workCount的int变量的当前值 
 14               int  rs = runStateOf(c);     //  获取当前线程池运行的状态
  15  
 16               //  if语句中的条件转换成一个等价实现:rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty()) 
 17               /* 
 18                 这个条件代表着以下几个情景,就直接返回false说明线程创建失败:
  19                 1.rs > SHUTDOWN; 此时不再接收新任务,且所有的任务已经执行完毕
  20                 2.rs = SHUTDOWN; 此时不再接收新任务,但是会执行队列中的任务,在后买年的或语句中,第一个不成立,firstTask != null成立
  21                 3.rs = SHUTDOWN;此时不再接收新任务,fistTask == null,任务队列workQueue已经空了
  22               */ 
 23               if  (rs >= SHUTDOWN &&
 24                  ! (rs == SHUTDOWN &&
 25                     firstTask ==  null  &&
 26                     !  workQueue.isEmpty()))
  27                   return   false  ;
  28  
 29               for   (;;) {
  30                   //  获取当前活动的线程数 
 31                   int  wc =  workerCountOf(c);    
  32                   //  先判断当前活动的线程数是否大于最大值,如果超过了就直接返回false说明线程创建失败
  33                   //  如果没有超过再根据core的值再进行以下判断 
 34                   /* 
 35                       1.core为true,则判断当前活动的线程数是否大于corePoolSize
  36                       2.core为false,则判断当前活动线程数是否大于maximumPoolSize
  37                   */ 
 38                   if  (wc >= CAPACITY ||
 39                      wc >= (core ?  corePoolSize : maximumPoolSize))
  40                       return   false  ;
  41                   //  比较当前值是否和c相同,如果相同,则改为c+1,并且跳出大循环,直接执行Worker进行线程创建 
 42                   if   (compareAndIncrementWorkerCount(c))
  43                       break   retry;
  44                  c = ctl.get();   //   获取ctl的当前值 
 45                   if  (runStateOf(c) != rs)     //  检查下当前线程池的状态是否已经发生改变 
 46                       continue  retry;     //  如果已经改变了,则进行外层retry大循环,否则只进行内层的循环
  47                   //   else CAS failed due to workerCount change; retry inner loop 
 48               }
  49           }
  50           //  下面这里就是开始创建新的线程了
  51           //  Worker的也是Runnable的实现类 
 52          Worker w =  new   Worker(firstTask);
  53           //  因为不可以直接在Worker的构造方法中进行线程创建
  54           //  所以要把它的引用赋给t方便后面进行线程创建 
 55          Thread t =  w.thread;    
  56  
 57           final  ReentrantLock mainLock =  this  .mainLock;
  58           mainLock.lock();
  59           try   {
  60            
 61               //  再次取出ctl的当前值,用于进行状态的检查,防止线程池的已经状态改变了 
 62               int  c =  ctl.get();
  63               int  rs =  runStateOf(c);
  64  
 65               //  将if语句中的条件转换为一个等价实现 :t == null || (rs >= SHUTDOWN && (rs != SHUTDOWN || firstTask != null))
  66               //  有个t == null是因为如果使用的是默认的ThreadFactory的话,那么它的newThread()可能会返回null 
 67               /* 
 68                 1. 如果t == null, 则减少一个线程数,如果线程池处于的状态 > SHUTDOWN,则尝试终止线程池
  69                 2. 如果t != null,且rs == SHUTDOWN,则不再接收新任务,若firstTask != null,则此时也是返回false,创建线程失败
  70                 3. 如果t != null, 且rs > SHUTDOWN,同样不再接受新任务,此时也是返回false,创建线程失败
  71               */ 
 72               if  (t ==  null  ||
 73                  (rs >= SHUTDOWN &&
 74                   ! (rs == SHUTDOWN &&
 75                      firstTask ==  null  ))) {
  76                  decrementWorkerCount();     //  减少一个活动的当前线程数 
 77                  tryTerminate();     //  尝试终止线程池 
 78                   return   false ;     //  返回线程创建失败 
 79               }
  80  
 81              workers.add(w);     //  将创建的线程添加到workers容器中 
 82  
 83               int  s = workers.size();     //  获取当前线程活动的数量 
 84               if  (s > largestPoolSize)     //  判断当前线程活动的数量是否超过线程池最大的线程数量 
 85                  largestPoolSize = s;     //  当池中的工作线程创新高时,会将这个数记录到largestPoolSize字段中。然后就可以启动这个线程t了 
 86          }  finally   {
  87               mainLock.unlock();
  88           }
  89  
 90          t.start();     //  开启线程
  91           //  若start后,状态又变成了SHUTDOWN状态(如调用了shutdownNow方法)且新建的线程没有被中断过,
  92           //  就要中断该线程(shutdownNow方法要求中断正在执行的线程),
  93           //  shutdownNow方法本身也会去中断存储在workers中的所有线程 
 94           if  (runStateOf(ctl.get()) == STOP && !  t.isInterrupted())
  95               t.interrupt();
  96  
 97           return   true  ;
  98      }

  那么在创建线程的时候,线程执行的是什么的呢?

  我们前面提到Worker继承的其实也是Runnable,它在创建线程的时候是以自身作为任务传进先创建的线程中的,这段比较简单,我就不一一注释了,只是给出源代码给大家看吧。

       Worker(Runnable firstTask) {
              this .firstTask =  firstTask;
              //  this指的是worker对象本身 
             this .thread = getThreadFactory().newThread( this  );
        } 

   它以自身的对象作为线程任务传进去,那么它的run方法又是怎样的呢?

  public   void   run() {
            runWorker(  this  );
        } 

   竟然只有一句话调用runWorker()方法,这个可是重头戏,我们来看看,究竟运行的是什么。

  1   /** 
  2    * 执行Worker中的任务,它的执行流程是这样的:
   3    * 若存在第一个任务,则先执行第一个任务,否则,从队列中拿任务,不断的执行,
   4    * 直到getTask()返回null或执行任务出错(中断或任务本身抛出异常),就退出while循环。
   5    *   @param   w woker
   6    */ 
  7    final   void   runWorker(Worker w) {
   8          Runnable task = w.firstTask;     //  将当前Worker中的任务取出来交给task,并释放掉w.firstTask占用的内存 
  9          w.firstTask =  null  ;
  10           //  用于判断线程是否由于异常终止,如果不是异常终止,在后面将会将该变量的值改为false
  11           //  该变量的值在processWorkerExit()会使用来判断线程是否由于异常终止 
 12           boolean  completedAbruptly =  true  ;    
  13           try   {
  14               //  执行任务,直到getTask()返回的值为null,在此处就相当于复用了线程,让线程执行了多个任务 
 15               while  (task !=  null  || (task = getTask()) !=  null  ) {    
  16                   w.lock();
  17                   clearInterruptsForTaskRun(); //对线程池状态进行一次判断,后面我们会讲解一下该方法 
  18                   try   {
  19                      beforeExecute(w.thread, task);     //  在任务执行前需要做的逻辑方法,该方面可以由用户进行重写自定义 
 20                      Throwable thrown =  null  ;
  21                       try   {
  22                          task.run();     //  开始执行任务 
 23                      }  catch   (RuntimeException x) {
  24                          thrown = x;  throw   x;
  25                      }  catch   (Error x) {
  26                          thrown = x;  throw   x;
  27                      }  catch   (Throwable x) {
  28                          thrown = x;  throw   new   Error(x);
  29                      }  finally   {
  30                          afterExecute(task, thrown);     //  在任务执行后需要做的逻辑方法,该方面可以由用户进行重写自定义 
 31                       }
  32                  }  finally   {
  33                      task =  null  ;    
  34                      w.completedTasks++;     //  增加该线程完成的任务 
 35                       w.unlock();
  36                   }
  37               }
  38              completedAbruptly =  false ;     //  线程不是异常终止 
 39          }  finally   {
  40              processWorkerExit(w, completedAbruptly);     //  结束该线程 
 41           }
  42      }

 下面就是线程在执行任务之前对线程池状态的一次判断:

  1       /** 
  2        * 对线程的结束做一些清理和数据同步
   3        *   @param   w 封装线程的Worker
   4        *   @param   completedAbruptly 表示该线程是否结束于异常
   5        */ 
  6       private   void  processWorkerExit(Worker w,  boolean   completedAbruptly) {
   7           //   如果completedAbruptly值为true,则说明线程是结束于异常
   8           //  如果不是结束于异常,那么它降在runWorker方法的while循环中的getTask()方法中已经减一了 
  9           if   (completedAbruptly) 
  10              decrementWorkerCount();     //  此时将线程数量减一 
 11  
 12           final  ReentrantLock mainLock =  this  .mainLock;
  13           mainLock.lock();
  14           try   {
  15              completedTaskCount += w.completedTasks;     //  统计总共完成的任务数 
 16              workers.remove(w);     //  将该线程数从workers容器中移除 
 17          }  finally   {
  18               mainLock.unlock();
  19           }
  20  
 21          tryTerminate();     //  尝试终止线程池 
 22  
 23           int  c =  ctl.get();
  24           //  接下来的这个if块要做的事儿了。当池的状态还是RUNNING,
  25           //  又要分两种情况,一种是异常结束,一种是正常结束。异常结束比较好弄,直接加个线程替换死掉的线程就好了,
  26           //  也就是最后的addWorker操作 
 27           if  (runStateLessThan(c, STOP)) {     //  如果当前运行状态为RUNNING,SHUTDOWN 
 28               if  (!completedAbruptly) {     //  如果线程不是结束于异常 
 29                   int  min = allowCoreThreadTimeOut ? 0 : corePoolSize;     //  是否允许线程超时结束 
 30                   if  (min == 0 && ! workQueue.isEmpty())     //  如果允许把那个且队列不为空 
 31                      min = 1;     //  至少要保留一个线程来完成任务
  32                   //  如果当前活动的线程数大于等于最小的值
  33                   //   1.不允许核心线程超时结束,则必须要使得活动线程数超过corePoolSize数才可以
  34                   //   2. 允许核心线程超时结束,但是队列中有任务,必须留至少一个线程 
 35                   if  (workerCountOf(c) >=  min)    
  36                       return ;  //   replacement not needed 
 37               }
  38               //  直接加个线程 
 39              addWorker( null ,  false  );    
  40           }
  41      }

 前面我们的方法遇见过很多次tryTerminate()方法,到底他是怎样尝试结束线程池的呢?

  1       /** 
  2        * 执行该方法,根据线程池状态进行 
   3        * 判断是否结束线程池
   4        */ 
  5        final   void   tryTerminate() {
   6           for   (;;) {
   7               int  c =  ctl.get();
   8               if  (isRunning(c) ||     //  线程池正在运行中,自然不能结束线程池啦 
  9                  runStateAtLeast(c, TIDYING) ||     //  如果状态为TIDYING或TERMINATED,池中的活动线程数已经是0,自然也不需要做什么操作了 
 10                  (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))     //  线程池出于SHUTDOWN状态,但是任务队列不为空,自然不能结束线程池啦 
 11                   return  ;
  12               if  (workerCountOf(c) != 0) {  //   Eligible to terminate 
 13                   /* 
 14                     调用这个方法的目的是将shutdown信号传播给其它线程。
  15                     调用shutdown方法的时候会去中断所有空闲线程,如果这时候池中所有的线程都正在执行任务,
  16                     那么就不会有线程被中断,调用shutdown方法只是设置了线程池的状态为SHUTDOWN,
  17                     在取任务(getTask,后面会细说)的时候,假如很多线程都发现队列里还有任务(没有使用锁,存在竞态条件),
  18                     然后都去调用take,如果任务数小于池中的线程数,那么必然有方法调用take后会一直等待(shutdown的时候这些线程正在执行任务,
  19                     所以没能调用它的interrupt,其中断状态没有被设置),那么在没有任务且线程池的状态为SHUTDWON的时候,
  20                     这些等待中的空闲线程就需要被终止iinterruptIdleWorkers(ONLY_ONE)回去中断一个线程,让其从take中退出,
  21                     然后这个线程也进入同样的逻辑,去终止一个其它空闲线程,直到池中的活动线程数为0。
  22                   */ 
 23                   interruptIdleWorkers(ONLY_ONE);
  24                   return  ;
  25               }
  26  
 27               final  ReentrantLock mainLock =  this  .mainLock;
  28               mainLock.lock();
  29               try   {
  30                   /* 
 31                     当状态为SHUTDOWN,且活动线程数为0的时候,就可以进入TIDYING状态了,
  32                     进入TIDYING状态就可以执行方法terminated(),
  33                     该方法执行结束就进入了TERMINATED状态(参考前文中各状态的含义以及可能的状态转变)
  34                   */ 
 35                   if  (ctl.compareAndSet(c, ctlOf(TIDYING, 0 ))) {
  36                       try   {
  37                          terminated();     //  执行该方法,结束线程池 
 38                      }  finally   {
  39                          ctl.set(ctlOf(TERMINATED, 0 ));
  40                           /* 
 41                             当线程池shutdown后,外部可能还有很多线程在等待线程池真正结束,
  42                             即调用了awaitTermination方法,该方法中,外部线程就是在termination上await的,
  43                             所以,线程池关闭之前要唤醒这些等待的线程,告诉它们线程池关闭结束了。
  44                           */ 
 45                           termination.signalAll();
  46                       }
  47                       return  ;
  48                   }
  49              }  finally   {
  50                   mainLock.unlock();
  51               }
  52               //   else retry on failed CAS 
 53           }
  54      }

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于memcached的学习的详细内容...

  阅读:41次