posts - 110, comments - 101, trackbacks - 0, articles - 7
  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

public class JVMTest {

public static void main(String[] args){
System.out.println("aa:" + aa());
}
public static int aa(){
int a = 1;
int b = 10;
try{
System.out.println("abc");
return a;
}finally{
a = 2;
System.out.println("a: "+ a);
}
}
}

运行结果为:

abc
a: 2
aa:1

由此可知:在try语句中,在执行return语句时,要返回的结果已经准备好了,就在此时,程序转到finally执行了。

在转去之前,try中先把要返回的结果存放到不同于a的局部变量中去,执行完finally之后,在从中取出返回结果,

因此,即使finally中对变量a进行了改变,但是不会影响返回结果。

但是,如果在finally子句中最后添加上return a会怎样呢?

执行结果如下:

Compiling 1 source file to E:\sun\InsideJVM\build\classes
E:\sun\InsideJVM\src\JVMTest.java:37: warning: finally clause cannot complete normally
}
1 warning
compile-single:
run-single:
abc
a: 2
aa:2

测试1
public static int test1()
{
int i = 1;
try
{
return ++i;
}
finally
{
++i;
Console.WriteLine("finally:" + i);
}
}

static void Main(string[] args)
{
Console.WriteLine("Main:" + test1());
}
结果:
finally:3
Main:2

测试2
public static int test2()
{
int i = 1;
try
{
throw new Exception();
}
catch
{
return ++i;
}
finally
{
++i;
Console.WriteLine("finally:" + i);
}
}

static void Main(string[] args)
{
Console.WriteLine("Main:" + test2());
}
结果:
finally:3
Main:2

测试3
public static int test3()
{
try{}
finally
{
return 1;
}
}

结果:
编译错误,控制不能离开 finally 子句主体。

结论:

1.不管出没出现异常,finally块中的语句都会执行;
2.当trycatch块中有return语句时,finally块中的语句仍会执行;
3.finally块中的语句是在return语句执行之后才执行的,即函数返回值是在finally块中语句执行前确定的;
4.finally块中不能包含return语句。

总结:finallyreturn前执行,在finally的操作,不会改变已经确定的return的值,

finally不能加return语句。出现异常,先找是否有处理器可以处理这个异常.finally

posted @ 2011-11-10 21:20 云云 阅读(523) | 评论 (1)编辑 收藏

这篇文章试验了JDK动态代理与CGLIB动态代理。从Spring的AOP框架介绍中得知对于使用接口的类,Spring使用JDK 动态代理(原来做项目中试图从Bean强制转换为实现类,结果报错,原来是这么回事),没有接口的就使用别的AOP框架aspectj,但这些都是依赖于 Java字节码工具ASM生成一个原类的新类,调用Callback

但是JDK动态代理为什么必须使用接口一直很疑惑,难道原理不是像ASM一样修改字节码吗?带着这个疑问,开始看JDK的Proxy代码。使用JDK动态代理的代码代码。

Java代码 复制代码 收藏代码
  1. ITestBean tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));


于是从创建代理函数看起,即public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException ,

通过源码可以看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),

Java代码 复制代码 收藏代码
  1. Class cl = getProxyClass(loader, interfaces);


然后通过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。

Java代码 复制代码 收藏代码
  1. Constructor cons = cl.getConstructor(constructorParams);//这个有用,在后面细说
  2. return (Object) cons.newInstance(new Object[] { h });


接口起什么作用呢,于是又看getProxyClass方法的代码,这个源码很长,就不细说了。大致分为三段:

第一:验证

第二:缓存创建新类的结构,如果创建过,则直接返回。(注意:这里的KEY就是接口列表)

第三:如果没有创建过,则创建新类

