少年阿宾

那些青春的岁月

  BlogJava :: 首页 :: 联系 :: 聚合  :: 管理
  500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

#

//研究了一下午,终于发现一个问题,写这个代码不是很难的,难的是找一个能代理的IP地址,实际网上的好多代码都可以用滴,只是代理IP和Port有问题而已,废话少说,直接上代码:
1、先说Get的代理(首先提供一个Servlet的Get的http服务)

package com.abin.lee.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;

/**
 * Created with IntelliJ IDEA.
 * User: abin
 * Date: 13-4-18
 * Time: 上午8:39
 * To change this template use File | Settings | File Templates.
 */
public class HttpClientGetProxyServlet extends HttpServlet {
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
    public void doGet(HttpServletRequest request,HttpServletResponse response)throws IOException {
        System.out.println("receive httpGet request");
        String userName=request.getParameter("userName");
        String passWord=request.getParameter("passWord");
        String localIp=request.getLocalAddr();
        String localName=request.getLocalName();
        int localPort=request.getLocalPort();
        int ServerPort=request.getServerPort();
        String leeHeader=request.getHeader("lee");
        System.out.println("userName="+userName+",passWord="+passWord+",localIp="+localIp+",localName="+localName+",localPort="+localPort);
        System.out.println("ServerPort="+ServerPort+",leeHeader="+leeHeader);
        String remoteIp=request.getRemoteAddr();
        String remoteHost=request.getRemoteHost();
        int remotePort=request.getRemotePort();
        System.out.println("remoteIp="+remoteIp+",remoteHost="+remoteHost+",remotePort="+remotePort);

        ServletOutputStream out=response.getOutputStream();
        BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(out));
        writer.write("success");
        writer.flush();
        writer.close();

    }
}




HttpGet代理测试类:

package com.abin.lee.ssh.senior.proxy.httpclient;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;

public class HttpClientGetProxyServletTest {
 public static final String HttpGetProxyUrl="http://localhost:8100/MyThread/HttpClientGetProxyServlet";
 @Test
    public  void testHttpClientPostProxyServlet()throws Exception {
        HttpHost proxy = new HttpHost("10.10.10.10", 1443, "http");
        DefaultHttpClient httpclient = new DefaultHttpClient();
        try {
            httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

            HttpHost target = new HttpHost("localhost", 8100, "http");
            HttpGet request = new HttpGet(HttpGetProxyUrl+"?"+"userName=abin&passWord=varyall");
            request.setHeader("lee", "lee");
            System.out.println("executing request to " + target + " via " + proxy);
            HttpResponse rsp = httpclient.execute(target, request);
            HttpEntity entity = rsp.getEntity();

            System.out.println("----------------------------------------");
            System.out.println(rsp.getStatusLine());
            Header[] headers = rsp.getAllHeaders();
            for (int i = 0; i<headers.length; i++) {
                System.out.println(headers[i]);
            }
            System.out.println("----------------------------------------");

            if (entity != null) {
                System.out.println(EntityUtils.toString(entity));
            }

        } finally {
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            httpclient.getConnectionManager().shutdown();
        }
    }
}







//正常测试代码,非代理

package com.abin.lee.ssh.senior.proxy.httpclient;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.Test;

public class HttpClientGetServletTest {
 public static final String HttpGetUrl = "http://localhost:8100/MyThread/HttpClientGetProxyServlet";

 @Test
 public void HttpClientGetServlet() {
  HttpClient httpClient = new DefaultHttpClient();
  StringEntity reqEntity = null;
  HttpGet httpGet = null;
  try {
   HttpGet request = new HttpGet(HttpGetUrl+"?"+"userName=abin&passWord=varyall");
            request.setHeader("lee", "lee");
   // 目标地址
   System.out.println("请求: " + httpGet.getRequestLine());
   // 执行
   HttpResponse response = httpClient.execute(httpGet);
   HttpEntity entity = response.getEntity();
   System.out.println("----------------------------------------");
   System.out.println(response.getStatusLine());
   if (entity != null) {
    System.out.println("Response content length: "
      + entity.getContentLength());
   }
   // 显示结果
   BufferedReader reader = new BufferedReader(new InputStreamReader(
     entity.getContent(), "UTF-8"));
   String line = null;
   while ((line = reader.readLine()) != null) {
    System.out.println(line);
   }
  } catch (Exception e) {
   e.printStackTrace();
  } finally {
   if (!httpGet.isAborted()) {
    httpGet.abort();
   }
   httpClient.getConnectionManager().shutdown();
  }
 }
}




//servlet配置

   <servlet>
             <servlet-name>HttpClientGetProxyServlet</servlet-name>
             <servlet-class>com.abin.lee.servlet.HttpClientGetProxyServlet</servlet-class>
         </servlet>
         <servlet-mapping>
             <servlet-name>HttpClientGetProxyServlet</servlet-name>
             <url-pattern>/HttpClientGetProxyServlet</url-pattern>
         </servlet-mapping>






我这里的IP和端口不一定能用哟,自己找能用的!!代码是100%没问题的,经过生产环境测试的哟!!!
posted @ 2013-04-18 17:50 abin 阅读(1432) | 评论 (0)编辑 收藏

//研究了一下午,终于发现一个问题,写这个代码不是很难的,难的是找一个能代理的IP地址,实际网上的好多代码都可以用滴,只是代理IP和Port有问题而已,废话少说,直接上代码:
1、先说Post的代理
//HttpClientPostProxyServlet

package com.abin.lee.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;

/**
 * Created with IntelliJ IDEA.
 * User: abin
 * Date: 13-4-18
 * Time: 上午8:39
 * To change this template use File | Settings | File Templates.
 */
public class HttpClientPostProxyServlet extends HttpServlet {
    public void init(ServletConfig config) throws ServletException {
        super.init(config);
    }
    public void doPost(HttpServletRequest request,HttpServletResponse response)throws IOException {
        System.out.println("receive httpPost request");
        String userName=request.getParameter("userName");
        String passWord=request.getParameter("passWord");
        String localIp=request.getLocalAddr();
        String localName=request.getLocalName();
        int localPort=request.getLocalPort();
        int serverPort=request.getServerPort();
        String leeHeader=request.getHeader("lee");
        System.out.println("userName="+userName+",passWord="+passWord+",localIp="+localIp+",localName="+localName+",localPort="+localPort);
        System.out.println("serverPort="+serverPort+",leeHeader="+leeHeader);
        String remoteIp=request.getRemoteAddr();
        String remoteHost=request.getRemoteHost();
        int remotePort=request.getRemotePort();
        String remoteUser=request.getRemoteUser();
        System.out.println("remoteIp="+remoteIp+",remoteHost="+remoteHost+",remotePort="+remotePort+",remoteUser="+remoteUser);

        ServletOutputStream out=response.getOutputStream();
        BufferedWriter writer=new BufferedWriter(new OutputStreamWriter(out));
        writer.write("success");
        writer.flush();
        writer.close();

    }
}




