January 12, 2012
| 作者:白菜
|
分类:心情与文字
看见周边这么多人各种总结回顾,再一想,人的一生也不过900个月而已,转瞬即逝,而我也不能免俗,嘘叹毕业已经一年半矣,虚度了300多个月了,竟也过了人生的1/3了,换句话说,土已经埋到大腿了。
10年七月毕业至今,只回过一次家,家远在几千里外的西南,每当吃饭时,总怀恋家乡,这里的饭菜,要是在俺家,那都是喂猪的,缺油少盐,调料、佐料简陋的无语,让人甚是怀恋西南的精致和美味,西南有各种北方人没吃过的,没见过的美食。到了这里,都是奢望。
现在是冬天了,出租屋里没有空调,没有暖气,在宿舍手脚冰冷,晚上睡觉盖两床被子仍瑟瑟发抖。
不抱怨了,还是说点正事。
一、生活上
1.生活仍然简简单单,不过倒是恋爱了,找到了实实在在那个对自己好的人,在乎我的人,这就够了。虽此前有过种种分分合合,不过现在我们都很有信心坚持下去,说不定很快就谈婚论嫁了。
2.依然是周一到周六公司上班,周日上午睡觉,下午逛逛。不喜欢喧闹,平平淡淡就好。晚上呢,10年12月-11月6月期间,每天睡觉不超过4个小时,用了半年的时间实现了九阳神功第一重的修炼。这之后稍微收敛了点,细火慢熬修炼第二重。
3.吃的不是很好,北方伙食和南方不是一个档次,所以没胖,还是很瘦。
4.这一年半,累计消费55449人民币。
二、做过的事
1.真没做啥事,就是上班,吃饭,学习,睡觉。
2.技术上从草根php转型JAVA开发,很感谢公司给我机会,让我这个因为专业原因被四处鄙视的半路出家的毕业生有了份工作,而且公司气氛很好,开发流程很正规,让我养成了好的代码习惯。另外,技术上放弃python学习,专攻java,C,php,数据库等。也放弃了之前热爱的PS和FLASH制作,想当年,我就是靠着我PS的照片招摇撞骗的,嘿嘿。期间翻译过PY手册,不过已经放弃,写过一个php的学习指南,也大半年没更新了,还写过几个小小的软件,就此而已,也没啥大作为。偶尔骂骂人,然后被人骂。
三、看过的书
一堆了,《java编程思想》,《可爱的python》,《TCP/IP》,《算法导论》,《windows程序设计》《java虚拟机》,《深入理解MYSQL》,《C++ primer》,《大话数据结构》然后就是一堆的C,linux的书籍了,一年半重点看了10本书。没有看过小说,要看的话也是用手机看的,或者MP4看的电子书,不过电子书看的累,基本都没深入。
另外,《英语学习》这本杂志是必看的。
累计学习时间2000小时.
四、想做的事
1.活下去,继续生活,和她在一起;
2.给自己放一个月假,整整学了400多天了,没睡过安稳觉了。能晚上十点睡,早上八点起我就满足了;
3.继续啃我的二十本书,力求技术突飞猛进,修炼九阳神功第三重;
4.长胖5-10公斤。
January 9, 2012
| 作者:白菜
|
分类:JAVA
简介: Java™ 语言包含两种内在的同步机制:同步块(或方法)和 volatile 变量。这两种机制的提出都是为了实现代码线程的安全性。其中 Volatile 变量的同步性较差(但有时它更简单并且开销更低),而且其使用也更容易出错。在这期的Java 理论与实践中,Brian Goetz 将介绍几种正确使用 volatile 变量的模式,并针对其适用性限制提出一些建议。
Java 语言中的 volatile 变量可以被看作是一种 “程度较轻的 synchronized”;与 synchronized 块相比,volatile 变量所需的编码较少,并且运行时开销也较少,但是它所能实现的功能也仅是 synchronized 的一部分。本文介绍了几种有效使用 volatile 变量的模式,并强调了几种不适合使用 volatile 变量的情形。
锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的 —— 如果没有同步机制提供的这种可见性保证,线程看到的共享变量可能是修改前的值或不一致的值,这将引发许多严重问题。
Volatile 变量
Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。
出于简易性或可伸缩性的考虑,您可能倾向于使用 volatile 变量而不是锁。当使用 volatile 变量而非锁时,某些习惯用法(idiom)更加易于编码和阅读。此外,volatile 变量不会像锁那样造成线程阻塞,因此也很少造成可伸缩性问题。在某些情况下,如果读操作远远大于写操作,volatile 变量还可以提供优于锁的性能优势。
正确使用 volatile 变量的条件
您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中。
实际上,这些条件表明,可以被写入 volatile 变量的这些有效值独立于任何程序的状态,包括变量的当前状态。
第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x 的值在操作期间保持不变,而 volatile 变量无法实现这点。(然而,如果将值调整为只从单个线程写入,那么可以忽略第一个条件。)
大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized 那样普遍适用于实现线程安全。清单 1 显示了一个非线程安全的数值范围类。它包含了一个不变式 —— 下界总是小于或等于上界。
清单 1. 非线程安全的数值范围类@NotThreadSafe
public class NumberRange {
private int lower, upper;
public int getLower() { return lower; }
public int getUpper() { return upper; }
public void setLower(int value) {
if (value > upper)
throw new IllegalArgumentException(...);
lower = value;
}
public void setUpper(int value) {
if (value < lower)
throw new IllegalArgumentException(...);
upper = value;
}
}
阅读剩余部分...
January 9, 2012
| 作者:白菜
|
分类:JAVA
在Java Concurrency in Practice中是这样定义线程安全的:
当多个线程访问一个类时,如果不用考虑这些线程在运行时环境下的调度和交替运行,并且不需要额外的同步及在调用方代码不必做其他的协调,这个类的行为仍然是正确的,那么这个类就是线程安全的。
显然只有资源竞争时才会导致线程不安全,因此。
原子操作的描述是: 多个线程执行一个操作时,其中,那么这个操作就是原子的。
枯燥的定义介绍完了,下面说更枯燥的理论知识。
Java语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程通过叫做指令的重排序。指令重排序存在的意义在于:JVM能够根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特点,最大限度的发挥机器的性能。
程序执行最简单的模型是按照指令出现的顺序执行,这样就与执行指令的CPU无关,最大限度的保证了指令的可移植性。这个模型的专业术语叫做顺序化一致性模型。但是现代计算机体系和处理器架构都不保证这一点(因为人为的指定并不能总是保证符合CPU处理的特性)。
我们来看最经典的一个案例。
阅读剩余部分...
January 9, 2012
| 作者:白菜
|
分类:JAVA
刚用了一个数据库连接池,在公司电脑上测试时报错
无法解析类型 java.sql.Wrapper。从必需的 .class 文件间接引用了它
找了下原因,原来这个Wrapper类是JDK1.6 版本才提供的,公司的环境是JDK 1.5。。。。
百度下,发现类似的也很多,大多是JDK版本过低的问题,也有包引用错了导致的。
好吧,附上我的任意切换JDK版本的方法。
假设我现在安装的是旧版本的JDK1.5,那么在系统环境变量里我们设置了java_home和classpath,对应存放的注册表项是(HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Control/Session Manager/Environment),JDK在安装的过程当中将在注册表会生成如下3个项目:
HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Development Kit
HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Plug-in
HKEY_LOCAL_MACHINE/SOFTWARE/JavaSoft/Java Runtime Environment
同时,JDK安装程序将会把java.exe,javaw.exe,javaws.exe这3个可执行文件拷贝到%SystemRoot%\system32目录下,由于%SystemRoot%\system32被操作系统缺省的设置为最高优先权的PATH搜索路径,因此可保证用户在命令行任何目录下可运行java.exe来启动JVM。
所以,
第一步是备份上面提到的四个注册表项和那三个可执行文件;
第二步,安装高版本的JDK 1.7,修改环境变量,然后备份上面提到的四个注册表项和那三个可执行文件;
第三步:把第一步得到的所有文件放到一个命名“1.5”文件夹中,把第二步得到的所有文件放到一个命名“1.7”文件夹中;
第四步:在每个文件夹下写如下的脚本:
@echo off
echo 设置JDK 1.5……
dir
copy /y java.exe %SystemRoot%\system32
copy /y javaw.exe %SystemRoot%\system32
copy /y javaws.exe %SystemRoot%\system32
pause
目录结构如下
├─1.5
│ 1.5.bat
│ java.exe
│ javaw.exe
│ javaws.exe
│ jdk1.5.reg
│ jdk1.5_.reg
│
├─1.7
│ 1.7.bat
│ java.exe
│ javaw.exe
│ javaws.exe
│ jdk 1.7_.reg
│ jdk1.7.reg
1.7文件夹下的只需要改下提示就可以了。现在的JAVA环境已经是JDK1.7了,我们到1.5下执行“1.5.bat”,导入注册表,JAVA环境就降成1.5了,反之,到1.7文件夹下执行“1.7..bat”,导入注册表,环境就变成1.7了,可以随意切换。
如果用eclipse的话,只要装一个高版本的JDK即可,在项目属性上设置JRE兼容性,不用这么麻烦。但对于netbeans则比较麻烦,可以借鉴此文。
January 5, 2012
| 作者:白菜
|
分类:JAVA
注:转自http://www.unclejoey.com/?p=554,有部分改动
摘要
常见的服务器会将用户post的数据保存在hashmap中. 而向hashmap中插入n对元素的时间复杂度大约是O(n), 但如果精心构造key使得每个key的hash值相同(也就是产生了碰撞),则时间复杂度会恶化到O(n^2),导致消耗大量的CPU时间.在java中,字符串的hash函数采用DJBX33A,只不过常数因子改为了31. 这样的函数有个特点,即如果字符串X, Y的hash值相同,那么X,Y都添加任意相同的前缀或后缀的以后的hash值也都相同.比如: "rQ"与 "qp"的hash值相同, 则"rQrQ", "rQqp", "qprQ", "qpqp" 这四个也相同,继续这个模式就可以很容易构造出 2^n 个 2*n长度的不同字符串
PS: JDK中关于hashCode()方法的实现,可用以下公式表达:

代码
package com.unclejoey.just4fun;
import java.math.BigDecimal;
public class HashCollision {
private static final int i1 = 48;
private static final int i2 = 8;
private static final int i3 = 31;
private static final int i4 = 60000;
private static final long l1 = i3 -1;
private static final long l2 = 2l << 32;
private static final BigDecimal d1 = new BigDecimal(31);
private static final BigDecimal d2 = d1.pow(i2);
private static final BigDecimal d3 = new BigDecimal(l2);
public static void main(String[] args) {
String t = "test_string";
for(int i=0; i<=i4; i++) {
String s = String.valueOf(i);
while(s.length() < 5){
s = "0" + s;
}
int hs = s.hashCode();
char[] r = g(hs, t.hashCode());
s = s.concat(new String(r));
if (s.hashCode() != t.hashCode()) {
System.err.println("NO WAY, I Couldn't be wrong...");
System.exit(1);
}
System.out.println(s);
}
}
private static char[] g(int s, int t) {
long hx1 = l1 * s + i1;
BigDecimal hx2 = d2.multiply(new BigDecimal(hx1)).subtract(new BigDecimal(i1));
BigDecimal hx3 = hx2.divide(new BigDecimal(l1));
BigDecimal hx4 = new BigDecimal(t).subtract(hx3);
BigDecimal b = hx4.divideToIntegralValue(d3.multiply(d3));
long l = hx4.subtract(b).longValue();
l = (l+l2) % l2;
if (l < 0) l += l2;
char[] c = new char[i2];
int p = 0;
while (l != 0) {
c[p++] = (char) (l % (i3) + i1);
l = l / i3;
}
int f = i2 - p;
char[] cs = new char[i2];
int i = 0;
while (i < f) {
cs[i++] = (char) i1;
}
while (i < i2) {
cs[i] = c[p - i + f - 1];
++i;
}
return cs;
}
}
注:以上代码为原博文作者的代码,不保证运行效果。详细的文档看这里,
http://www.nruns.com/_downloads/advisory28122011.pdf 。如果是phper,看这里:
http://www.laruence.com/2011/12/29/2412.html
December 30, 2011
| 作者:白菜
|
分类:默认分类
近期网络流传的纷纷扬扬的用户密码泄露事件大部分来自于这个所谓的安全平台-乌云系统
http://www.wooyun.org/,一瞬间,此网站可谓是挣足了眼球和面子,赢得了不少流量和关注,可是细细一想,总觉得有点猫腻。
(1)此网站实为一个松散的虚拟组织,任何人都可以在上面发布漏洞,我也看到过认识的人的影子。上面还有PPC的会员的影子。试问,这么混乱,质量能得到保证不?只要提交漏洞就能得到邀请码,成为其会员,也就跻身于安全研究人员的行业。
(2)就从其近日发布的密码泄露来看,一次比一次不靠谱,什么支付宝密码泄露,银行密码泄露,一次比一次夸张,一次比一次规模庞大,动辄几千万,上亿,但是就从其给出的数据看,却经不起推敲。首先这些泄漏事件越来越让人不相信,其次已有相关企业出来辟谣,指出其泄露的银行卡号都是废卡。另外,从其泄露的数据来看,字段项也显得不够专业,更像是凭空臆想出来的。再者从我所了解到的大企业数据中心的情况来看,也不可能拿到这么多数据。很多企业的安全系统可谓是固若金汤,都是硬件级别的安全,经得起推敲的,是国际上引进的军方级别的安全系统,有这么容易破解?很多大公司的办公电脑都是硬件加密,没有U盘无法启动,系统一旦有被入侵和破坏倾向,立刻自动销毁数据。
(3)此网站惯用虚虚实实的招数,先弄一些比较小的不起眼的,容易得到的漏洞来垫底,然后就是五花八门的泄露,其泄露的数据也时虚虚实实,很多是比较老旧的数据再加上一堆捏造的数据。其所展示的数据和技术也不够专业。
最后,虽然不太清楚这个所谓的乌云系统的数据来自哪里(此网站官方已宣布关闭),是否是第一数据源,但是就其近期所发布的事件而言,是越来越让人不信任。在我眼里,更愿意把其看做网络黑社会,幕后黑手。根据目前所找到的资料,知其是80sec团队制作的网站,但是我对这个团队的性质表示不大信任。
December 27, 2011
| 作者:白菜
|
分类:编程算法
进程和线程这对概念的理解也是很难的,至今网络上可查的资料对其的理解出入都挺大,在不同的操作系统中,如linux和windows中,其概念和实现都是有出入的。因此,我在这里结合我自己的理解谈下这两个概念,讲的都是一般性的概念,并且主要是基以WINDOWS的。
一般将进程定义为一个正在运行的程序的实例。我们在任务管理器重所看到的每一项,就可以理解为一个进程,每个进程都有一个地址空间,这个地址空间里有可执行文件的代码和数据,以及线程堆栈等。一个程序至少有一个进程。进程可以创建子进程,创建的子进程可以和父进程一起工作,也可以独立运行。
而线程是隶属于进程的,也就是说,线程是不能单独存在的,线程存在于进程中。每个进程至少有一个主线程,进程里的线程就负责执行进程里的代码,这也叫做进程的“惰性”。线程所使用的资源是它所属的进程的资源。线程也有自己的资源,主要组成部分就是一些必要的计数器和线程栈,占用的资源很少。我们可以理解为进程就是个容器,而线程才是真正干活的。线程可以在内核空间实现,也可以在用户空间实现。
这里特别提一下linux,在linux中,每一个进程都必须有一个父进程(如果没有父进程,则把PID=1的根进程作为其父进程)。进程退出了,就成了僵尸进程,等待父进程的退出信号,如果父进程没有给他发信号,得给他找一个父进程,或者等待内核自动销毁。Linux内核只提供了轻量进程的支持,限制了更高效的线程模型的实现,但Linux着重优化了进程的调度开销,使得linux进程切换开销较小。所以linux系统没有真正意义上的线程机制。linux中,Linux线程是通过进程来实现,进程和线程是同一个层次的。(我这里提到的是基于linux 2.6 内核的,在最新的LINUX中,对线程的实现做了优化,但是还是基于进程的。有些linux实现了新的线程机制,但主流还是和我描述的一样)
进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这也是进程和线程的重要区别。
比如,windows中的explorer就是资源管理器进程,我们每打开一个窗口,这个进程就会创建一个线程。有上面的描述,我们可以知道,进程有独立的地址空间,多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。
在第一节中,我提到过,“并发!=多线程“,是的。如Unix操作系统和Windows操作系统支持多用户、多进程的并发执行,而Java这样的语言支持应用程序进程内部的多个执行线程的并发执行。我们知道,php在语法上是没有多线程这个概念的,那java/NET的多线程是怎么实现的。实际上,java只是通过native方法调用操作系统API来创建多线程而已。另外,我们经常说某个网站并发是多少多少,nginx是高并发服务器了。。这里的并发和我前面的并发概念是不同的。要想做到高并发,除了多线程等的实现,还需要各种好的IO机制,不然线程再多也没用,资源利用跟不上来。高并发又跟同步、异步等IO复用机制有了牵连。
下面列举一下进程和线程中的一些概念。
进程:创建,销毁,创建子进程(fork),优先级。
线程:创建,挂起,恢复,销毁,切换,协作,睡眠,唤醒,等待,同步,锁,优先级。
从上面的描述,基本可以理清进程和线程的关系了。最后总结下:
1.linux没有内核上的多线程实现,但是不能说linux没有多线程。WINDOWS有内核的多线程实现。
2.进程开销大,线程开销小,但是linux的进程和线程开销差异不明显。
3.并行的实现离不开多线程。
再留个悬念,多线程的实现和运行还会涉及到同步异步,原子性等概念。这些概念是相互交叉的,这里先不做深入。
December 27, 2011
| 作者:白菜
|
分类:编程算法
最近正想整理下这个专题,想了下,主要有以下几组概念,我们时不时会听到,但只要一细想就会觉得困惑,觉得没那么简单。这几组概念有:
1.并发和并行
2.线程与进程
3.缓冲与缓存
4.同步与异步
5.阻塞与非阻塞
6.原子性与事务性
这几组概念,不仅概念间容易混淆,而且一组概念和另一组概念之间又存在交叉,极容易搞混。
------------------------------------------------------------------------
下面,就我所了解的知识,分门别类的谈一下这几个概念间的关系,如有不当之处,还望指出和补充。
1.并发( concurrency)和并行( Parallelism)
狭义的并发和并行属于操作系统里的概念。并发和并行是两个即有相同处,又有区别的概念,相同的地方在于他们都是指同时处理多个任务。不同的地方在于并发是逻辑上的,并行是物理上真真实实的。
我们都清楚,在单个CPU的情况下,对于到达CPU执行单元的作业(作业就是待执行的进程和线程),都是需要排队的,因为我只有一个窗口,你要吃饭,就必须到我这个窗口来,也就是说CPU资源是有限的。假如说这个时候,某个作业一直占着CPU资源不放,那么其他作业就得不到执行,也就是被阻塞,被挂起了。这就需要一种机制,保证每个作业都有机会被执行到,这种机制就叫做处理器的调度机制,其中又分为抢占式和非抢占式的调度。细分后又有先来先服务(老老实实排队,谁先到就服务谁),最短作业优先(哪个好搞定就先服务谁),优先级调度,时间片调度等。并发就是通过时间片轮转的方式实现的,一个作业执行一段时间后被中断,换另外一个上去,大家都有机会被执行到。
时间片轮转,就涉及到一个进程切换的问题,频繁的切换必然带来性能开销。我们看到的一个程序一直在持续运行,但实际上并不是这样的,从CPU级别的粒度上讲,其实是多个程序间在快速的切换,只是这个切换的速度很快(想一下,CPU的主频现在都有2GHZ了),我们感觉不到而已。在早期的操作系统中,不支持多任务,一个任务在执行,其它任务就必须等待。
废话说了这么多,就是要说明,并发就是多个进程或线程在同时执行,但实际上,他们并不是真正的在同一个时间点上一起跑的,而是我跑一段时间后,接下来你跑,我再跑,看起来我们都在跑,这就是并发。
而并行,则是实实在在的多个进程同时跑在高速公路上,齐头并进。看下面的图,上面的是并发,下面的是并行。

通过我前面的描述,可以知道,并发是由CPU调度算法实现的,只需要一个CPU即可实现并发。而并行则要在多核或者多处理器情况下才能做到,单CPU是无法实现并行的,因为任一个时刻点上只有一个程序在处理器上运行。并行计算,这就是当今大型计算机所要实现的难题,如何让几千个CPU进行协作。并发和并行都需要处理器和OS的支持。
提到并发,就难免会提到多线程,并发!=多线程,多线程只是一种并发的实现模型。这里暂且留作后话。根据我们前面的描述,可以知道,时间片切换是有开销的,因此多线程不一定就能发挥出更佳的效率,特别是在单处理器的情况下。故,多线程也是不能盲目迷信的。