创建代码如下
Java代码 复制代码 收藏代码
  1. long num;
  2. //获得代理类数字标识
  3. synchronized (nextUniqueNumberLock) {
  4. num = nextUniqueNumber++;
  5. }
  6. //获得创建新类的类名$Proxy,包名为接口包名,但需要注意的是,如果有两个接口而且不在同一个包下,也会报错
  7. String proxyName = proxyPkg + proxyClassNamePrefix + num;
  8. //调用class处理文件生成类的字节码,根据接口列表创建一个新类,这个类为代理类,
  9. byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
  10. proxyName, interfaces);
  11. //通过JNI接口,将Class字节码文件定义一个新类
  12. proxyClass = defineClass0(loader, proxyName,
  13. proxyClassFile, 0, proxyClassFile.length);



根据前面的代码Constructor cons = cl.getConstructor(constructorParams);

可以猜测到接口创建的新类proxyClassFile 不管采用什么接口,都是以下结构

Java代码 复制代码 收藏代码
  1. public class $Proxy1 extends Proxy implements 传入的接口{
  2. }


生成新类的看不到源代码,不过猜测它的执行原理很有可能是如果类是Proxy的子类,则调用InvocationHandler进行方法的Invoke

到现在大家都应该明白了吧,JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类。

cglib采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类

JDK动态代理与CGLIB对比如下:

//JDK动态代理测试代码

Java代码 复制代码 收藏代码
  1. ITestBean tb = new TestBean();
  2. tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(), tb.getClass().getInterfaces(), new TestBeanHander(tb));//这句用接口引用指向,不会报错
  3. TestBean tmp = (TestBean) tb;//强制转换为实现类,将抛出类强制转换异常


//CGLIB测试代码
Java代码 复制代码 收藏代码
  1. TestProxy tp = new TestProxy();
  2. tb = (ITestBean) tp.getProxy(TestBean.class);
  3. tmp = (TeatBean) tb;//强制转换为实现类,不会抛出异常

补充说明,如果在实现类中,接口定义的方法互相调用不会在调用InvocationHandler的invoke方法,JDK动态代理应该不是嵌入到Java的反射机制中,而是在反射机制上的一个调用。


应用举例如下:

JDK动态代理的简单使用示例:


Java代码 复制代码 收藏代码
  1. package com.proxy;
  2. public class ForumServiceImpl implements ForumService{
  3. public void removeTopic(int topicId){
  4. System.out.println("模拟删除记录"+topicId);
  5. try{
  6. Thread.currentThread().sleep(20);
  7. }catch(Exception e){
  8. throw new RuntimeException(e);
  9. }
  10. }
  11. public void removeForum(int forumId){
  12. System.out.println("模拟删除记录"+forumId);
  13. try{
  14. Thread.currentThread().sleep(20);
  15. }catch(Exception e){
  16. throw new RuntimeException(e);
  17. }
  18. }
  19. }


创建一个实现java.lang.reflect.InvocationHandler 接口的代理类,如:
Java代码 复制代码 收藏代码
  1. import java.lang.reflect.InvocationHandler;
  2. import java.lang.reflect.Method;
  3. public class PerformanceHandler implements InvocationHandler{
  4. private Object target; //要进行代理的业务类的实例
  5. public PerformanceHandler(Object target){
  6. this.target = target;
  7. }
  8. //覆盖java.lang.reflect.InvocationHandler的方法invoke()进行织入(增强)的操作
  9. //在实际应用中, 这里会引用一个Intercepter类来做处理。 然后Intercepter就可以独立发展
  10. public Object invoke(Object proxy, Method method, Object[] args)
  11. throws Throwable{
  12. System.out.println("Object target proxy:"+target);
  13. System.out.println("模拟代理加强的方法...");
  14. Object obj = method.invoke(target, args); //调用目标业务类的方法
  15. System.out.println("模拟代理加强的方法执行完毕...");
  16. return obj;
  17. }
  18. }