Post代理测试类:
//HttpClientPostProxyServletTest.java

package com.abin.lee.ssh.senior.proxy.httpclient;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.junit.Test;

public class HttpClientPostProxyServletTest {
 public static final String HttpPostProxyUrl="http://localhost:8100/MyThread/HttpClientPostProxyServlet";
 @Test
    public  void testHttpClientPostProxyServlet()throws Exception {
        HttpHost proxy = new HttpHost("10.10.10.10", 1443, "http");

        DefaultHttpClient httpclient = new DefaultHttpClient();
        try {
            httpclient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);

            HttpHost target = new HttpHost("localhost", 8100, "http");
            HttpPost request = new HttpPost(HttpPostProxyUrl);
      StringEntity reqEntity = new StringEntity("userName=abin&passWord=varyall");  
      reqEntity.setContentType("application/x-www-form-urlencoded");  
      request.setEntity(reqEntity);  
      request.setHeader("lee", "lee");
            System.out.println("executing request to " + target + " via " + proxy);
            HttpResponse rsp = httpclient.execute(target, request);
            HttpEntity entity = rsp.getEntity();

            System.out.println("----------------------------------------");
            System.out.println(rsp.getStatusLine());
            Header[] headers = rsp.getAllHeaders();
            for (int i = 0; i<headers.length; i++) {
                System.out.println(headers[i]);
            }
            System.out.println("----------------------------------------");

            if (entity != null) {
                System.out.println(EntityUtils.toString(entity));
            }

        } finally {
            // When HttpClient instance is no longer needed,
            // shut down the connection manager to ensure
            // immediate deallocation of all system resources
            httpclient.getConnectionManager().shutdown();
        }
    }
}




//正常测试代码,非代理

package com.abin.lee.ssh.senior.proxy.httpclient;

import java.io.BufferedReader;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.junit.Test;

public class HttpClientPostServletTest {
 public static final String HttpPostUrl="http://localhost:8100/MyThread/HttpClientPostProxyServlet";
 @Test
 public void testHttpClientPostServlet(){
   HttpClient httpClient = new DefaultHttpClient();
   HttpPost httpPost = new HttpPost(HttpPostUrl);
  try {
      // 目标地址  
       System.out.println("请求: " + httpPost.getRequestLine());  
      // 构造最简单的字符串数据  
       StringEntity reqEntity = new StringEntity("userName=abin&passWord=varyall");  
      // 设置类型  
       reqEntity.setContentType("application/x-www-form-urlencoded");  
      // 设置请求的数据  
       httpPost.setEntity(reqEntity);  
       httpPost.setHeader("lee", "lee");
      // 执行  
       HttpResponse response = httpClient.execute(httpPost);  
       HttpEntity entity = response.getEntity();  
       System.out.println("----------------------------------------");  
       System.out.println(response.getStatusLine());  
      if (entity != null) {  
         System.out.println("Response content length: " + entity.getContentLength());  
       }  
      // 显示结果  
       BufferedReader reader = new BufferedReader(new InputStreamReader(entity.getContent(), "UTF-8"));  
       String line = null;  
      while ((line = reader.readLine()) != null) {  
         System.out.println(line);  
       }  
  } catch (Exception e) {
   e.printStackTrace();
  }finally{
   if(!httpPost.isAborted()){
    httpPost.abort();
   }
   httpClient.getConnectionManager().shutdown();
  }
 }
}



//servlet配置
  <servlet>
             <servlet-name>HttpClientPostProxyServlet</servlet-name>
             <servlet-class>com.abin.lee.servlet.HttpClientPostProxyServlet</servlet-class>
         </servlet>
         <servlet-mapping>
             <servlet-name>HttpClientPostProxyServlet</servlet-name>
             <url-pattern>/HttpClientPostProxyServlet</url-pattern>
         </servlet-mapping>




我这里的IP和端口不一定能用哟,自己找能用的!!代码是100%没问题的,经过生产环境测试的哟!!!

