在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.
基于以下的原因:
一.代码的可读性和可维护性.
虽然用PreparedStatement来代替Statement会使代码多出几行,但这样的代码无论从可读性还是可维护性上来说.都比直接用Statement的代码高很多档次:
stmt.executeUpdate("insert into tb_name (col1,col2,col2,col4) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");
perstmt = con.prepareStatement("insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)");
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate();
不用我多说,对于第一种方法.别说其他人去读你的代码,就是你自己过一段时间再去读,都会觉得伤心.
二.PreparedStatement尽最大可能提高性能.
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个涵数)就会得到执行.这并不是说只有一个 Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.比如:
insert into tb_name (col1,col2) values ('11','22');
insert into tb_name (col1,col2) values ('11','23');
即使是相同操作但因为数据内容不一样,所以整个个语句本身不能匹配,没有缓存语句的意义.事实是没有数据库会对普通语句编译后的执行代码缓存.这样每执行一次都要对传入的语句编译一次.
当然并不是所以预编译语句都一定会被缓存,数据库本身会用一种策略,比如使用频度等因素来决定什么时候不再缓存已有的预编译结果.以保存有更多的空间存储新的预编译语句.
三.最重要的一点是极大地提高了安全性.
即使到目前为止,仍有一些人连基本的恶义SQL语法都不知道.
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?
select * from tb_name = '随意' and passwd = '' or '1' = '1';
因为'1'='1'肯定成立,所以可以任何通过验证.更有甚者:
把[';drop table tb_name;]作为varpasswd传入进来,则:
select * from tb_name = '随意' and passwd = '';drop table tb_name;有些数据库是不会让你成功的,但也有很多数据库就可以使这些语句得到执行.
而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement, 有可能要对drop,;等做费尽心机的判断和过虑.
上面的几个原因,还不足让你在任何时候都使用PreparedStatement吗?
有的新人可能此时对于用法还不太理解下面给个小例子
Code Fragment 1:
String updateString = "UPDATE COFFEES SET SALES = 75 " + "WHERE COF_NAME LIKE ′Colombian′";
stmt.executeUpdate(updateString);
Code Fragment 2:
PreparedStatement updateSales = con.prepareStatement("UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? ");
updateSales.setInt(1, 75);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
set中的1对应第一个? 2对应第二个? 同时注意你set 的类型 是int还是string 哈哈很简单吧
原文出处:http://blog.csdn.net/spcusa/archive/2009/05/09/4164076.aspx
//ValueObject类
public class AdEntity {
private String id;
private String name;
private String broker;
private String date;
private String body;
//get/set
public String toString(){
return "【编号为:"+id+",广告名称为:"+name+",代理商为:"+broker+",发布日期为:"+date+",内容为:"+body+"】";
}
}
//调用任务类
public class AdTask implements Callable<AdEntity> {
@Override
public AdEntity call() throws Exception {
// 模拟远程调用花费的一些时间
Thread.sleep(5*1000);
AdEntity vo=new AdEntity();
vo.setId(String.valueOf(new Random().nextInt(1000)));
vo.setName("Ad@内衣广告");
vo.setBroker("CHANNEL");
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String dateStr=sdf.format(date);
vo.setDate(dateStr);
vo.setBody("远端内容");
return vo;
}
}
//主函数
public class TestQueryMemg {
/**
* @param args
* @throws ExecutionException
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService exec=Executors.newCachedThreadPool();
//创建Future来完成网络调用任务
Callable<AdEntity> returnAd=new AdTask();
Future<AdEntity> future=exec.submit(returnAd);
//开始执行本地化查询信息
AdEntity localAd=new AdEntity();
localAd.setId(String.valueOf(new Random().nextInt(1000)));
localAd.setName("Ad@食品广告");
localAd.setBroker("蒙牛");
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");
String dateStr=sdf.format(new Date());
localAd.setDate(dateStr);
localAd.setBody("内容本地");
System.out.println("当前本地化查询内容为:"+localAd.toString());
System.out.println("稍等片刻以获取远端信息");
while(!future.isDone()){
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
AdEntity entityRemote=(AdEntity)future.get();
System.out.println("合并查询内容为:\n"+localAd.toString()+"\n"+entityRemote.toString());
}
}
个人账户类:
public class PrivateAccount implements Callable {
Integer total;
public Object call() throws Exception {
Thread.sleep(5*1000);
total=new Integer(new Random().nextInt(10000));
System.out.println("您个人账户上还有"+total+" 存款可以支配");
return total;
}
}
主函数测试:
public class SumTest {
/**
* @param args
* @throws ExecutionException
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable privateAccount=new PrivateAccount();
FutureTask task=new FutureTask(privateAccount);
//创建新线程获取个人账户信息
Thread thread=new Thread(task);
thread.start();
int total=new Random().nextInt(1000);
System.out.println("主线程在这工作");
System.out.println("您目前操作金额为: "+total+" .");
System.out.println("请等待计算个人账户的金额");
while(!task.isDone()){//判断是否已经获取返回值
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Integer privateSingle=(Integer)task.get();
int post=privateSingle.intValue();
System.out.println("您当前账户共有金额为:"+(total+post)+" ¥");
}
}
Memcached,人所皆知的remote distribute cache(不知道的可以javaeye一下下,或者google一下下,或者baidu一下下,但是鉴于baidu的排名商业味道太浓(从最近得某某事件可以看出),所以还是建议javaeye一下下),使用起来也非常的简单,它被用在了很多网站上面,几乎很少有大型的网站不会使用memcached。
曾经我也看过很多剖析memcached内部机制的文章,有一点收获,但是看过之后又忘记了,而且没有什么深刻的概念,但是最近我遇到一个问题,这个问题迫使我重新来认识memcache,下面我阐述一下我遇到的问题
问题:我有几千万的数据,这些数据会经常被用到,目前来看,它必须要放到memcached中,以保证访问速度,但是我的memcached中数据经常会有丢失,而业务需求是memcached中的数据是不能丢失的。我的数据丢失的时候,memcached server的内存才使用到60%,也就是还有40%内存被严重的浪费掉了。但不是所有的应用都是这样,其他应用内存浪费的就比较少。为什么内存才使用到60%的时候LRU就执行了呢(之所以确定是LRU执行是因为我发现我的数据丢失的总是前面放进去的,而且这个过程中,这些数据都没有被访问,比如第一次访问的时候,只能访问第1000w条,而第300w条或者之前的数据都已经丢失了,从日志里看,第300w条肯定是放进去了)。
带着这些疑问,我开始重新审视memcached这个产品,首先从它的内存模型开始:我们知道c++里分配内存有两种方式,预先分配和动态分配,显然,预先分配内存会使程序比较快,但是它的缺点是不能有效利用内存,而动态分配可以有效利用内存,但是会使程序运行效率下降,memcached的内存分配就是基于以上原理,显然为了获得更快的速度,有时候我们不得不以空间换时间。
也就是说memcached会预先分配内存,对了,memcached分配内存方式称之为allocator,首先,这里有3个概念:
1 slab
2 page
3 chunk
解释一下,一般来说一个memcahced进程会预先将自己划分为若干个slab,每个slab下又有若干个page,每个page下又有多个chunk,如果我们把这3个咚咚看作是object得话,这是两个一对多得关系。再一般来说,slab得数量是有限得,几个,十几个,或者几十个,这个跟进程配置得内存有关。而每个slab下得page默认情况是1m,也就是说如果一个slab占用100m得内存得话,那么默认情况下这个slab所拥有得page得个数就是100,而chunk就是我们得数据存放得最终地方。
举一个例子,我启动一个memcached进程,占用内存100m,再打开telnet,telnet localhost 11211,连接上memcache之后,输入stats slabs,回车,出现如下数据:
- STAT 1:chunk_size 80
- STAT 1:chunks_per_page 13107
- STAT 1:total_pages 1
- STAT 1:total_chunks 13107
- STAT 1:used_chunks 13107
- STAT 1:free_chunks 0
- STAT 1:free_chunks_end 13107
- STAT 2:chunk_size 100
- STAT 2:chunks_per_page 10485
- STAT 2:total_pages 1
- STAT 2:total_chunks 10485
- STAT 2:used_chunks 10485
- STAT 2:free_chunks 0
- STAT 2:free_chunks_end 10485
- STAT 3:chunk_size 128
- STAT 3:chunks_per_page 8192
- STAT 3:total_pages 1
- STAT 3:total_chunks 8192
- STAT 3:used_chunks 8192
- STAT 3:free_chunks 0
- STAT 3:free_chunks_end 8192
STAT 1:chunk_size 80
STAT 1:chunks_per_page 13107
STAT 1:total_pages 1
STAT 1:total_chunks 13107
STAT 1:used_chunks 13107
STAT 1:free_chunks 0
STAT 1:free_chunks_end 13107
STAT 2:chunk_size 100
STAT 2:chunks_per_page 10485
STAT 2:total_pages 1
STAT 2:total_chunks 10485
STAT 2:used_chunks 10485
STAT 2:free_chunks 0
STAT 2:free_chunks_end 10485
STAT 3:chunk_size 128
STAT 3:chunks_per_page 8192
STAT 3:total_pages 1
STAT 3:total_chunks 8192
STAT 3:used_chunks 8192
STAT 3:free_chunks 0
STAT 3:free_chunks_end 8192
以上就是前3个slab得详细信息
chunk_size表示数据存放块得大小,chunks_per_page表示一个内存页page中拥有得chunk得数量,total_pages表示每个slab下page得个数。total_chunks表示这个slab下chunk得总数(=total_pages * chunks_per_page),used_chunks表示该slab下已经使用得chunk得数量,free_chunks表示该slab下还可以使用得chunks数量。
从上面得示例slab 1一共有1m得内存空间,而且现在已经被用完了,slab2也有1m得内存空间,也被用完了,slab3得情况依然如此。 而且从这3个slab中chunk得size可以看出来,第一个chunk为80b,第二个是100b,第3个是128b,基本上后一个是前一个得1.25倍,但是这个增长情况我们是可以控制得,我们可以通过在启动时得进程参数 –f来修改这个值,比如说 –f 1.1表示这个增长因子为1.1,那么第一个slab中得chunk为80b得话,第二个slab中得chunk应该是80*1.1左右。
解释了这么多也该可以看出来我遇到得问题得原因了,如果还看不出来,那我再补充关键的一句:memcached中新的value过来存放的地址是该value的大小决定的,value总是会被选择存放到chunk与其最接近的一个slab中,比如上面的例子,如果我的value是80b,那么我这所有的value总是会被存放到1号slab中,而1号slab中的free_chunks已经是0了,怎么办呢,如果你在启动memcached的时候没有追加-M(禁止LRU,这种情况下内存不够时会out of memory),那么memcached会把这个slab中最近最少被使用的chunk中的数据清掉,然后放上最新的数据。这就解释了为什么我的内存还有40%的时候LRU就执行了,因为我的其他slab中的chunk_size都远大于我的value,所以我的value根本不会放到那几个slab中,而只会放到和我的value最接近的chunk所在的slab中(而这些slab早就满了,郁闷了)。这就导致了我的数据被不停的覆盖,后者覆盖前者。
问题找到了,解决方案还是没有找到,因为我的数据必须要求命中率时100%,我只能通过调整slab的增长因子和page的大小来尽量来使命中率接近100%,但是并不能100%保证命中率是100%(这话怎么读起来这么别扭呢,自我检讨一下自己的语文水平),如果您说,这种方案不行啊,因为我的memcached server不能停啊,不要紧还有另外一个方法,就是memcached-tool,执行move命令,如:move 3 1,代表把3号slab中的一个内存页移动到1号slab中,有人问了,这有什么用呢,比如说我的20号slab的利用率非常低,但是page却又很多,比如200,那么就是200m,而2好slab经常发生LRU,明显page不够,我就可以move 20 2,把20号slab的一个内存页移动到2号slab上,这样就能更加有效的利用内存了(有人说了,一次只移动一个page,多麻烦啊?ahuaxuan说,还是写个脚本,循环一下吧)。
有人说不行啊,我的memcache中的数据不能丢失啊,ok,试试新浪的memcachedb吧,虽然我没有用过,但是建议大家可以试试,它也使利用memcache协议和berkeleyDB做的(写到这里,我不得不佩服danga了,我觉得它最大的贡献不是memcache server本身,而是memcache协议),据说它被用在新浪的不少应用上,包括新浪的博客。
补充,stats slab命令可以查看memcached中slab的情况,而stats命令可以查看你的memcached的一些健康情况,比如说命中率之类的,示例如下:
- STAT pid 2232
- STAT uptime 1348
- STAT time 1218120955
- STAT version 1.2.1
- STAT pointer_size 32
- STAT curr_items 0
- STAT total_items 0
- STAT bytes 0
- STAT curr_connections 1
- STAT total_connections 3
- STAT connection_structures 2
- STAT cmd_get 0
- STAT cmd_set 0
- STAT get_hits 0
- STAT get_misses 0
- STAT bytes_read 26
- STAT bytes_written 16655
- STAT limit_maxbytes 104857600
STAT pid 2232
STAT uptime 1348
STAT time 1218120955
STAT version 1.2.1
STAT pointer_size 32
STAT curr_items 0
STAT total_items 0
STAT bytes 0
STAT curr_connections 1
STAT total_connections 3
STAT connection_structures 2
STAT cmd_get 0
STAT cmd_set 0
STAT get_hits 0
STAT get_misses 0
STAT bytes_read 26
STAT bytes_written 16655
STAT limit_maxbytes 104857600
从上面的数据可以看到这个memcached进程的命中率很好,get_misses低达0个,怎么回事啊,因为这个进程使我刚启动的,我只用telnet连了一下,所以curr_connections为1,而total_items为0,因为我没有放数据进去,get_hits为0,因为我没有调用get方法,最后的结果就是misses当然为0,哇哦,换句话说命中率就是100%,又yy了。
该到总结的时候了,从这篇文章里我们可以得到以下几个结论:
结论一,memcached得LRU不是全局的,而是针对slab的,可以说是区域性的。
结论二,要提高memcached的命中率,预估我们的value大小并且适当的调整内存页大小和增长因子是必须的。
结论三,带着问题找答案理解的要比随便看看的效果好得多。
- 关闭所有oracle的服务和程序
- 选择开始| 程序|oracle Installation Products命令,运行Universal Installer,弹出欢迎对话框
- 单机 卸载产品 按钮,弹出Inventory对话框
- 选中Inventroy对话框中的所有节点,点击删除,确认即可
- 选 择 开始|运行 输入regedit并按ENTER键,选择HKEY_LOCAL_MACHINE\SOFTWARE\ORACLE,删除此象,然后选择 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services,滚动此列表,删除与oracle有关的所 有选项。
- 从桌面上、STARTUP和程序菜单中删除所有ORACLE的组和图标。
- 重启系统。
- 删除包括文件在内的安装目录。至此ORACLE的全部卸载完成。
位运算应用口诀
清零取位要用与,某位置一可用或
若要取反和交换,轻轻松松用异或
移位运算
要点 1 它们都是双目运算符,两个运算分量都是整形,结果也是整形。
2 "<<" 左移:右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2。
3 ">>"右移:右边的位被挤掉。对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或补1,这取决于所用的计算机系统。
4 ">>>"运算符,右边的位被挤掉,对于左边移出的空位一概补上0。
位运算符的应用 (源操作数s 掩码mask)
(1) 按位与-- &
1 清零特定位 (mask中特定位置0,其它位为1,s=s&mask)
2 取某数中指定位 (mask中特定位置1,其它位为0,s=s&mask)
(2) 按位或-- |
常用来将源操作数某些位置1,其它位不变。 (mask中特定位置1,其它位为0 s=s|mask)
(3) 位异或-- ^
1 使特定位的值取反 (mask中特定位置1,其它位为0 s=s^mask)
2 不引入第三变量,交换两个变量的值 (设 a=a1,b=b1)
目 标 操 作 操作后状态
a=a1^b1 a=a^b a=a1^b1,b=b1
b=a1^b1^b1 b=a^b a=a1^b1,b=a1
a=b1^a1^a1 a=a^b a=b1,b=a1
二进制补码运算公式:
-x = ~x + 1 = ~(x-1)
~x = -x-1
-(~x) = x+1
~(-x) = x-1
x+y = x - ~y - 1 = (x|y)+(x&y)
x-y = x + ~y + 1 = (x|~y)-(~x&y)
x^y = (x|y)-(x&y)
x|y = (x&~y)+y
x&y = (~x|y)-~x
x==y: ~(x-y|y-x)
x!=y: x-y|y-x
x< y: (x-y)^((x^y)&((x-y)^x))
x<=y: (x|~y)&((x^y)|~(y-x))
x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较
x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较
应用举例
(1) 判断int型变量a是奇数还是偶数
a&1 = 0 偶数
a&1 = 1 奇数
(2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k&1
(3) 将int型变量a的第k位清0,即a=a&~(1<<k)
(4) 将int型变量a的第k位置1, 即a=a|(1<<k)
(5) int型变量循环左移k次,即a=a<<k|a>>16-k (设sizeof(int)=16)
(6) int型变量a循环右移k次,即a=a>>k|a<<16-k (设sizeof(int)=16)
(7)整数的平均值
对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:
int average(int x, int y) //返回X,Y 的平均值
{
return (x&y)+((x^y)>>1);
}
(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂
boolean power2(int x)
{
return ((x&(x-1))==0)&&(x!=0);
}
(9)不用temp交换两个整数
void swap(int x , int y)
{
x ^= y;
y ^= x;
x ^= y;
}
(10)计算绝对值
int abs( int x )
{
int y ;
y = x >> 31 ;
return (x^y)-y ; //or: (x+y)^y
}
(11)取模运算转化成位运算 (在不产生溢出的情况下)
a % (2^n) 等价于 a & (2^n - 1)
(12)乘法运算转化成位运算 (在不产生溢出的情况下)
a * (2^n) 等价于 a<< n
(13)除法运算转化成位运算 (在不产生溢出的情况下)
a / (2^n) 等价于 a>> n
例: 12/8 == 12>>3
(14) a % 2 等价于 a & 1
(15) if (x == a) x= b;
else x= a;
等价于 x= a ^ b ^ x;
(16) x 的 相反数 表示为 (~x+1)
posted @
2010-03-30 13:59 叶澍成 阅读(396) |
评论 (0) |
编辑 收藏
线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始。运行run方法体中的那一段相对独立的过程。
线程的并发与并行
在过去的电脑都已单CPU作为主要的处理方式,无论是PC或者是服务器都是如此。系统调用某一个时刻只能有一个线程运行。当然这当中采用了比较多的策略来做时间片轮询。通过不断的调度切换来运行线程运行,而这种方式就叫做并发(concurrent)。
随着工艺水平的逐渐提升,CPU的技术也在不断增进。因此在如今多个CPU已经不是什么特别的,而大家常常以SMP的方式来形容多个CPU来处理两个或者两个以上的线程运行方式就称为并行(parallel)。
JAVA线程对象
继承Thread,实现start()方法
要实现线程运行,JAVA中有两种方式:
实现Runnable,然后再传递给Thread实例
注意:线程对象和线程是两个截然不同的概念。
线程对象是JVM产生的一个普通的Object子类
线程是CPU分配给这个对象的一个运行过程
public class Test {
public static void main(String[] args) throws Exception{
MyThread mt = new MyThread();
mt.start();
mt.join();
Thread.sleep(3000);
mt.start();
}
}
当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程.结果我们看到:
Exception in thread "main" java.lang.IllegalThreadStateException
根本原因是在以下源代码中找出:
public synchronized void start() {
if (started)
throw new IllegalThreadStateException();
started = true;
group.add(this);
start0();
}
一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:
【通过Thread实例的start(),一个Thread的实例只能产生一个线程】
interrupt()方法
当一个线程对象调用interrupt()方法,它对应的线程并没有被中断,只是改变了它的中断状态。使当前线程的状态变以中断状态,如果没有其它影响,线程还会自己继续执行。只有当线程执行到sleep,wait,join等方法时,或者自己检查中断状态而抛出异常的情况下,线程才会抛出异常。
join()方法
join()方法,正如第一节所言,在一个线程对象上调用join方法,是当前线程等待这个线程对象对应的线程结束
例如:有两个工作,工作A要耗时10秒钟,工作B要耗时10秒或更多。我们在程序中先生成一个线程去做工作B,然后做工作A。
new B().start();//做工作B
A();//做工作A
工作A完成后,下面要等待工作B的结果来进行处理。如果工作B还没有完成我就不能进行下面的工作C,所以:
B b = new B();
b.start();//做工作B
A();//做工作A
b.join();//等工作B完成.
C();//继续工作C
原则:【join是测试其它工作状态的唯一正确方法】
yield()方法
yield()方法也是类方法,只在当前线程上调用,理由同上,它主是让当前线程放弃本次分配到的时间片,调用这个方法不会提高任何效率,只是降低了CPU的总周期上面介绍的线程一些方法,基于(基础篇)而言只能简单提及。以后具体应用中我会结合实例详细论述。
原则:【不是非常必要的情况下,没有理由调用它】
wait()、notify()/notityAll()方法
首先明确一点他们的属于普通对象方法,并非是线程对象方法;其次它们只能在同步方法中调用。线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又立即释放该锁。
线程的互斥控制
多个线程同时操作某一对象时,一个线程对该对象的操作可能会改变其状态,而该状态会影响另一线程对该对象的真正结果。
synchornized关键字
把一个单元声明为synchornized,就可以让在同一时间只有一个线程操作该方法。作为记忆可以把synchronized看作是一个锁。但是我们要理解锁是被动的,还是主动的呢?换而言之它到底锁什么了?锁谁了?
例如:
synchronized(obj){
//todo…
}
如果代码运行到此处,synchronized首先获取obj参数对象的锁,若没有获取线程只能等待,如果多个线程运行到这只能有一个线程获取obj的锁,然后再执行{}中的代码。因此obj作用范围不同,控制程序也不同。
如果一个方法声明为synchornized的,则等同于把在为个方法上调用synchornized(this)。
如果一个静态方法被声明为synchornized,则等同于把在为个方法上调用synchornized(类.class)
真正的停止线程
要让一个线程得到真正意义的停止,需要了解当前的线程在干什么,如果线程当前没有做什么,那立刻让对方退出,当然是没有任何问题,但是如果对方正在手头赶工,那就必须让他停止,然后收拾残局。因此,首先需要了解步骤:
1. 正常运行;
2. 处理结束前的工作,也就是准备结束;
3. 结束退出。
注:以上部分概括出自某位牛人大哥的笔记,经常拜读他的博客
posted @
2009-07-20 10:00 叶澍成 阅读(371) |
评论 (0) |
编辑 收藏
对于这本书的阅读,说来也很惭愧。过去黎敏基本对于他翻译过的书,都有邮寄送给我,书拿到手后,都是内心的高兴——哇,不用花钱的书,爽!(自己YY下)唯一要求就是能够读后能写个读后感,但是很多次都抱歉的忽悠了他。尽管如此他这次翻译的书还是一如既往的邮寄给我(当然也是在我厚脸皮的去索要的前提下,哈哈),同样答应写一篇读后感,可是迟到的读后感一直到现在才肯出炉,确实有点对不住他。这本书是在我每天早早挤车一个星期多读出来的,那个汽车真叫热啊,嘿嘿。切入正题,谈谈对于本书的一个看法和意见。
这本书整体风格基本还是沿袭了第一版本的套路,把每个重点都写成一个条目来针对性的阐述,本书总共条目为七十八条(第一版书共有五十七条)。当中很多是在JDK5基础上作为第一版的更新(与第一版比较明显特征是把原有第一版的第五章,第六章的结构化特征和方法换成JDK5的新特性:泛型,枚举,方法)。
这本书我到现在都怀疑出版社没有花很多时间在排版上,为什么这么说?在黎敏在翻译阶段也就有拿书的样稿给我看过,我第一看到就是封面问题——居然是黄色的,当时就跟他提出来,这个需要他和出版社去很好的斟酌一番。当时黎敏也说已经跟出版社商量过,哪知道最后拿到手上的居然还是“黄色”。可见出版社不知道是出于什么来考虑,让我来猜想下:难道是怕大伙都是色盲非要用黄色来提醒大家吗?更为失望的居然标题使用红色,晕啊!其次就要说书纸张也未免太扯淡了吧?我第一感觉纸张就是草稿纸一般,实在无语。在代码处理方面,显得不如第一版的那种爽朗,是不是出版社考虑节省纸张啊?非要把很多代码的间隔弄的特小,这样对于可读性来说确实很有疲劳感。所以这里向提醒以后的出版社——你忽悠是可以,但是别把读者当傻瓜。
上面是谈到在书的编排和效果的问题。现在谈谈书中内容的一些感受。整体上说书翻译还是可以,不过当中也不乏一些乏味用词过当的问题,这里要说到最明显的就是书中出现很多在每一个条目后的总结词汇都或多或少带有“本条目”一词,个人觉得偶尔写写是没很大问题,但是过多重复显得机械化的审美疲劳,甚至有时候过多这样的词汇对于阅读流畅性稍微欠佳。像这样类似的词汇还有——“不严格地讲”,“简而言之”,这些词汇确实稍微影响阅读感。这里小纠下不爽或者错误的位置:P240处第二段第三行和第四行中出现两个“现在”,这个可能在校正时没有很好去润色。P216倒数第二段第三行有一个估计是印刷错误:【原】仅仅一个异常就会导致该方法不得不外于… 如果没有错的话,这个字应该是:处。
还有类似第二章谈到的:静态工厂方法与构造器不同的第三的优势在于,它们可以返回原返回类型的任何子类型的对象。就这句话,老实讲我真看了很久才明白啥意思。
以上是谈到一些不足的细微之处,不过阅读此书后,确实对于以前一些不起眼的所谓了解语法也很好的得到一次重新的认识。比如以前在使用非检测警告上,以前很习惯的直接在整个类上直接使用标记表示;对于了解for-each的循环上得到进一步的认识;对于枚举类型的使用上更加灵活。这些都是个人对于此书泛读之后的一些浅薄的看法。如果对于译者有不敬之处还望原谅!
posted @
2009-07-17 18:09 叶澍成 阅读(298) |
评论 (0) |
编辑 收藏