用java.lang.reflect.Proxy.newProxyInstance()方法创建动态实例来调用代理实例的方法:
Java代码 复制代码 收藏代码
  1. import java.lang.reflect.Proxy;
  2. public class TestForumService {
  3. public static void main(String args[]){
  4. ForumService target = new ForumServiceImpl();//要进行代理的目标业务类
  5. PerformanceHandler handler = new PerformanceHandler(target);//用代理类把目标业务类进行编织
  6. //创建代理实例,它可以看作是要代理的目标业务类的加多了横切代码(方法)的一个子类
  7. ForumService proxy = (ForumService)Proxy.newProxyInstance(
  8. target.getClass().getClassLoader(),
  9. target.getClass().getInterfaces(), handler);
  10. proxy.removeForum(10);
  11. proxy.removeTopic(20);
  12. }
  13. }



CGLib动态代理示例:


创建一个实现net.sf.cglib.proxy.MethodInterceptor接口的实例来为目标业务类加入进行代理时要进行的操作或增强:

Java代码 复制代码 收藏代码
  1. import java.lang.reflect.Method;
  2. import net.sf.cglib.proxy.MethodProxy;
  3. import net.sf.cglib.proxy.Enhancer;
  4. import net.sf.cglib.proxy.MethodInterceptor;
  5. /**
  6. *CGlib采用非常底层的字节码技术,可以为一个类创建子类,
  7. 并在子类中采用方法拦截技术拦截父类方法的调用,并顺势进行增强,即是织入横切逻辑
  8. * @author tufu
  9. */
  10. public class CglibProxy implements MethodInterceptor{
  11. private Enhancer enhancer = new Enhancer();
  12. //覆盖MethodInterceptor接口的getProxy()方法,设置
  13. public Object getProxy(Class clazz){
  14. enhancer.setSuperclass(clazz); //设者要创建子类的类
  15. enhancer.setCallback(this); //设置回调的对象
  16. return enhancer.create(); //通过字节码技术动态创建子类实例,
  17. }
  18. public Object intercept(Object obj,Method method,Object[] args,
  19. MethodProxy proxy) throws Throwable {
  20. System.out.println("模拟代理增强方法");
  21. //通过代理类实例调用父类的方法,即是目标业务类方法的调用
  22. Object result = proxy.invokeSuper(obj, args);
  23. System.out.println("模拟代理增强方法结束");
  24. return result;
  25. }
  26. }


通过java.lang.reflect.Proxy的getProxy()动态生成目标业务类的子类,即是代理类,再由此得到代理实例:
Java代码 复制代码 收藏代码
  1. import com.proxy.ForumServiceImpl;
  2. import java.lang.reflect.Proxy;
  3. public class TestCglibProxy {
  4. public static void main(String args[]){
  5. CglibProxy proxy = new CglibProxy();
  6. //动态生成子类的方法创建代理类
  7. ForumServiceImpl fsi =
  8. (ForumServiceImpl)proxy.getProxy(ForumServiceImpl.class);
  9. fsi.removeForum(10);
  10. fsi.removeTopic(2);
  11. }
  12. }


总结下Spring的AOP运用的设计模式 , AOP 主要利用代理模式, 然后依赖通知(本人认为是策略模式)来实现AOP。 这样通知就可以独立发展。

posted @ 2011-11-09 23:30 云云 阅读(5539) | 评论 (0)编辑 收藏

数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现.

在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低.

大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle.

少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎

即使是最低的级别,也不会出现 第一类 丢失 更新问题 .

1. 脏读(事务没提交,提前读取):脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。