posted @ 2013-04-18 17:45 abin 阅读(1163) | 评论 (1)编辑 收藏

     摘要: Introduction EasyMock (http://www.easymock.org/)使用Java的proxy机制即时为接口提供Mock Object (和扩展类的object)。因EasyMock独特的期望记录方式,大部分重构不会印象Mock Object,故EasyMock很适合于TDD。 EasyMock的license为Apache 2.0 (details see...  阅读全文
posted @ 2013-04-10 22:06 abin 阅读(348) | 评论 (0)编辑 收藏

使用simple-spring-memcached统一缓存的使用

如何在一个中型的Java应用中使用Memcached缓存数据不是个简单的问题。当某个缓存数据需要在多个系统间共享和失效时,必须要有统一的规划才能保证不出错。经过各种实践,目前系统在使用Memcached缓存数据全部采用Simple-Spring-Memcached框架来完成,并统一规划各系统Spring和Cache key的配置。
下面对在使用过程中需要注意的点做一个详细说明:

Cache整体规划

目前我们系统中有两个不同的Memcached服务器:

  1. session memcached服务器:主要存储用户的session
  2. app memcached服务器: 主要用于缓存应用数据

由于应用所有的缓存数据都放在app缓存上,为避免各应用的缓存数据出现冲突,必须规划好它们的命名空间。所幸Simple-Spring-Memcached支持namespace的概念,因此对各应用的namespace前缀规定如下:

应用NAMESPACE前缀
goodscentergoodscenter
tradetrade
uicuic

这个namespace在生成key时,将放在最前面,稍后会有例子详述。
同一个应用中存在许多需要缓存的对象,因此约定namespace前缀之后再加上缓存对象的类名。
例子如下:

应用缓存对象完整的NAMESPACE最终生成的KEY
tradeTcRate (id为42)trade:TcRatetrade:TcRate:12
goodscenterGoodsDo(id为42)goodscenter:GoodsDogoodscenter:GoodsDo:12

key的生成规则

Simple-Spring-Memcached提供的针对单个对象的注解接口提供了两种key生成方式,详情见此文

  1. AssignCache类注解通过assignKey指定cache的key
  2. SingleCache类注解通过ParameterValueKeyProvider注解指定生成key的方法

对于第一种只要求必须保证key不与其它的冲突,且namesapce符合规则。
第二种时,约定缓存的数据对象必须实现有带CacheKeyMethod的cacheKey方法,参考实现如下:

    @CacheKeyMethod     public String cacheKey() {         return this.getId();     }
目前@CacheKeyMethod只支持返回String的方法,需要改造成可接受Long,Integer型的。当前必须有单独的方法来作为缓存Key的生成器
真实存放到Memcached的key的生成规则是:namespace:key。
如goodscenter的id为42的domain对象GoodsDo,按上述方式生成的key为:goodscenter:GoodsDo:42

spring配置说明

关于Simple-Spring-Memcached具体XML配置如下:

<beans xmlns="http://www.springframework.org/schema/beans"        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"        xmlns:context="http://www.springframework.org/schema/context"        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">      <import resource="classpath:simplesm-context.xml"/>      <aop:aspectj -autoproxy/>     <context:annotation -config/>      <bean name="appCache" class="com.google.code.ssm.CacheFactory">         <property name="cacheClientFactory">             <bean class="com.google.code.ssm.providers.xmemcached.MemcacheClientFactoryImpl"/>         </property>         <property name="addressProvider">             <bean class="com.google.code.ssm.config.DefaultAddressProvider">                 <!--memcached服务器ip:port 可以是多个,格式为: 127.0.0.1:11211,192.168.100.11:11211-->                 <property name="address" value="{memcached.server}"/>             </bean>         </property>         <property name="configuration">             <!-- memcached连接器的配置,具体的配置项参考这个类 -->             <bean class="com.google.code.ssm.providers.XMemcachedConfiguration">                 <!--是否使用一致性哈希-->                 <property name="consistentHashing" value="true"/>                 <!--连接池-->                 <property name="connectionPoolSize" value="10"/>                 <property name="optimizeGet" value="true"/>              </bean>         </property>         <property name="cacheName">             <!-- 该Memcached配置的Cache名称 一个应用中存在多个Memcached时,各个配置的cacheName必须不同。如果该值未设,系统默认为default -->             <value>appCache</value>         </property>     </bean> </beans>

Java代码中使用说明

a. 注解方式使用

直接使用注解来处理缓存及失效非常简单,下面是相应的例子:
读取缓存:

EventGoodsServiceClientImpl.java
    @Override     @ReadThroughSingleCache(namespace = "goodscenter:EventGoodsDo", expiration = 60)     @CacheName("appCache")     public EventGoodsDo queryEventGoodsDo(@ParameterValueKeyProvider(order = 0) long goodsId, @ParameterValueKeyProvider(order = 1) long eventId) {         return getRemoteServiceBean().queryEventGoodsDo(goodsId, eventId);     }

更新缓存:

EventGoodsDaoImpl.java
@BridgeMethodMappings(value = {@BridgeMethodMapping(erasedParamTypes ={Object.class},targetParamTypes = {com.hqb360.promotion.dao.entity.EventGoods.class},methodName = "update")}) public class EventGoodsDaoImpl&lt;EventGoods&gt; extends BaseDaoImpl&lt;EventGoods&gt; implements EventGoodsDao&lt;EventGoods&gt; {      @Override     public DaoStatementName getDaoStatementName() {         return new DefaultDaoStatementName() {             public String getDomainName() {                 return "EventGoods";             }         };     }      @Override     @InvalidateSingleCache(namespace = "goodscenter:EventGoodsDo")     @CacheName("appCache")     public void update(@ParameterValueKeyProvider EventGoods obj) throws DataAccessException {         super.update(obj);     } }
EventGoods.java
    @CacheKeyMethod     public String getCacheKey() {         return goodsId + CACHE_ID_SEPARATOR + eventId;     }      public static final String CACHE_ID_SEPARATOR = "/";
上述代码需要注意的点
  1. 多个方法参数都作为cacheKey时,ParameterValueKeyProvider必须指明其order值
  2. 多个方法参数作为cacheKey时,参数之间在 / 号分隔
  3. EventGoodsDaoImpl类中的update方法参数接收的是一个泛型对象,因此必须在该类上配置BridgeMethodMappings。具体配置见示例

b. 以bean的方式使用Cache对象

某些场景我们希望更便捷地自己手动来管理缓存数据,此时需要使用Simple-Spring-Memcached配置中定义的bean。以上面的配置文件为例,使用方法如下
bean的注入:

@Autowired private Cache appCache;

bean的使用:

appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);

Posted in Java

 

posted @ 2013-04-03 18:45 abin 阅读(3729) | 评论 (0)编辑 收藏

@CacheName指定缓存实例注解

@CacheKeyMethod:缓存key生成注解

---------------------------------读取-------------------------------------------

@ReadThroughAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): 读取指定key缓存

@ReadThroughSingleCache(namespace = SINGLE_NS, expiration = 0):读取单个缓存

@ReadThroughMultiCache(option = @ReadThroughMultiCacheOption(generateKeysFromResult = true)):读取多个缓存

@ReadThroughMultiCacheOption(generateKeysFromResult = true) 读取多个缓存操作generateKeysFromResult 通过结果生成key

 

---------------------------------更新-------------------------------------------

@UpdateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo", expiration = 3000): 指定key更新缓存

@UpdateSingleCache(namespace = SINGLE_NS, expiration = 2): 更新单个缓存(namespace 命名空间, expiration 失效时间单位秒)

@UpdateMultiCache(namespace = "Bravo", expiration = 300): 更新多个缓存

 

---------------------------------失效-------------------------------------------

@InvalidateAssignCache(assignedKey = "SomePhatKey", namespace = "Echo") : 指定key失效缓存

@InvalidateSingleCache(namespace = SINGLE_NS):失效单个缓存

@InvalidateMultiCache(namespace = "Delta") : 失效多个缓存

 

---------------------------------参数-------------------------------------------

@ParameterDataUpdateContent标记方法的参数作为更新内容。这个注解应结合Update*Cache注解使用

@ParameterValueKeyProvider: 标记将方法的参数做为计算缓存key.如果方法被注解的对象标记CacheKeyMethod的方法将会用来生成缓存key否则调用toString()生成

@ParameterValueKeyProvider(order=0) 属性表示如果多个参数做为key时需提供参数顺序

与@ParameterValueKeyProvider类似的注解有:

{

  @ReturnValueKeyProvider返回值对象中计算key

}

---------------------------------泛型处理-------------------------------------------

@BridgeMethodMappings({ @BridgeMethodMapping(methodName = "updateUser", 

erasedParamTypes = { Object.class }, targetParamTypes = { AppUser.class }) }): 泛型桥接注解

methodName 指定方法

erasedParamTypes 擦除对象类型

targetParamTypes 目标转换类型

 

 

---------------------------------计数器-------------------------------------------

@InvalidateAssignCache  :在给的计算器上加1. 如果不存在则初始化为1

@DecrementCounterInCache 在给的计数器上减1

 

@ReadCounterFromCache  :读取计数器

@UpdateCounterFromCache 更新计数器

 

 

 

Simple-Spring-Memcached代码阅读之BridgeMethod

 

http://www.colorfuldays.org/program/java/bridgemethod%E7%9A%84%E4%BD%9C%E7%94%A8/

 

http://www.colorfuldays.org/tag/ssm/   这个系列不错

 

b. 以bean的方式使用Cache对象