2. 不可重复读(两次读的不一致) :是指在一个事务内,多次读同一数据。在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。例如,一个编辑人员两次读取同一文档,但在两次读取之间,作者重写了该文档。当编辑人员第二次读取文档时,文档已更改。原始读取不可重复。如果只有在作者全部完成编写后编辑人员才可以读取文档,则可以避免该问题。
3. 幻读 : 是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样。例如,一个编辑人员更改作者提交的文档,但当生产部门将其更改内容合并到该文档的主复本时,发现作者已将未编辑的新材料添加到该文档中。如果在编辑人员和生产部门完成对原始文档的处理之前,任何人都不能将新材料添加到文档中,则可以避免该问题。
4.第一类更新丢失(回滚丢失)
当2个事务更新相同的数据源,如果第一个事务被提交,而另外一个事务却被撤销,那么会连同第一个事务所做的跟新也被撤销。也就是说第一个事务做的跟新丢失了。
5.第二类更新丢失(覆盖丢失)
第二类更新丢失实在实际应用中经常遇到的并发问题,他和不可重复读本质上是同一类并发问题,通常他被看做不可重复读的特例:当2个或这个多个事务查询同样的记录然后各自基于最初的查询结果更新该行时,会造成第二类丢失更新。因为每个事务都不知道不知道其他事务的存在,最后一个事务对记录做的修改将覆盖其他事务对该记录做的已提交的跟新...
补充 : 基于元数据的 Spring 声明性事务 :

Isolation 属性一共支持五种事务设置,具体介绍如下:

l DEFAULT 使用数据库设置的隔离级别 ( 默认 ) ,由 DBA 默认的设置来决定隔离级别 .

l READ_UNCOMMITTED 会出现脏读、不可重复读、幻读 ( 隔离级别最低,并发性能高 )