某些场景我们希望更便捷地自己手动来管理缓存数据,此时需要使用Simple-Spring-Memcached配置中定义的bean。以上面的配置文件为例,使用方法如下
bean的注入:

@Autowired private Cache appCache;

bean的使用:

appCache.set(Constants.CACHE_KEY + members.getMemberId(), 3600,cacheValue);
posted @ 2013-04-03 18:43 abin 阅读(1494) | 评论 (2)编辑 收藏

主要是思维方式的不同: 

显然,RPC是以方法调用的方式描述WebSerivce的,也就是说,你要说清楚调用的那个方法,以及各个参数的名称和值。要描述这些东东,SOAP消息就要有一个统一的规范,指出那一部分是方法名,哪个部分是参数,哪个部分是返回值。换句话说,RPC方式调用的SOAP消息格式是有章可循的,固定的。(比如说,每个Parameter必须对应一个Part,Part的name必须和参数名一致)。 

而Document则是以文档传输的方式描述WebService,只要你的SoapBody里面是一个可以用Schema描述的合法的Xml文档就行了,对具体的格式没有什么要求(Schema要在WSDL里面写)。 

可以看出,Document形式要更加灵活——尤其是需要传输特定格式的Xml文档的时候,而RPC的Soap消息实际上也可以用Document形式模拟(只要Schema定义得当)。所以目前Document方式应用更广泛一些(也是.NET里面的缺省方式)。 

对Namespace,我觉得两者应该没有明显的区别。主要是RPC通常与Encoding模式结合使用,这就要引用Soap的namespace了;而Document只要引用XmlSchema的Namespace定义类型就成了。

posted @ 2013-04-02 15:26 abin 阅读(551) | 评论 (0)编辑 收藏

尽管是转载的,但是原文中几处错误或者不规范的表达,我已经进行了修改。
 
大部分 Web 服务都是围绕着远程过程调用而构建的,而 WSDL 规范允许另外一种 Web 服务体系结构:文档样式( document style )。在该体系结构中,整个文档在服务客户端和服务器之间进行交换。在本文中, James McCarthy 将向您解释文档样式以及应该何时使用它。
Web 服务描述语言( Web Service Definition Language WDSL )规范中隐含着一个非常巧妙的转换开关,它可以将 Web 服务的 SOAP 绑定从远程过程调用转换成 pass-through 文档。在 SOAP 协议绑定中的样式属性可以包含这两个值中的一个: rpc document 。当属性被设定为文档样式时,客户端知道应该使用 XML 模式而不是远程过程调用约定。本文将提供对这个 WSDL 转换开关的说明,描述它的好处,并将解释应该何时使用 pass-through 文档。
首先,让我们简要地谈谈 WSDL 的一些要点,来理解这个巧妙的转换是如何发生的。 WSDL 是一项 XML 规范,它被用来描述Web服务以及对于到达端点(服务)的协议相关的需求。 WSDL 用抽象术语来描述服务;通过可扩展的绑定定义,它能够为使用具体术语调用服务定义协议和数据格式规范。下面的语法是直接从 WSDL 规范中摘 录出来的,展示了在绑定中所包含的可扩展性元素:

<wsdl:definitions .... >
    <wsdl:binding name="nmtoken" type="qname"> *
        <-- extensibility element (1) --> *
        <wsdl:operation name="nmtoken"> *
           <-- extensibility element (2) --> *
           <wsdl:input name="nmtoken"? > ?
               <-- extensibility element (3) -->
           </wsdl:input>
           <wsdl:output name="nmtoken"? > ?
               <-- extensibility element (4) --> *
           </wsdl:output>
           <wsdl:fault name="nmtoken"> *
               <-- extensibility element (5) --> *
           </wsdl:fault>
        </wsdl:operation>
    </wsdl:binding>
</wsdl:definitions>

 
WSDL 规范通常描述三种绑定扩展: HTTP GET/POST MIME 以及 SOAP version 1.1 HTTP GET/POST MIME 中定义的绑定扩展用来定义与标准的 Web 应用程序进行通信的需
求,这些应用程序可能返回(也可能不返回) XML 文档。在发送或返回 XML 文档时, HTTP GET/POST 绑定的扩展是隐式的文档样式。
SOAP 绑定扩展用来定义支持 SOAP 信封协议的服务。 SOAP 信封是一种简单模式,它设计成能包含 XML 消息,提供特定于应用程序的消息头和消息体。 SOAP 绑定的扩展使 WSDL 文档能够声明 SOAP 消息的需求,这样应用程序就能够与服务正确通信。 SOAP 扩展允许将 SOAP 消息的样式声明为文档或 RPC 。如果在 soap:binding 元素中声明了样式属性,那么该样式将成为所有没有显式声明的样式属性的 soap:operation 元素的缺省值。如果在 soap:binding 元素中没有声明样式属性,那么缺省的样式就是文档。下面是文档样式的显式声明:

<soap:binding style="document" transport="uri">

 
不管 soap:binding 元素中的声明如何, soap:operation 元素可以覆盖每个操作的声明,就像这样的:

 
<soap:operation soapAction="uri" style="document">

 
在声明了文档样式的 SOAP 消息中,原始( as-is )或编码( encoded )的消息被直接放置在 SOAP 信封的体部。
如果样式声明为 RPC ,消息就封装在包装器元素中,同时带有从操作名属性中提取的的元素的名称以及从操作名称空间属性中提取的名称空间。
勿庸置疑,使用 XML 调用跨平台的远程过程调用的能力是非常有用的,它是使用 Web 服务的非常有说服力的理由。但是如果 Web 服务仅仅局限于 RPC 消息传递,这项技术的影响将是有限的。幸运的是,开发人员可以选择是使用 RPC 还是文档样式的消息传递,并且能够使用适合的技术来完成他们面临的任务。
XML 规范开发用来使通常锁定于专有格式的常规数据可以以一种人易读的、自描述的、自验证的开放格式来描述。当 Web 服务使用文档消息传递时,它可以利用 XML 的全部能力来描述和验证高级业务文档。当服务使用 RPC 消息格式化时, XML 描述方法以及为方法调用编码的参数,但是却不能用来执行高级业务规则。为了执行这些规则, RPC 消息必须包含 XML 文档作为字符串参数并且在被调用的方法中隐藏验证。出于这个原因, XML 的某些好处就丧失了,或者至少是被隐藏在后台应用程序里了。
使用文档消息传递的另外一个原因在于,远程过程调用必须是相对静态的,并且对接口的任何变化都将破坏服务和应用程序之间的契约。如果服务是广泛分布的,那么很可能大量的应用程序已经从它的 WSDL 文档中产生了存根代码。改变 WSDL 将会导致所有依赖于特定方法签名的应用程序被破坏,而且许多支持行产生问题。好的设计要求 RPC 消息服务的方法签名不应该改变。使用文档消息传递,规则更严格,并且可以使 XML 模式得到显著增强和改变,同时又不会破坏调用应用程序。
当业务使用基于 Web 的应用程序通过 Internet 交换信息时,应用程序应该能够使用有保证的交付机制来提高它的可靠性、可伸缩性和性能。为了达到这个目的,应用程序通常将使用异步消息队列。由于文档消息通常是自包含的,所以它更适合于异步处理,并且可以直接放到队列中。之所以说应用程序的可靠性得到了提高,是因为即使目标应用程序当前不是活动的,消息队列也可以保证消息的交付;之所以说性能得到了提高,是因为 Web 应用程序只是把文档发送到队列中,然后便可以自由地执行其他的任务;之所以说可扩展性得到了提高,是因为文档被下传到一个或多个应用程序的实例以进行处理。
业务文档的设计通常很好地适于面向对象的体系结构。结果,两个应用程序可以设计成通过使用 XML 交换对象的状态。与对象序列化相比,在对象交换中,交换的每个端点都可以自由地设计它认为合适的对象,只要交换符合达成协议的 XML 文件格式即可。不使用对象序列化的一个原因是为了支持对象在客户端和服务器端的实现。许多现有的特定于行业的 XML 模式被设计成客户端 / 服务器体系结构,在这种体系结构中,在客户端上完成的处理与预定在服务器上完成的处理是分离的。通常的情况是,客户端仅仅以服务器要求的特定文档格式请求或保存信息。当然,这种类型的交换也能用 RPC 消息完成,但是这种消息的编码方式限制了每个端点上的对象的设计。在文档样式中就不会出现这些限制问题。
什么时候应该使用文档样式呢?简单地说:只要没有连接到已存在的远程过程调用,任何时候都可以使用文档方式。使用文档方式比起通常花费额外的工作来连接服务,好处要大得多。不过需要提醒的是:一般来说,构建一个使用文档消息传递的服务的工作量要比构建一个 RPC 消息服务所需的工作量大。这些额外的工作通常包括 XML 模式的设计或对已存在的模式的支持、以及从文档中提取相关的信息。模式设计是重要的,因为 XML 解析器使用这个模式来验证文档,支持预定的业务规则。服务需要进行额外的工作来从文档中提取用于处理请求的相关信息。相比之下, RPC 消息只需要设计方法的接口,通过方法的接口, RPC 消息就可以自动地编组和解组参数。
当您决定发布一项服务时,您可能应该考虑下列问题。我将在下面的部分中分析您的答案的结果。
  • 这项服务是连接到已存在的过程调用,并且这个过程调用是无状态的吗?
  • 这项服务是仅在您的组织内部使用,还是也可以被外部用户所使用?
  • 参数之一仅仅是 XML 文档规范吗?
  • 这项服务需要请求 / 响应体系结构吗?
  • 参数表示了可以从用于验证的 XML 文档模式受益的复杂结构吗?
  • 所有需要进行交换的信息都能够合理地存放在内存中吗?
 如果必须以特定的顺序调用多个过程来维护应用程序状态,您应该考虑在您的服务中使用文档体系结构。如果需要多个过程调用,那么过程就不是无状态的,并且服务必须维护应用程序状态。在 Web 服务中维护状态可能是困难的;在远程过程调用的情况下,很少有客户端平台会产生能够支持状态信息的存根代码。一个可能的解决方案是使用文档体系结构,并在文档内部传送整个事务的内容。在这种情况下,服务将执行调用,以确保服务内部保持正确的顺序,并且状态信息的维护不超出单个事务的范围。如果仍然需要状态信息,可以将状态信息记录在最终得到的文档中,客户端应用程序也可以维护一个用于服务识别它的状态的令牌。
如果一个应用程序被发布到组织以外,发布者就很难控制谁正依赖于这个服务,以及如果做出任何改动后果会怎样。在这种情况下,使用文档消息传递和支持像 ebXML 这样的通用交换协议可能更加有利。通用交换协议正发展成能改善外部交换的管理,因此新的贸易合作伙伴协定就可以快速地部署。同样,如果您的服务不需要请求 / 响应体系结构,那么通用交换协议就可以更好地设计来处理认证、可靠消息交付以及异步请求 / 响应。
如果您的服务正使用字符串参数来传递或返回 XML 文档,或者它的参数之一是一个具有复杂结构且需要自定义处理的对象,那么文档消息传递就可能是较好的选择。将参数的真实含义隐藏在字符串里经常会导致带有无效参数的有效调用。如果服务发布了 XML 文档模式,那么在调用服务之前根据这个模式进行验证就会更加容易。复杂结构经常用来传递组成完整事务的数百条信息。在处理复杂的结构时,远程过程服务可能不得不处理自定义的编组代码,同时应用程序仍然负责仔细地验证结构的每个元素。如果使用文档消息传递,那么应用程序程序员就可以使用 XML 模式来将验证下传到文档设计器,并且不需要自定义的编组代码。
    择使用文档样式的消息传递还是 RPC 样式的消息传递时,需要考虑的最后一个因素是需要处理的信息量大小。由于采用 RPC 样式的消息传递来编组参数的大部分(如果不是全部的话)实现都是在内存中执行这项操作,所以内存约束可能会使得 RPC 消息传递行不通。许多文档消息传递服务能够选择是用 DOM 还是用 SAX 来处理文档,因而能够最小化内存中的处理。这对于 Web 服务尤为关键,因为它可能需要处理成千上万的请求,而且其中许多是同时发生的。
在您设计下一个 Web 服务时,您需要考虑当前的 WSDL 规范为您提供的所有选择。在开始创建过程性的接口之前,考虑好将如何使用服务,谁将使用它,以及需要交换的数据的类型和数量。设计开发文档样式的 Web 服务可能需要稍多一些的工作量,但是在很多情况下,这些额外的工作量将会换取更高的信息质量和更可靠的交换性能。
posted @ 2013-04-02 15:02 abin 阅读(1384) | 评论 (0)编辑 收藏

TCP/IP:
数据链路层:ARP,RARP
网络层: IP,ICMP,IGMP
传输层:TCP ,UDP,UGP
应用层:Telnet,FTP,SMTP,SNMP.

OSI:
物理层:EIA/TIA-232, EIA/TIA-499, V.35, V.24, RJ45, Ethernet, 802.3, 802.5, FDDI, NRZI, NRZ, B8ZS
数据链路层:Frame Relay, HDLC, PPP, IEEE 802.3/802.2, FDDI, ATM,  IEEE 802.5/802.2
网络层:IP,IPX,AppleTalk DDP
传输层:TCP,UDP,SPX
会话层:RPC,SQL,NFS,NetBIOS,names,AppleTalk,ASP,DECnet,SCP
表示层:TIFF,GIF,JPEG,PICT,ASCII,EBCDIC,encryption,MPEG,MIDI,HTML
应用层:FTP,WWW,Telnet,NFS,SMTP,Gateway,SNMP

应用层
1.主要功能 :用户接口、应用程序
application 2.典型设备:网关
3.典型协议、标准和应用:TELNET, FTP, HTTP

表示层
1.主要功能 :数据的表示、压缩和加密
presentation2.典型设备:网关
3.典型协议、标准和应用:ASCLL、PICT、TIFF、JPEG、 MIDI、MPEG

会话层
1.主要功能 :会话的建立和结束
session2.典型设备:网关
3.典型协议、标准和应用:RPC、SQL、NFS 、X WINDOWS、ASP


传输层
1.主要功能 :端到端控制
transport 2.典型设备:网关
3.典型协议、标准和应用:TCP、UDP、SPX

网络层
1.主要功能 :路由,寻址
network2.典型设备:路由器
3.典型协议、标准和应用:IP、IPX、APPLETALK、ICMP

数据链路层
1.主要功能 :保证误差错的数据链路
data link 2.典型设备:交换机、网桥、网卡
3.典型协议、标准和应用:802.2、802.3ATM、HDLC、FRAME RELAY

物理层
1.主要功能 :传输比特流
physical2.典型设备:集线器、中继器
3.典型协议、标准和应用:V.35、EIA/TIA-232

从下到上,物理层最低的!!!!应用层最高。

什么是TCP/IP协议,划为几层,各有什么功能?
TCP/IP协议族包含了很多功能各异的子协议。为此我们也利用上文所述的分层的方式来剖析它的结构。TCP/IP层次模型共分为四层:应用层、传输层、网络层、数据链路层。

TCP/IP网络协议
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网间网协议)是目前世界上应用最为广泛的协议,它的流行与Internet的迅猛发展密切相关—TCP/IP最初是为互联网的原型ARPANET所设计的,目的是提供一整套方便实用、能应用于多种网络上的协议,事实证明TCP/IP做到了这一点,它使网络互联变得容易起来,并且使越来越多的网络加入其中,成为Internet的事实标准。

* 应用层—应用层是所有用户所面向的应用程序的统称。ICP/IP协议族在这一层面有着很多协议来支持不同的应用,许多大家所熟悉的基于Internet的应用的实现就离不开这些协议。如我们进行万维网(WWW)访问用到了HTTP协议、文件传输用FTP协议、电子邮件发送用SMTP、域名的解析用DNS协议、远程登录用Telnet协议等等,都是属于TCP/IP应用层的;就用户而言,看到的是由一个个软件所构筑的大多为图形化的操作界面,而实际后台运行的便是上述协议。

* 传输层—这一层的的功能主要是提供应用程序间的通信,TCP/IP协议族在这一层的协议有TCP和UDP。

* 网络层—是TCP/IP协议族中非常关键的一层,主要定义了IP地址格式,从而能够使得不同应用类型的数据在Internet上通畅地传输,IP协议就是一个网络层协议。

* 网络接口层—这是TCP/IP软件的最低层,负责接收IP数据包并通过网络发送之,或者从网络上接收物理帧,抽出IP数据报,交给IP层。

1.TCP/UDP协议
TCP (Transmission Control Protocol)和UDP(User Datagram Protocol)协议属于传输层协议。其中TCP提供IP环境下的数据可靠传输,它提供的服务包括数据流传送、可靠性、有效流控、全双工操作和多路复用。通过面向连接、端到端和可靠的数据包发送。通俗说,它是事先为所发送的数据开辟出连接好的通道,然后再进行数据发送;而UDP则不为IP提供可靠性、流控或差错恢复功能。一般来说,TCP对应的是可靠性要求高的应用,而UDP对应的则是可靠性要求低、传输经济的应用。TCP支持的应用协议主要有:Telnet、FTP、SMTP等;UDP支持的应用层协议主要有:NFS(网络文件系统)、SNMP(简单网络管理协议)、DNS(主域名称系统)、TFTP(通用文件传输协议)等。

IP协议的定义、IP地址的分类及特点

什么是IP协议,IP地址如何表示,分为几类,各有什么特点?
为了便于寻址和层次化地构造网络,IP地址被分为A、B、C、D、E五类,商业应用中只用到A、B、C三类。

IP协议(Internet Protocol)又称互联网协议,是支持网间互连的数据报协议,它与TCP协议(传输控制协议)一起构成了TCP/IP协议族的核心。它提供网间连接的完善功能, 包括IP数据报规定互连网络范围内的IP地址格式。

Internet 上,为了实现连接到互联网上的结点之间的通信,必须为每个结点(入网的计算机)分配一个地址,并且应当保证这个地址是全网唯一的,这便是IP地址。

目前的IP地址(IPv4:IP第4版本)由32个二进制位表示,每8位二进制数为一个整数,中间由小数点间隔,如159.226.41.98,整个IP地址空间有4组8位二进制数,由表示主机所在的网络的地址(类似部队的编号)以及主机在该网络中的标识(如同士兵在该部队的编号)共同组成。

为了便于寻址和层次化的构造网络,IP地址被分为A、B、C、D、E五类,商业应用中只用到A、B、C三类。

* A类地址:A类地址的网络标识由第一组8位二进制数表示,网络中的主机标识占3组8位二进制数,A类地址的特点是网络标识的第一位二进制数取值必须为 “0”。不难算出,A类地址允许有126个网段,每个网络大约允许有1670万台主机,通常分配给拥有大量主机的网络(如主干网)。

* B类地址:B类地址的网络标识由前两组8位二进制数表示,网络中的主机标识占两组8位二进制数,B类地址的特点是网络标识的前两位二进制数取值必须为“10”。B类地址允许有16384个网段,每个网络允许有65533台主机,适用于结点比较多的网络(如区域网)。

* C类地址:C类地址的网络标识由前3组8位二进制数表示,网络中主机标识占1组8位二进制数,C类地址的特点是网络标识的前3位二进制数取值必须为“110”。具有C类地址的网络允许有254台主机,适用于结点比较少的网络(如校园网)。

为了便于记忆,通常习惯采用4个十进制数来表示一个IP地址,十进制数之间采用句点“.”予以分隔。这种IP地址的表示方法也被称为点分十进制法。如以这种方式表示,A类网络的IP地址范围为1.0.0.1-127.255.255.254;B类网络的IP地址范围为:128.1.0.1-191.255.255.254;C类网络的IP地址范围为:192.0.1.1-223.255.255.254。

由于网络地址紧张、主机地址相对过剩,采取子网掩码的方式来指定网段号。

TCP/IP协议与低层的数据链路层和物理层无关,这也是TCP/IP的重要特点。正因为如此 ,它能广泛地支持由低两层协议构成的物理网络结构。目前已使用TCP/IP连接成洲际网、全国网与跨地区网。

OSP与TCP/IP的参考层次图:

 