l READ_COMMITTED 会出现不可重复读、幻读问题(锁定正在读取的行

l REPEATABLE_READ 会出幻读(锁定所读取的所有行

l SERIALIZABLE 保证所有的情况不会发生(锁表

不可重复读的重点是修改 :
同样的条件 , 你读取过的数据 , 再次读取出来发现值不一样了
幻读的重点在于新增或者删除
同样的条件 , 第 1 次和第 2 次读出来的记录数不一样

posted @ 2011-11-09 20:53 云云 阅读(1605) | 评论 (1)编辑 收藏

面临的问题

对于高并发高访问的Web应用程序来说,数据库存取瓶颈一直是个令人头疼的问题。特别当你的程序架构还是建立在单数据库模式,而一个数据池连接数峰值已经达到500的时候,那你的程序运行离崩溃的边缘也不远了。很多小网站的开发人员一开始都将注意力放在了产品需求设计上,缺忽视了程序整体性能,可扩展性等方面的考虑,结果眼看着访问量一天天网上爬,可突然发现有一天网站因为访问量过大而崩溃了,到时候哭都来不及。所以我们一定要未雨绸缪,在数据库还没罢工前,想方设法给它减负,这也是这篇文章的主要议题。

大家都知道,当有一个request过来后,web服务器交给app服务器,app处理并从db中存取相关数据,但db存取的花费是相当高昂的。特别是每次都取相同的数据,等于是让数据库每次都在做高耗费的无用功,数据库如果会说话,肯定会发牢骚,你都问了这么多遍了,难道还记不住吗?是啊,如果app拿到第一次数据并存到内存里,下次读取时直接从内存里读取,而不用麻烦数据库,这样不就给数据库减负了?而且从内存取数据必然要比从数据库媒介取快很多倍,反而提升了应用程序的性能。

因此,我们可以在web/app层与db层之间加一层cache层,主要目的:1. 减少数据库读取负担;2. 提高数据读取速度。而且,cache存取的媒介是内存,而一台服务器的内存容量一般都是有限制的,不像硬盘容量可以做到TB级别。所以,可以考虑采用分布式的cache层,这样更易于破除内存容量的限制,同时又增加了灵活性。

Memcached 介绍

Memcached是开源的分布式cache系统,现在很多的大型web应用程序包括facebook,youtube,wikipedia,yahoo等等都在使用memcached来支持他们每天数亿级的页面访问。通过把cache层与他们的web架构集成,他们的应用程序在提高了性能的同时,还大大降低了数据库的负载。
具体的memcached资料大家可以直接从它的官方网站[1]上得到。这里我就简单给大家介绍一下memcached的工作原理:

Memcached处理的原子是每一个(key,value)对(以下简称kv对),key会通过一个hash算法转化成hash-key,便于查找、对比以及做到尽可能的散列。同时,memcached用的是一个二级散列,通过一张大hash表来维护。

Memcached有两个核心组件组成:服务端(ms)和客户端(mc),在一个memcached的查询中,mc先通过计算key的hash值来确定kv对所处在的ms位置。当ms确定后,客户端就会发送一个查询请求给对应的ms,让它来查找确切的数据。因为这之间没有交互以及多播协议,所以memcached交互带给网络的影响是最小化的。

举例说明:考虑以下这个场景,有三个mc分别是X,Y,Z,还有三个ms分别是A,B,C:

设置kv对
X想设置key=”foo”,value=”seattle”
X拿到ms列表,并对key做hash转化,根据hash值确定kv对所存的ms位置
B被选中了
X连接上B,B收到请求,把(key=”foo”,value=”seattle”)存了起来

获取kv对
Z想得到key=”foo”的value
Z用相同的hash算法算出hash值,并确定key=”foo”的值存在B上
Z连接上B,并从B那边得到value=”seattle”
其他任何从X,Y,Z的想得到key=”foo”的值的请求都会发向B

Memcached服务器(ms)

内存分配

默认情况下,ms是用一个内置的叫“块分配器”的组件来分配内存的。舍弃c++标准的malloc/free的内存分配,而采用块分配器的主要目的是为了避免内存碎片,否则操作系统要花费更多时间来查找这些逻辑上连续的内存块(实际上是断开的)。用了块分配器,ms会轮流的对内存进行大块的分配,并不断重用。当然由于块的大小各不相同,当数据大小和块大小不太相符的情况下,还是有可能导致内存的浪费。

同时,ms对key和data都有相应的限制,key的长度不能超过250字节,data也不能超过块大小的限制 --- 1MB。
因为mc所使用的hash算法,并不会考虑到每个ms的内存大小。理论上mc会分配概率上等量的kv对给每个ms,这样如果每个ms的内存都不太一样,那可能会导致内存使用率的降低。所以一种替代的解决方案是,根据每个ms的内存大小,找出他们的最大公约数,然后在每个ms上开n个容量=最大公约数的instance,这样就等于拥有了多个容量大小一样的子ms,从而提供整体的内存使用率。

缓存策略

当ms的hash表满了之后,新的插入数据会替代老的数据,更新的策略是LRU(最近最少使用),以及每个kv对的有效时限。Kv对存储有效时限是在mc端由app设置并作为参数传给ms的。

同时ms采用是偷懒替代法,ms不会开额外的进程来实时监测过时的kv对并删除,而是当且仅当,新来一个插入的数据,而此时又没有多余的空间放了,才会进行清除动作。

缓存数据库查询
现在memcached最流行的一种使用方式是缓存数据库查询,下面举一个简单例子说明:

App需要得到userid=xxx的用户信息,对应的查询语句类似:

“SELECT * FROM users WHERE userid = xxx”

App先去问cache,有没有“user:userid”(key定义可预先定义约束好)的数据,如果有,返回数据;如果没有,App会从数据库中读取数据,并调用cache的add函数,把数据加入cache中。

当取的数据需要更新,app会调用cache的update函数,来保持数据库与cache的数据同步。

从上面的例子我们也可以发现,一旦数据库的数据发现变化,我们一定要及时更新cache中的数据,来保证app读到的是同步的正确数据。当然我们可以通过定时器方式记录下cache中数据的失效时间,时间一过就会激发事件对cache进行更新,但这之间总会有时间上的延迟,导致app可能从cache读到脏数据,这也被称为狗洞问题。(以后我会专门描述研究这个问题)

数据冗余与故障预防

从设计角度上,memcached是没有数据冗余环节的,它本身就是一个大规模的高性能cache层,加入数据冗余所能带来的只有设计的复杂性和提高系统的开支。

当一个ms上丢失了数据之后,app还是可以从数据库中取得数据。不过更谨慎的做法是在某些ms不能正常工作时,提供额外的ms来支持cache,这样就不会因为app从cache中取不到数据而一下子给数据库带来过大的负载。

同时为了减少某台ms故障所带来的影响,可以使用“热备份”方案,就是用一台新的ms来取代有问题的ms,当然新的ms还是要用原来ms的IP地址,大不了数据重新装载一遍。

另外一种方式,就是提高你ms的节点数,然后mc会实时侦查每个节点的状态,如果发现某个节点长时间没有响应,就会从mc的可用server列表里删除,并对server节点进行重新hash定位。当然这样也会造成的问题是,原本key存储在B上,变成存储在C上了。所以此方案本身也有其弱点,最好能和“热备份”方案结合使用,就可以使故障造成的影响最小化。

Memcached客户端(mc)

Memcached客户端有各种语言的版本供大家使用,包括java,c,php,.net等等,具体可参见memcached api page[2]。
大家可以根据自己项目的需要,选择合适的客户端来集成。

缓存式的Web应用程序架构
有了缓存的支持,我们可以在传统的app层和db层之间加入cache层,每个app服务器都可以绑定一个mc,每次数据的读取都可以从ms中取得,如果没有,再从db层读取。而当数据要进行更新时,除了要发送update的sql给db层,同时也要将更新的数据发给mc,让mc去更新ms中的数据。

假设今后我们的数据库可以和ms进行通讯了,那可以将更新的任务统一交给db层,每次数据库更新数据的同时会自动去更新ms中的数据,这样就可以进一步减少app层的逻辑复杂度。如下图:

不过每次我们如果没有从cache读到数据,都不得不麻烦数据库。为了最小化数据库的负载压力,我们可以部署数据库复写,用slave数据库来完成读取操作,而master数据库永远只负责三件事:1.更新数据;2.同步slave数据库;3.更新cache。如下图:

以上这些缓存式web架构在实际应用中被证明是能有效并能极大地降低数据库的负载同时又能提高web的运行性能。当然这些架构还可以根据具体的应用环境进行变种,以达到不同硬件条件下性能的最优化。

未来的憧憬

posted @ 2011-11-08 21:51 云云 阅读(807) | 评论 (0)编辑 收藏

     摘要: 1. 基本 概念 IO 是主存和外部设备 ( 硬盘、终端和网络等 ) 拷贝数据的过程。 IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成。 所有语言运行时系统提供执行 I/O 较高级别的工具。 (c 的 printf scanf,java 的面向对象封装 ) 2. Java 标准 io 回顾 Java 标准 IO 类库是 io 面向对象的一种抽象。基于本地方法的底层实...  阅读全文

posted @ 2011-11-08 21:25 云云 阅读(1432) | 评论 (0)编辑 收藏

class NewThread implements Runnable {
Thread t;
public NewThread() {
t = new Thread(this,"Demo thread");
System.out.println("Child thread : " + t);
t.run();
}
public void run(){
try{
for( int i = 5; i > 0; i --){
System.out.println("Child thread :" + i);
Thread.sleep(500);
}

}catch(InterruptedException e){
System.out.println("Child interrupted.");
}
System.out.println("Exiting child thread.");

}
}

public class TestDemo{
public static void main(String args[]){
new NewThread();
try{
for( int i = 5; i > 0; i --){
System.out.println("Main thread :" + i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("Main interrupted.");
}
System.out.println("Exiting Main thread.");
}
}

这是一个实现多线程的程序,运行结果如下:
Child thread : Thread[Demo thread,5,main]
Main thread :5
Child thread :5
Child thread :4
Main thread :4
Child thread :3
Child thread :2
Main thread :3
Child thread :1
Exiting child thread.
Main thread :2
Main thread :1
Exiting Main thread.

试想,如果把 start()改成run()会出现什么结果?
修改之后运行结果:
Child thread : Thread[Demo thread,5,main]
Child thread :5
Child thread :4
Child thread :3
Child thread :2
Child thread :1
Exiting child thread.
Main thread :5
Main thread :4
Main thread :3
Main thread :2
Main thread :1
Exiting Main thread.
程序运行成为了单线程顺序执行。为什么?
start方法:用来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体, 它包含了要执行的这个线程的内容, run方法运行结束, 此线程终止, 而CPU再运行其它线程,
直接用run方法: 这只是调用一个方法而已, 程序中依然只有主线程--这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的。
记住:线程就是为了更好地利用CPU,提高程序运行速率的!

posted @ 2011-11-08 20:53 云云 阅读(2796) | 评论 (0)编辑 收藏

在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口;Thread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的run()方法就可以实现多线程操作了,但是一个类只能继承一个父类,这是此方法的局限,下面看例子:
package org.thread.demo;
class MyThread extends Thread{
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run(){
for(int i=0;i<10;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}

package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
mt1.run();
mt2.run();
}
}
但是,此时结果很有规律,先第一个对象执行,然后第二个对象执行,并没有相互运行。在JDK的文档中可以发现,一旦调用start()方法,则会通过 JVM找到run()方法。下面启动
start()方法启动线程:
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
mt1.start();
mt2.start();
}
};

这样程序可以正常完成交互式运行。那么为啥非要使用start();方法启动多线程呢?
在JDK的安装路径下,src.zip是全部的java源程序,通过此代码找到Thread中的start()方法的定义,可以发现此方法中使用了 private native void start0();其中native关键字表示可以调用操作系统的底层函数,那么这样的技术成为JNI技术(java Native Interface)

Runnable接口
在实际开发中一个多线程的操作很少使用Thread类,而是通过Runnable接口完成。
public interface Runnable{
public void run();
}
例子:
package org.runnable.demo;
class MyThread implements Runnable{
private String name;
public MyThread(String name) {
this.name = name;
}
public void run(){
for(int i=0;i<100;i++){
System.out.println("线程开始:"+this.name+",i="+i);
}
}
};
但是在使用Runnable定义的子类中没有start()方法,只有Thread类中才有。此时观察Thread类,有一个构造方法:public Thread(Runnable targer)
此构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。(start()可以协调系统的资源):
package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1=new MyThread("线程a");
MyThread mt2=new MyThread("线程b");
new Thread(mt1).start();
new Thread(mt2).start();
}
}
两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下好处:
->避免点继承的局限,一个类可以继承多个接口。
->适合于资源的共享
以卖票程序为例,通过Thread类完成:
package org.demo.dff;
class MyThread extends Thread{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("賣票:ticket"+this.ticket--);
}
}
}
};
下面通过三个线程对象,同时卖票:
package org.demo.dff;
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1=new MyThread();
MyThread mt2=new MyThread();
MyThread mt3=new MyThread();
mt1.start();//每个线程都各卖了10张,共卖了30张票
mt2.start();//但实际只有10张票,每个线程都卖自己的票
mt3.start();//没有达到资源共享
}
}
如果用Runnable就可以实现资源共享,下面看例子:
package org.demo.runnable;
class MyThread implements Runnable{
private int ticket=10;
public void run(){
for(int i=0;i<20;i++){
if(this.ticket>0){
System.out.println("賣票:ticket"+this.ticket--);
}
}
}
}
package org.demo.runnable;
public class RunnableTicket {
public static void main(String[] args) {
MyThread mt=new MyThread();
new Thread(mt).start();//同一个mt,但是在Thread中就不可以,如果用同一
new Thread(mt).start();//个实例化对象mt,就会出现异常
new Thread(mt).start();
}
};
虽然现在程序中有三个线程,但是一共卖了10张票,也就是说使用Runnable实现多线程可以达到资源共享目的。