OSI七层协议和TCP/IP四层协议之比较

 

OSP与TCP/IP的比较:

分层结构
OSI参考模型与TCP/IP协议都采用了分层结构,都是基于独立的协议栈的概念。OSI参考模型有7层,而TCP/IP协议只有4层,即TCP/IP协议没有了表示层和会话层,并且把数据链路层和物理层合并为网络接口层。不过,二者的分层之间有一定的对应关系

标准的特色
OSI参考模型的标准最早是由ISO和CCITT(ITU的前身)制定的,有浓厚的通信背景,因此也打上了深厚的通信系统的特色,比如对服务质量(QoS)、差错率的保证,只考虑了面向连接的服务。并且是先定义一套功能完整的构架,再根据该构架来发展相应的协议与系统。

TCP/IP协议产生于对Internet网络的研究与实践中,是应实际需求而产生的,再由IAB、IETF等组织标准化,而并不是之前定义一个严谨的框架。而且TCP/IP最早是在UNIX系统中实现的,考虑了计算机网络的特点,比较适合计算机实现和使用。

连接服务
OSI的网络层基本与TCP/IP的网际层对应,二者的功能基本相似,但是寻址方式有较大的区别。

OSI的地址空间为不固定的可变长,由选定的地址命名方式决定,最长可达160byte,可以容纳非常大的网络,因而具有较大的成长空间。根据OSI的规定,网络上每个系统至多可以有256个通信地址。

TCP/IP网络的地址空间为固定的4byte(在目前常用的IPV4中是这样,在IPV6中将扩展到16byte)。网络上的每一个系统至少有一个唯一的地址与之对应。

传输服务
OSI与TCP/IP的传输层都对不同的业务采取不同的传输策略。OSI定义了五个不同层次的服务:TP1,TP2,TP3,TP4,TP5。TCP/IP定义了TCP和UPD两种协议,分别具有面向连接和面向无连接的性质。其中TCP与OSI中的TP4,UDP与OSI中的TP0在构架和功能上大体相同,只是内部细节有一些差异。

应用范围
OSI由于体系比较复杂,而且设计先于实现,有许多设计过于理想,不太方便计算机软件实现,因而完全实现OSI参考模型的系统并不多,应用的范围有限。而TCP/IP协议最早在计算机系统中实现,在UNIX、Windows平台中都有稳定的实现,并且提供了简单方便的编程接口(API),可以在其上开发出丰富的应用程序,因此得到了广泛的应用。TCP/IP协议已成为目前网际互联事实上的国际标准和工业标准。

posted @ 2013-03-28 21:26 abin 阅读(829) | 评论 (0)编辑 收藏

 数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,会从池中取出一个连接。如果当前池中正在使用的连接数等于maxActive,则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超过了maxWait,则会报错;如果当前正在使用的连接数没有达到maxActive,则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立一个连接。在连接使用完毕后,不是将其物理连接关闭,而是将其放入池中等待其他操作复用。
  对于一个数据库连接池,一般包括一下属性。
  
  
  名称   说明   备注
minIdle 最小活跃数 可以允许的空闲连接数目
maxActive 最大连接数 相当于池的大小
initialSize 初始化连接大小
maxWait 获取连接的最大等待时间
timeBetweenEvictionRunsMillis 空闲连接的最大生存时间 指定空闲连接在空闲多长时间后,关闭其物理连接
testWhileIdle 是否在空闲时检测连接可用性 由于网络或者数据库配置的原因(比如mysql连接的8小时限制),开启这个开关可以定期检测连接的可用性
posted @ 2013-03-27 10:32 abin 阅读(552) | 评论 (0)编辑 收藏


 

锁:

  1. 内置锁 (监视器锁): 每个java对象都可以做一个实现同步的锁,这些锁被成为内置锁. 获得锁的唯一途径就是进入有这个锁保护的代码块或方法
  2. 重入锁: 由于内置锁是可重入的,因此如果某个线程试图获得一个以已经由他自己持有的锁, 那么这个请求就会成功.重入意味着获取锁的操作粒度是"线程",而不是"调用"

volatile 使用条件(必须同时满足所有条件):

  1. 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值
  2. 该变量不会与其他状态变量一起纳入不变性条件中
  3. 在访问变量时间不需要加锁


 


 

高并发术语



术语

英文单词

描述

比较并交换

Compare and Swap

CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换

CPU流水线

CPU pipeline

CPU流水线的工作方式就象工业生产上的装配流水线,在CPU中由5~6个不同功能的电路单元组成一条指令处理流水线,然后将一条X86指令分成5~6步后再由这些电路单元分别执行,这样就能实现在一个CPU时钟周期完成一条指令,因此提高CPU的运算速度

内存顺序冲突

Memory order violation

内存顺序冲突一般是由假共享引起,假共享是指多个CPU同时修改同一个缓存行的不同部分而引起其中一个CPU的操作无效,当出现这个内存顺序冲突时,CPU必须清空流水线

共享变量


在多个线程之间能够被共享的变量被称为共享变量。共享变量包括所有的实例变量,静态变量和数组元素。他们都被存放在堆内存中,Volatile只作用于共享变量。

内存屏障

Memory Barriers

是一组处理器指令,用于实现对内存操作的顺序限制。

缓冲行

Cache line

缓存中可以分配的最小存储单位。处理器填写缓存线时会加载整个缓存线,需要使用多个主内存读周期。

原子操作

Atomic operations

不可中断的一个或一系列操作。

缓存行填充

cache line fill

当处理器识别到从内存中读取操作数是可缓存的,处理器读取整个缓存行到适当的缓存(L1,L2,L3的或所有)

缓存命中

cache hit

如果进行高速缓存行填充操作的内存位置仍然是下次处理器访问的地址时,处理器从缓存中读取操作数,而不是从内存。

写命中

write hit

当处理器将操作数写回到一个内存缓存的区域时,它首先会检查这个缓存的内存地址是否在缓存行中,如果存在一个有效的缓存行,则处理器将这个操作数写回到缓存,而不是写回到内存,这个操作被称为写命中。



 

synchronized

volatile

concurrent 

在并发编程中很常用的实用工具类。此包包括了几个小的、已标准化的可扩展框架,以及一些提供有用功能的类,没有这些类,这些功能会很难实现或实现起来冗长乏味。下面简要描述主要的组件。另请参阅 locks 和 atomic 包。

执行程序

接口。Executor 是一个简单的标准化接口,用于定义类似于线程的自定义子系统,包括线程池、异步 IO 和轻量级任务框架。根据所使用的具体 Executor 类的不同,可能在新创建的线程中,现有的任务执行线程中,或者调用 execute() 的线程中执行任务,并且可能顺序或并发执行。ExecutorService 提供了多个完整的异步任务执行框架。ExecutorService 管理任务的排队和安排,并允许受控制的关闭。ScheduledExecutorService 子接口及相关的接口添加了对延迟的和定期任务执行的支持。ExecutorService 提供了安排异步执行的方法,可执行由 Callable 表示的任何函数,结果类似于 RunnableFuture 返回函数的结果,允许确定执行是否完成,并提供取消执行的方法。RunnableFuture 是拥有 run 方法的 Future,run 方法执行时将设置其结果。

实现。类 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 提供可调的、灵活的线程池。Executors 类提供大多数 Executor 的常见类型和配置的工厂方法,以及使用它们的几种实用工具方法。其他基于 Executor 的实用工具包括具体类 FutureTask,它提供 Future 的常见可扩展实现,以及 ExecutorCompletionService,它有助于协调对异步任务组的处理。

队列

java.util.concurrent ConcurrentLinkedQueue 类提供了高效的、可伸缩的、线程安全的非阻塞 FIFO 队列。java.util.concurrent 中的五个实现都支持扩展的 BlockingQueue 接口,该接口定义了 put 和 take 的阻塞版本:LinkedBlockingQueueArrayBlockingQueueSynchronousQueuePriorityBlockingQueue 和 DelayQueue。这些不同的类覆盖了生产者-使用者、消息传递、并行任务执行和相关并发设计的大多数常见使用的上下文。BlockingDeque 接口扩展 BlockingQueue,以支持 FIFO 和 LIFO(基于堆栈)操作。LinkedBlockingDeque 类提供一个实现。

计时

TimeUnit 类为指定和控制基于超时的操作提供了多重粒度(包括纳秒级)。该包中的大多数类除了包含不确定的等待之外,还包含基于超时的操作。在使用超时的所有情况中,超时指定了在表明已超时前该方法应该等待的最少时间。在超时发生后,实现会“尽力”检测超时。但是,在检测超时与超时之后再次实际执行线程之间可能要经过不确定的时间。接受超时期参数的所有方法将小于等于 0 的值视为根本不会等待。要“永远”等待,可以使用 Long.MAX_VALUE 值。

同步器

四个类可协助实现常见的专用同步语句。Semaphore 是一个经典的并发工具。CountDownLatch 是一个极其简单但又极其常用的实用工具,用于在保持给定数目的信号、事件或条件前阻塞执行。CyclicBarrier 是一个可重置的多路同步点,在某些并行编程风格中很有用。Exchanger 允许两个线程在 collection 点交换对象,它在多流水线设计中是有用的。

并发 Collection

除队列外,此包还提供了设计用于多线程上下文中的 Collection 实现:ConcurrentHashMapConcurrentSkipListMapConcurrentSkipListSetCopyOnWriteArrayList 和 CopyOnWriteArraySet。当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMapConcurrentSkipListMap 通常优于同步的 TreeMap。当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList

此包中与某些类一起使用的“Concurrent&rdquo前缀;是一种简写,表明与类似的“同步”类有所不同。例如,java.util.Hashtable 和Collections.synchronizedMap(new HashMap()) 是同步的,但 ConcurrentHashMap 则是“并发的”。并发 collection 是线程安全的,但是不受单个排他锁的管理。在 ConcurrentHashMap 这一特定情况下,它可以安全地允许进行任意数目的并发读取,以及数目可调的并发写入。需要通过单个锁不允许对 collection 的所有访问时,“同步”类是很有用的,其代价是较差的可伸缩性。在期望多个线程访问公共 collection 的其他情况中,通常“并发”版本要更好一些。当 collection 是未共享的,或者仅保持其他锁时 collection 是可访问的情况下,非同步 collection 则要更好一些。

大多数并发 Collection 实现(包括大多数 Queue)与常规的 java.util 约定也不同,因为它们的迭代器提供了弱一致的,而不是快速失败的遍历。弱一致的迭代器是线程安全的,但是在迭代时没有必要冻结 collection,所以它不一定反映自迭代器创建以来的所有更新。

内存一致性属性

Java Language Specification 第 17 章定义了内存操作(如共享变量的读写)的 happen-before 关系。只有写入操作 happen-before 读取操作时,才保证一个线程写入的结果对另一个线程的读取是可视的。synchronized 和 volatile 构造 happen-before 关系,Thread.start() 和Thread.join() 方法形成 happen-before 关系。尤其是:
  • 线程中的每个操作 happen-before 稍后按程序顺序传入的该线程中的每个操作。
  • 一个解除锁监视器的(synchronized 阻塞或方法退出)happen-before 相同监视器的每个后续锁(synchronized 阻塞或方法进入)。并且因为 happen-before 关系是可传递的,所以解除锁定之前的线程的所有操作 happen-before 锁定该监视器的任何线程后续的所有操作。
  • 写入 volatile 字段 happen-before 每个后续读取相同字段。volatile 字段的读取和写入与进入和退出监视器具有相似的内存一致性效果,但 需要互斥锁。
  • 在线程上调用 start happen-before 已启动的线程中的任何线程。
  • 线程中的所有操作 happen-before 从该线程上的 join 成功返回的任何其他线程。
java.util.concurrent 中所有类的方法及其子包扩展了这些对更高级别同步的保证。尤其是:
  • 线程中将一个对象放入任何并发 collection 之前的操作 happen-before 从另一线程中的 collection 访问或移除该元素的后续操作。
  • 线程中向 Executor 提交 Runnable 之前的操作 happen-before 其执行开始。同样适用于向 ExecutorService 提交 Callables
  • 异步计算(由 Future 表示)所采取的操作 happen-before 通过另一线程中 Future.get() 获取结果后续的操作。
  • “释放”同步储存方法(如 Lock.unlockSemaphore.release 和 CountDownLatch.countDown)之前的操作 happen-before 另一线程中相同同步储存对象成功“获取”方法(如 Lock.lockSemaphore.acquireCondition.await 和 CountDownLatch.await)的后续操作。
  • 对于通过 Exchanger 成功交换对象的每个线程对,每个线程中 exchange() 之前的操作 happen-before 另一线程中对应 exchange() 后续的操作。
  • 调用 CyclicBarrier.await 之前的操作 happen-before 屏障操作所执行的操作,屏障操作所执行的操作 happen-before 从另一线程中对应await 成功返回的后续操作。

 

Condition

Condition 将 Object 监视器方法(waitnotify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

条件(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。

 class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition(); 
   final Condition notEmpty = lock.newCondition(); 

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) 
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;
       ++count;
       notEmpty.signal();
     } finally {
       lock.unlock();
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) 
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }
 

ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。)

Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。

注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用Condition 实例。

除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException

实现注意事项

在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。





http://blog.csdn.net/fh13760184/article/details/8551546#
posted @ 2013-03-21 16:03 abin 阅读(2357) | 评论 (3)编辑 收藏

仅列出标题
共50页: First 上一页 14 15 16 17 18 19 20 21 22 下一页 Last