Runnable接口和Thread之间的联系:
public class Thread extends Object implements Runnable
发现Thread类也是Runnable接口的子类。

因为一个线程只能启动一次,通过Thread实现线程时,线程和线程所要执行的任务是捆绑在一起的。也就使得一个任务只能启动一个线程,不同的线程执行的任务是不相同的,所以没有必要,也不能让两个线程共享彼此任务中的资源。

一个任务可以启动多个线程,通过Runnable方式实现的线程,实际是开辟一个线程,将任务传递进去,由此线程执行。可以实例化多个 Thread对象,将同一任务传递进去,也就是一个任务可以启动多个线程来执行它。这些线程执行的是同一个任务,所以他们的资源是共享。

两种不同的线程实现方式本身就决定了其是否能进行资源共享。


posted @ 2011-11-08 20:46 云云 阅读(632) | 评论 (1)编辑 收藏

     摘要:  Spring AOP: Spring之面向方面编程 5.1. 概念 面向方面编程 (AOP) 提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。 面向对象将应用程序分解成 各个层次的对象,而AOP将程序分解成各个方面 或者说 关注点 。 这使得可以模块化诸如事务管理等这些横切多个对象的关注点。(这些关注点术语称作 横切关注点。) Spring的一...  阅读全文

posted @ 2011-11-08 20:35 云云 阅读(9754) | 评论 (0)编辑 收藏

     摘要: Java里有个很重要的特色是Exception ,也就是说允许程序产生例外状况。而在学Java 的时候,我们也只知道Exception 的写法,却未必真能了解不同种类的Exception 的区别。   首先,您应该知道的是Java 提供了两种Exception 的模式,一种是执行的时候所产生的Exception (Runtime Exception),另外一种则是受控制的Exception (...  阅读全文

posted @ 2011-11-07 22:49 云云 阅读(546) | 评论 (0)编辑 收藏

Comparable & Comparator 都是用来实现集合中的排序的,只是 Comparable 是在集合内部定义的方法实现的排序,
Comparator 是在集合外部实现的排序,所以,如想实现排序,就需要在集合外定义 Comparator 接口的方法或在集合内实现 Comparable 接口的方法。
Comparable 是一个对象本身就已经支持自比较所需要实现的接口(如 String、Integer 自己就可以完成比较大小操作)

而 Comparator 是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。

  可以说一个是自己完成比较,一个是外部程序实现比较的差别而已。

  用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。

  比如:你想对整数采用绝对值大小来排序,Integer 是不符合要求的,你不需要去修改 Integer 类(实际上你也不能这么做)去改变它的排序行为,只要使用一个实现了 Comparator 接口的对象来实现控制它的排序就行了。
Comparator 在java.util包中
Comparable 在java.lang包中

  

 1public class TestComparator {
 2    AsComparator cl=new AsComparator();
 3    /**
 4     * @param args
 5     */
  
 6    @SuppressWarnings("unchecked")
 7    public static void main(String[] args) {
 8         Integer[] datas=new Integer[20];
 9         Random rand=new Random();
10         for(int i=0;i<20;i++){
11             datas[i]=new Integer(rand.nextInt(100));
12         }

13         Arrays.sort(datas);
14         System.out.println(Arrays.asList(datas));
15         TestComparator test=new TestComparator();
16         Arrays.sort(datas,test.cl);
17         System.out.println(Arrays.asList(datas));
18         
19    }

20
21    @SuppressWarnings("rawtypes")
22    class AsComparator implements Comparator{
23
24        public int compare(Object o1, Object o2) {
25             int value1= Math.abs(((Integer)o1).intValue());
26             int value2=Math.abs(((Integer)o2).intValue());
27             return value1>value2?1:(value1==value2?0:-1);
28        }

29        
30    }

31     
32    
33}

34

 

posted @ 2011-11-07 11:19 云云 阅读(3131) | 评论 (0)编辑 收藏

仅列出标题
共12页: First 上一页 4 5 6 7 8 9 10 11 12 下一页