<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Iciness's space...]]></title> 
<link>http://www.newtyper.com/blog/index.php</link> 
<description><![CDATA[-心静止水-]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Iciness's space...]]></copyright>
<item>
<link>http://www.newtyper.com/blog/post/177/</link>
<title><![CDATA[My friends cat]]></title> 
<author>Fphoto &lt;iciness@ymail.com&gt;</author>
<category><![CDATA[缤纷世界]]></category>
<pubDate>Fri, 16 Apr 2010 08:48:02 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/177/</guid> 
<description>
<![CDATA[ 
	<div style="text-align: left; padding: 3px;"><a href="http://www.flickr.com/photos/newtyper/4524858411/" title="photo sharing"><img src="http://farm5.static.flickr.com/4066/4524858411_99f5ea383c.jpg" style="border: solid 2px #000000;" alt="" /></a><br /><span style="font-size: 0.8em; margin-top: 0px;"><a href="http://www.flickr.com/photos/newtyper/4524858411/">My friend's cat</a>，原由 <a href="http://www.flickr.com/people/newtyper/">Benny Feng</a> 上載。</span></div><p>=。=</p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/176/</link>
<title><![CDATA[更换了空间，先买了2年的IX空间]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Sun, 10 Jan 2010 16:41:26 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/176/</guid> 
<description>
<![CDATA[ 
	<img src="images/emot/cry.gif" border="0" />摊手，有些东西都是被迫的<br />先购买2年，不过现在我也很少写blog，其实以前就很少，感觉有些东西还是需要坚持<br />以前有些图片链接由于大家都知道原因看不到了，以后我慢慢再搬家吧，flickr但愿不要再被咔嚓了<br />欢迎访问我的flickr&nbsp; <a href="http://www.flickr.com/photos/newtyper/" target="_blank">http://www.flickr.com/phot...</a><br /><br /><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E7%25A9%25BA%25E9%2597%25B4/" rel="tag">空间</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%2590%25AC%25E5%25AE%25B6/" rel="tag">搬家</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/175/</link>
<title><![CDATA[Java多线程初学者指南]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Mon, 06 Jul 2009 10:21:34 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/175/</guid> 
<description>
<![CDATA[ 
	<p>转载至blogjava的<a href="http://www.blogjava.net/nokiaguy/" target="_blank">真的有外星人吗？</a></p>&nbsp;&nbsp;<p><strong>第一节 线程简介</strong></p>&nbsp;&nbsp;<p><strong>一、线程概述</strong></p>&nbsp;&nbsp;<p>线程是程序运行的基本执行单元。当操作系统(不包括单线程的操作系统，如微软早期的DOS)在执行一个程序时，会在系统中建立一个进程，而在这个进程中，必须至少建立一个线程（这个线程被称为主线程）来作为这个程序运行的入口点。因此，在操作系统中运行的任何程序都至少有一个主线程。</p>&nbsp;&nbsp;<p>进程和线程是现代操作系统中两个必不可少的运行模型。在操作系统中可以有多个进程，这些进程包括系统进程（由操作系统内部建立的进程）和用户进程（由用户程序建立的进程）；一个进程中可以有一个或多个线程。进程和进程之间不共享内存，也就是说系统中的进程是在各自独立的内存空间中运行的。而一个进程中的线可以共享系统分派给这个进程的内存空间。</p>&nbsp;&nbsp;<p>线程不仅可以共享进程的内存，而且还拥有一个属于自己的内存空间，这段内存空间也叫做线程栈, 是在建立线程时由系统分配的，主要用来保存线程内部所使用的数据，如线程执行函数中所定义的变量。</p>&nbsp;&nbsp;<p><strong>注意：</strong>任何一个线程在建立时都会执行一个函数，这个函数叫做线程执行函数。也可以将这个函数看做线程的入口点（类似于程序中的main函数）。无论使用什么语言或技术来建立线程，都必须执行这个函数（这个函数的表现形式可能不一样，但都会有一个这样的函数）。如在Windows中用于建立线程的API函数CreateThread的第三个参数就是这个执行函数的指针。</p>&nbsp;&nbsp;<p>在操作系统将进程分成多个线程后，这些线程可以在操作系统的管理下并发执行，从而大大提高了程序的运行效率。虽然线程的执行从宏观上看是多个线程同时执行，但实际上这只是操作系统的障眼法。由于一块CPU同时只能执行一条指令，因此，在拥有一块CPU的计算机上不可能同时执行两个任务。而操作系统为了能提高程序的运行效率，在一个线程空闲时会撤下这个线程，并且会让其他的线程来执行，这种方式叫做线程调度。我们之所以从表面上看是多个线程同时执行，是因为不同线程之间切换的时间非常短，而且在一般情况下切换非常频繁。假设我们有线程A和B。在运行时，可能是A执行了1毫秒后，切换到B后，B又执行了1毫秒，然后又切换到了A，A又执行1毫秒。由于1毫秒的时间对于普通人来说是很难感知的，因此，从表面看上去就象A和B同时执行一样，但实际上A和B是交替执行的。</p>&nbsp;&nbsp;<p><strong>二、线程给我们带来的好处</strong></p>&nbsp;&nbsp;<p>如果能合理地使用线程，将会减少开发和维护成本，甚至可以改善复杂应用程序的性能。如在GUI应用程序中，还以通过线程的异步特性来更好地处理事件；在应用服务器程序中可以通过建立多个线程来处理客户端的请求。线程甚至还可以简化虚拟机的实现，如Java虚拟机(JVM)的垃圾回收器（garbage collector）通常运行在一个或多个线程中。因此，使用线程将会从以下五个方面来改善我们的应用程序：</p>&nbsp;&nbsp;<p><strong>1. </strong><strong>充分利用</strong><strong>CPU</strong><strong>资源</strong></p>&nbsp;&nbsp;<p>现在世界上大多数计算机只有一块CPU。因此，充分利用CPU资源显得尤为重要。当执行单线程程序时，由于在程序发生阻塞时CPU可能会处于空闲状态。这将造成大量的计算资源的浪费。而在程序中使用多线程可以在某一个线程处于休眠或阻塞时，而CPU又恰好处于空闲状态时来运行其他的线程。这样CPU就很难有空闲的时候。因此，CPU资源就得到了充分地利用。</p>&nbsp;&nbsp;<p><strong>2. </strong><strong>简化编程模型</strong></p>&nbsp;&nbsp;<p>如果程序只完成一项任务，那只要写一个单线程的程序，并且按着执行这个任务的步骤编写代码即可。但要完成多项任务，如果还使用单线程的话，那就得在在程序中判断每项任务是否应该执行以及什么时候执行。如显示一个时钟的时、分、秒三个指针。使用单线程就得在循环中逐一判断这三个指针的转动时间和角度。如果使用三个线程分另来处理这三个指针的显示，那么对于每个线程来说就是指行一个单独的任务。这样有助于开发人员对程序的理解和维护。</p>&nbsp;&nbsp;<p><strong>3. </strong><strong>简化异步事件的处理</strong></p>&nbsp;&nbsp;<p>当一个服务器应用程序在接收不同的客户端连接时最简单地处理方法就是为每一个客户端连接建立一个线程。然后监听线程仍然负责监听来自客户端的请求。如果这种应用程序采用单线程来处理，当监听线程接收到一个客户端请求后，开始读取客户端发来的数据，在读完数据后，read方法处于阻塞状态，也就是说，这个线程将无法再监听客户端请求了。而要想在单线程中处理多个客户端请求，就必须使用非阻塞的Socket连接和异步I/O。但使用异步I/O方式比使用同步I/O更难以控制，也更容易出错。因此，使用多线程和同步I/O可以更容易地处理类似于多请求的异步事件。</p>&nbsp;&nbsp;<p><strong>4. </strong><strong>使</strong><strong>GUI</strong><strong>更有效率</strong></p>&nbsp;&nbsp;<p>使用单线程来处理GUI事件时，必须使用循环来对随时可能发生的GUI事件进行扫描，在循环内部除了扫描GUI事件外，还得来执行其他的程序代码。如果这些代码太长，那么GUI事件就会被&ldquo;冻结&rdquo;，直到这些代码被执行完为止。</p>&nbsp;&nbsp;<p>在现代的GUI框架（如SWING、AWT和SWT）中都使用了一个单独的事件分派线程（event dispatch thread，EDT）来对GUI事件进行扫描。当我们按下一个按钮时，按钮的单击事件函数会在这个事件分派线程中被调用。由于EDT的任务只是对GUI事件进行扫描，因此，这种方式对事件的反映是非常快的。</p>&nbsp;&nbsp;<p><strong>5. </strong><strong>节约成本</strong></p>&nbsp;&nbsp;<p>提高程序的执行效率一般有三种方法：</p>&nbsp;&nbsp;<p>（1）增加计算机的CPU个数。</p>&nbsp;&nbsp;<p>（2）为一个程序启动多个进程</p>&nbsp;&nbsp;<p>（3）在程序中使用多进程。</p>&nbsp;&nbsp;<p>第一种方法是最容易做到的，但同时也是最昂贵的。这种方法不需要修改程序，从理论上说，任何程序都可以使用这种方法来提高执行效率。第二种方法虽然不用购买新的硬件，但这种方式不容易共享数据，如果这个程序要完成的任务需要必须要共享数据的话，这种方式就不太方便，而且启动多个线程会消耗大量的系统资源。第三种方法恰好弥补了第一种方法的缺点，而又继承了它们的优点。也就是说，既不需要购买CPU，也不会因为启太多的线程而占用大量的系统资源（在默认情况下，一个线程所占的内存空间要远比一个进程所占的内存空间小得多），并且多线程可以模拟多块CPU的运行方式，因此，使用多线程是提高程序执行效率的最廉价的方式。</p>&nbsp;&nbsp;<p><strong>三、</strong><strong>Java</strong><strong>的线程模型</strong></p>&nbsp;&nbsp;<p>由于Java是纯面向对象语言，因此，Java的线程模型也是面向对象的。Java通过Thread类将线程所必须的功能都封装了起来。要想建立一个线程，必须要有一个线程执行函数，这个线程执行函数对应Thread类的run方法。Thread类还有一个start方法，这个方法负责建立线程，相当于调用Windows的建立线程函数CreateThread。当调用start方法后，如果线程建立成功，并自动调用Thread类的run方法。因此，任何继承Thread的Java类都可以通过Thread类的start方法来建立线程。如果想运行自己的线程执行函数，那就要覆盖Thread类的run方法。</p>&nbsp;&nbsp;<p>在Java的线程模型中除了Thread类，还有一个标识某个Java类是否可作为线程类的接口Runnable，这个接口只有一个抽象方法run，也就是Java线程模型的线程执行函数。因此，一个线程类的唯一标准就是这个类是否实现了Runnable接口的run方法，也就是说，拥有线程执行函数的类就是线程类。</p>&nbsp;&nbsp;<p>从上面可以看出，在Java中建立线程有两种方法，一种是继承Thread类，另一种是实现Runnable接口，并通过Thread和实现Runnable的类来建立线程，其实这两种方法从本质上说是一种方法，即都是通过Thread类来建立线程，并运行run方法的。但它们的大区别是通过继承Thread类来建立线程，虽然在实现起来更容易，但由于Java不支持多继承，因此，这个线程类如果继承了Thread，就不能再继承其他的类了，因此，Java线程模型提供了通过实现Runnable接口的方法来建立线程，这样线程类可以在必要的时候继承和业务有关的类，而不是Thread类。</p>&nbsp;&nbsp;<p><strong>第二节 用Thread类创建线程</strong></p>&nbsp;&nbsp;<p>在Java中创建线程有两种方法：使用Thread类和使用Runnable接口。在使用Runnable接口时需要建立一个Thread实例。因此，无论是通过Thread类还是Runnable接口建立线程，都必须建立Thread类或它的子类的实例。Thread类的构造方法被重载了八次，构造方法如下：</p>&nbsp;&nbsp;<p>public Thread( );&nbsp;&nbsp;&nbsp;&nbsp;<br />public Thread(Runnable target);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(Runnable target, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target, String name);&nbsp;&nbsp;&nbsp;&nbsp; <br />public Thread(ThreadGroup group, Runnable target, String name, long stackSize);</p>&nbsp;&nbsp;<p><strong>Runnable target</strong></p>&nbsp;&nbsp;<p>实现了Runnable接口的类的实例。要注意的是Thread类也实现了Runnable接口，因此，从Thread类继承的类的实例也可以作为target传入这个构造方法。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>String name</strong></p>&nbsp;&nbsp;<p>线程的名子。这个名子可以在建立Thread实例后通过Thread类的setName方法设置。如果不设置线程的名子，线程就使用默认的线程名：Thread-N，N是线程建立的顺序，是一个不重复的正整数。</p>&nbsp;&nbsp;<p><strong>ThreadGroup group</strong></p>&nbsp;&nbsp;<p>当前建立的线程所属的线程组。如果不指定线程组，所有的线程都被加到一个默认的线程组中。关于线程组的细节将在后面的章节详细讨论。</p>&nbsp;&nbsp;<p><strong>long stackSize</strong></p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 线程栈的大小，这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下，默认的线程栈大小是12KB。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>一个普通的Java类只要从Thread类继承，就可以成为一个线程类。并可通过Thread类的start方法来执行线程代码。虽然Thread类的子类可以直接实例化，但在子类中必须要覆盖Thread类的run方法才能真正运行线程的代码。下面的代码给出了一个使用Thread类建立线程的例子：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class Thread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(this.getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />009 public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread1 thread1 = new Thread1();&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread1 thread2 = new Thread1 ();&nbsp;&nbsp;&nbsp;&nbsp; <br />014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017&nbsp; &#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 上面的代码建立了两个线程：thread1和thread2。上述代码中的005至008行是Thread1类的run方法。当在014和015行调用 start方法时，系统会自动调用run方法。在007行使用this.getName()输出了当前线程的名字，由于在建立线程时并未指定线程名，因此，所输出的线程名是系统的默认值，也就是Thread-n的形式。在011行输出了主线程的线程名。&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>main&nbsp;&nbsp;&nbsp;&nbsp;<br />Thread-0&nbsp;&nbsp;&nbsp;&nbsp; <br />Thread-1</p>&nbsp;&nbsp;<p>从上面的输出结果可以看出，第一行输出的main是主线程的名子。后面的Thread-1和Thread-2分别是thread1和thread2的输出结果。</p>&nbsp;&nbsp;<p><strong>注意：</strong>任何一个Java程序都必须有一个主线程。一般这个主线程的名子为main。只有在程序中建立另外的线程，才能算是真正的多线程程序。也就是说，多线程程序必须拥有一个以上的线程。&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; Thread类有一个重载构造方法可以设置线程名。除了使用构造方法在建立线程时设置线程名，还可以使用Thread类的setName方法修改线程名。要想通过Thread类的构造方法来设置线程名，必须在Thread的子类中使用Thread类的public Thread(String name)构造方法，因此，必须在Thread的子类中也添加一个用于传入线程名的构造方法。下面的代码给出了一个设置线程名的例子：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class Thread2 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 private String who;&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp; <br />007 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(who + &quot;:&quot; + this.getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />011 public Thread2(String who)&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />013 super();&nbsp;&nbsp;&nbsp;&nbsp; <br />014 this.who = who;&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016 public Thread2(String who, String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />017&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />018 super(name);&nbsp;&nbsp;&nbsp;&nbsp; <br />019 this.who = who;&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021 public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread1 = new Thread2 (&quot;thread1&quot;, &quot;MyThread1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread2 = new Thread2 (&quot;thread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />025&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread2 thread3 = new Thread2 (&quot;thread3&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.setName(&quot;MyThread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />027&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />029&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread3.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />031</p>&nbsp;&nbsp;<p>在类中有两个构造方法：</p>&nbsp;&nbsp;<p><strong>第</strong><strong>011</strong><strong>行：</strong><strong>public sample2_2(String who)</strong></p>&nbsp;&nbsp;<p>这个构造方法有一个参数：who。这个参数用来标识当前建立的线程。在这个构造方法中仍然调用Thread的默认构造方法public Thread( )。</p>&nbsp;&nbsp;<p><strong>第</strong><strong>016</strong><strong>行：</strong><strong>public sample2_2(String who, String name)</strong></p>&nbsp;&nbsp;<p>这个构造方法中的who和第一个构造方法的who的含义一样，而name参数就是线程的名名。在这个构造方法中调用了Thread类的public Thread(String name)构造方法，也就是第018行的super(name)。</p>&nbsp;&nbsp;<p>在main方法中建立了三个线程：thread1、thread2和thread3。其中thread1通过构造方法来设置线程名，thread2通过setName方法来修改线程名，thread3未设置线程名。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 运行结果如下：</p>&nbsp;&nbsp;<p>thread1:MyThread1&nbsp;&nbsp;&nbsp;&nbsp;<br />thread2:MyThread2&nbsp;&nbsp;&nbsp;&nbsp; <br />thread3:Thread-1</p>&nbsp;&nbsp;<p>从上面的输出结果可以看出，thread1和thread2的线程名都已经修改了，而thread3的线程名仍然为默认值：Thread-1。thread3的线程名之所以不是Thread-2，而是Thread-1，这是因为在026行已经指定了thread2的Name，因此，启动thread3时就将thread3的线程名设为Thread-1。因此就会得到上面的输出结果。</p>&nbsp;&nbsp;<p><strong>注意：</strong>在调用start方法前后都可以使用setName设置线程名，但在调用start方法后使用setName修改线程名，会产生不确定性，也就是说可能在run方法执行完后才会执行setName。如果在run方法中要使用线程名，就会出现虽然调用了setName方法，但线程名却未修改的现象。</p>&nbsp;&nbsp;<p>Thread类的start方法不能多次调用，如不能调用两次thread1.start()方法。否则会抛出一个IllegalThreadStateException异常。</p>&nbsp;&nbsp;<p>&nbsp;</p>&nbsp;&nbsp;<p><strong>第三节 使用Runnable接口创建线程</strong></p>&nbsp;&nbsp;<p>实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为两步：</p>&nbsp;&nbsp;<p>1. 将实现Runnable接口的类实例化。</p>&nbsp;&nbsp;<p>2. 建立一个Thread对象，并将第一步实例化后的对象作为参数传入Thread类的构造方法。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp; 最后通过Thread类的start方法建立线程。</p>&nbsp;&nbsp;<p>下面的代码演示了如何使用Runnable接口来创建线程：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyRunnable implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Thread.currentThread().getName());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyRunnable t1 = new MyRunnable();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyRunnable t2 = new MyRunnable();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread1 = new Thread(t1, &quot;MyThread1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread2 = new Thread(t2);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.setName(&quot;MyThread2&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>MyThread1&nbsp;&nbsp;&nbsp;&nbsp;<br />MyThread2</p>&nbsp;&nbsp;<p><strong>第四节 线程的生命周期</strong></p>&nbsp;&nbsp;<p>与人有生老病死一样，线程也同样要经历开始（等待）、运行、挂起和停止四种不同的状态。这四种状态都可以通过Thread类中的方法进行控制。下面给出了Thread类中和这四种状态相关的方法。</p>&nbsp;&nbsp;<p>// 开始线程&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; public void start( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void run( );&nbsp;&nbsp;&nbsp;&nbsp; <br />// 挂起和唤醒线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void resume( );&nbsp;&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void suspend( );&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void sleep(long millis);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static void sleep(long millis, int nanos);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // 终止线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public void stop( );&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 不建议使用&nbsp;&nbsp;&nbsp;&nbsp; <br />public void interrupt( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // 得到线程状态&nbsp;&nbsp;&nbsp;&nbsp; <br />public boolean isAlive( );&nbsp;&nbsp;&nbsp;&nbsp; <br />public boolean isInterrupted( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static boolean interrupted( );&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; // join方法&nbsp;&nbsp;&nbsp;&nbsp; <br />public void join( ) throws InterruptedException;</p>&nbsp;&nbsp;<p><strong>一、创建并运行线程</strong></p>&nbsp;&nbsp;<p>线程在建立后并不马上执行run方法中的代码，而是处于等待状态。线程处于等待状态时，可以通过Thread类的方法来设置线程不各种属性，如线程的优先级（setPriority）、线程名(setName)和线程的类型（setDaemon）等。</p>&nbsp;&nbsp;<p>当调用start方法后，线程开始执行run方法中的代码。线程进入运行状态。可以通过Thread类的isAlive方法来判断线程是否处于运行状态。当线程处于运行状态时，isAlive返回true，当isAlive返回false时，可能线程处于等待状态，也可能处于停止状态。下面的代码演示了线程的创建、运行和停止三个状态之间的切换，并输出了相应的isAlive返回值。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class LifeCycle extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />while ((++n) &lt; 1000);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LifeCycle thread1 = new LifeCycle();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread1.join();&nbsp; // 等线程thread1结束后再继续执行&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;thread1已经结束!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;isAlive: &quot; + thread1.isAlive());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>要注意一下，在上面的代码中使用了join方法，这个方法的主要功能是保证线程的run方法完成后程序才继续运行，这个方法将在后面的文章中介绍&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; 上面代码的运行结果：</p>&nbsp;&nbsp;<p>isAlive: false&nbsp;&nbsp;&nbsp;&nbsp;<br />isAlive: true&nbsp;&nbsp;&nbsp;&nbsp; <br />thread1已经结束!&nbsp;&nbsp;&nbsp;&nbsp; <br />isAlive: false</p>&nbsp;&nbsp;<p><strong>二、挂起和唤醒线程</strong></p>&nbsp;&nbsp;<p>一但线程开始执行run方法，就会一直到这个run方法执行完成这个线程才退出。但在线程执行的过程中，可以通过两个方法使线程暂时停止执行。这两个方法是suspend和sleep。在使用suspend挂起线程后，可以通过resume方法唤醒线程。而使用sleep使线程休眠后，只能在设定的时间后使线程处于就绪状态（在线程休眠结束后，线程不一定会马上执行，只是进入了就绪状态，等待着系统进行调度）。</p>&nbsp;&nbsp;<p>虽然suspend和resume可以很方便地使线程挂起和唤醒，但由于使用这两个方法可能会造成一些不可预料的事情发生，因此，这两个方法被标识为deprecated(抗议)标记，这表明在以后的jdk版本中这两个方法可能被删除，所以尽量不要使用这两个方法来操作线程。下面的代码演示了sleep、suspend和resume三个方法的使用。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />class SleepThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(2000);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(new java.util.Date().getTime());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread thread = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SleepThread sleepThread = thread.new SleepThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleepThread.start(); // 开始运行线程sleepThread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleepThread.join();&nbsp; // 使线程sleepThread延迟2秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />boolean flag = false;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(5000);&nbsp; // 使主线程延迟5秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; flag = !flag;&nbsp;&nbsp;&nbsp;&nbsp; <br />if (flag)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.suspend();&nbsp; <br />else&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.resume();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>从表面上看，使用sleep和suspend所产生的效果类似，但sleep方法并不等同于suspend。它们之间最大的一个区别是可以在一个线程中通过suspend方法来挂起另外一个线程，如上面代码中在主线程中挂起了thread线程。而sleep只对当前正在执行的线程起作用。在上面代码中分别使sleepThread和主线程休眠了2秒和5秒。在使用sleep时要注意，不能在一个线程中来休眠另一个线程。如main方法中使用thread.sleep(2000)方法是无法使thread线程休眠2秒的，而只能使主线程休眠2秒。</p>&nbsp;&nbsp;<p>在使用sleep方法时有两点需要注意：</p>&nbsp;&nbsp;<p>1. sleep方法有两个重载形式，其中一个重载形式不仅可以设毫秒，而且还可以设纳秒(1,000,000纳秒等于1毫秒)。但大多数操作系统平台上的Java虚拟机都无法精确到纳秒，因此，如果对sleep设置了纳秒，Java虚拟机将取最接近这个值的毫秒。</p>&nbsp;&nbsp;<p>2. 在使用sleep方法时必须使用throws或try&#123;...&#125;catch&#123;...&#125;。因为run方法无法使用throws，所以只能使用try&#123;...&#125;catch&#123;...&#125;。当在线程休眠的过程中，使用interrupt方法（这个方法将在2.3.3中讨论）中断线程时sleep会抛出一个InterruptedException异常。sleep方法的定义如下：</p>&nbsp;&nbsp;<p>public static void sleep(long millis)&nbsp; throws InterruptedException&nbsp;&nbsp;&nbsp;&nbsp;<br />public static void sleep(long millis,&nbsp; int nanos)&nbsp; throws InterruptedException</p>&nbsp;&nbsp;<p><strong>三、终止线程的三种方法</strong></p>&nbsp;&nbsp;<p>有三种方法可以使终止线程。</p>&nbsp;&nbsp;<p>1.&nbsp; 使用退出标志，使线程正常退出，也就是当run方法完成后线程终止。</p>&nbsp;&nbsp;<p>2.&nbsp; 使用stop方法强行终止线程（这个方法不推荐使用，因为stop和suspend、resume一样，也可能发生不可预料的结果）。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 3.&nbsp; 使用interrupt方法中断线程。<strong> </strong></p>&nbsp;&nbsp;<p><strong>1. </strong><strong>使用退出标志终止线程</strong></p>&nbsp;&nbsp;<p>当run方法执行完后，线程就会退出。但有时run方法是永远不会结束的。如在服务端程序中使用线程进行监听客户端请求，或是其他的需要循环处理的任务。在这种情况下，一般是将这些任务放在一个循环中，如while循环。如果想让循环永远运行下去，可以使用while(true)&#123;...&#125;来处理。但要想使while循环在某一特定条件下退出，最直接的方法就是设一个boolean类型的标志，并通过设置这个标志为true或false来控制while循环是否退出。下面给出了一个利用退出标志终止线程的例子。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class ThreadFlag extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public volatile boolean exit = false;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />while (!exit);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ThreadFlag thread = new ThreadFlag();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(5000); // 主线程延迟5秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.exit = true;&nbsp; // 终止线程thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;线程退出!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面代码中定义了一个退出标志exit，当exit为true时，while循环退出，exit的默认值为false。在定义exit时，使用了一个Java关键字volatile，这个关键字的目的是使exit同步，也就是说在同一时刻只能由一个线程来修改exit的值，</p>&nbsp;&nbsp;<p><strong>2. </strong><strong>使用</strong><strong>stop</strong><strong>方法终止线程</strong></p>&nbsp;&nbsp;<p>使用stop方法可以强行终止正在运行或挂起的线程。我们可以使用如下的代码来终止线程：</p>&nbsp;&nbsp;<p>thread.stop();</p>&nbsp;&nbsp;<p>虽然使用上面的代码可以终止线程，但使用stop方法是很危险的，就象突然关闭计算机电源，而不是按正常程序关机一样，可能会产生不可预料的结果，因此，并不推荐使用stop方法来终止线程。</p>&nbsp;&nbsp;<p><strong>3. </strong><strong>使用</strong><strong>interrupt</strong><strong>方法终止线程</strong></p>&nbsp;&nbsp;<p>使用interrupt方法来终端线程可分为两种情况：</p>&nbsp;&nbsp;<p>（1）线程处于阻塞状态，如使用了sleep方法。</p>&nbsp;&nbsp;<p>（2）使用while(!isInterrupted())&#123;...&#125;来判断线程是否被中断。</p>&nbsp;&nbsp;<p>在第一种情况下使用interrupt方法，sleep方法将抛出一个InterruptedException例外，而在第二种情况下线程将直接退出。下面的代码演示了在第一种情况下使用interrupt方法。</p>&nbsp;&nbsp;<p>package chapter2;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class ThreadInterrupt extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(50000);&nbsp; // 延迟50秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (InterruptedException e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(e.getMessage());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new ThreadInterrupt();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;在50秒之内按任意键中断线程!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.in.read();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.interrupt();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;线程已经退出!&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>上面代码的运行结果如下：</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 在50秒之内按任意键中断线程!&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; sleep interrupted&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; 线程已经退出!</p>&nbsp;&nbsp;<p>在调用interrupt方法后， sleep方法抛出异常，然后输出错误信息：sleep interrupted。</p>&nbsp;&nbsp;<p>注意：在Thread类中有两个方法可以判断线程是否通过interrupt方法被终止。一个是静态的方法interrupted()，一个是非静态的方法isInterrupted()，这两个方法的区别是interrupted用来判断当前线是否被中断，而isInterrupted可以用来判断其他线程是否被中断。因此，while (!isInterrupted())也可以换成while (!Thread.interrupted())。</p>&nbsp;&nbsp;<p><strong>第五节 join方法的使用</strong></p>&nbsp;&nbsp;<p>在上面的例子中多次使用到了Thread类的join方法。我想大家可能已经猜出来join方法的功能是什么了。对，join方法的功能就是使异步执行的线程变成同步执行。也就是说，当调用线程实例的start方法后，这个方法会立即返回，如果在调用start方法后后需要使用一个由这个线程计算得到的值，就必须使用join方法。如果不使用join方法，就不能保证当执行到start方法后面的某条语句时，这个线程一定会执行完。而使用join方法后，直到这个线程退出，程序才会往下执行。下面的代码演示了join的用法。</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />static synchronized void inc()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inc();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3);&nbsp; // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp; // 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp; // 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />if (args.length &gt; 0)&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp; // 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在例程2-8中建立了100个线程，每个线程使静态变量n增加10。如果在这100个线程都执行完后输出n，这个n值应该是1000。</p>&nbsp;&nbsp;<p><strong>1.&nbsp; </strong><strong>测试</strong><strong>1</strong></p>&nbsp;&nbsp;<p>使用如下的命令运行上面程序：</p>&nbsp;&nbsp;<p>java mythread.JoinThread</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>n=442</p>&nbsp;&nbsp;<p>这个运行结果可能在不同的运行环境下有一些差异，但一般n不会等于1000。从上面的结果可以肯定，这100个线程并未都执行完就将n输出了。</p>&nbsp;&nbsp;<p><strong>2.&nbsp; 测试</strong><strong>2</strong></p>&nbsp;&nbsp;<p>使用如下的命令运行上面的代码：</p>&nbsp;&nbsp;<p>在上面的命令行中有一个参数join，其实在命令行中可以使用任何参数，只要有一个参数就可以，这里使用join，只是为了表明要使用join方法使这100个线程同步执行。</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>n=1000</p>&nbsp;&nbsp;<p>无论在什么样的运行环境下运行上面的命令，都会得到相同的结果：n=1000。这充分说明了这100个线程肯定是都执行完了，因此，n一定会等于1000。</p>&nbsp;&nbsp;<p><strong>第六节 慎重使用volatile关键字</strong></p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量，如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile，对它们的操作就会变成原子级别的。但这有一定的限制。例如，下面的例子中的n就不是原子级别的：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static volatile int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n = n + 1;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3); // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 如果对n的操作是原子级别的，最后输出的结果应该为n=1000，而在执行上面积代码时，很多时侯输出的n都小于1000，这说明n=n+1不是原子级别的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关，那么volatile关键字不起作用，也就是说如下的表达式都不是原子操作：</p>&nbsp;&nbsp;<p>n = n + 1;&nbsp;&nbsp;&nbsp;&nbsp;<br />n++;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果要想使这种情况变成原子操作，需要使用synchronized关键字，如上的代码可以改成如下的形式：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class JoinThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void inc()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; 10; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; inc(); // n = n + 1 改成了 inc();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(3); // 为了使运行结果更随机，延迟3毫秒&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 建立100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new JoinThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 运行刚才建立的100个线程&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />// 100个线程都执行完后继续&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n=&quot; + JoinThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 上面的代码将n=n+1改成了inc()，其中inc方法使用了synchronized关键字进行方法同步。因此，在使用volatile关键字时要慎重，并不是只要简单类型变量使用volatile修饰，对这个变量的所有操作都是原来操作，当变量的值由自身的上一个决定时，如n=n+1、n++ 等，volatile关键字将失效，只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的，如n = m + 1，这个就是原级别的。所以在使用volatile关键时一定要谨慎，如果自己没有把握，可以使用synchronized来代替volatile。</p>&nbsp;&nbsp;<p><strong>第七节 向线程传递数据的三种方法</strong></p>&nbsp;&nbsp;<p>在传统的同步开发模式下，当我们调用一个函数时，通过这个函数的参数将数据传入，并通过这个函数的返回值来返回最终的计算结果。但在多线程的异步开发模式下，数据的传递和返回和同步开发模式有很大的区别。由于线程的运行和结束是不可预料的，因此，在传递和返回数据时就无法象函数一样通过函数参数和return语句来返回数据。本文就以上原因介绍了几种用于向线程传递数据的方法，在下一篇文章中将介绍从线程中返回数据的方法。</p>&nbsp;&nbsp;<p>欲先取之，必先予之。一般在使用线程时都需要有一些初始化数据，然后线程利用这些数据进行加工处理，并返回结果。在这个过程中最先要做的就是向线程中传递数据。</p>&nbsp;&nbsp;<p><strong>一、通过构造方法传递数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>在创建线程时，必须要建立一个Thread类的或其子类的实例。因此，我们不难想到在调用start方法之前通过线程类的构造方法将数据传入线程。并将传入的数据使用类变量保存起来，以便线程使用(其实就是在run方法中使用)。下面的代码演示了如何通过构造方法来传递数据：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String name;&nbsp;&nbsp;&nbsp;&nbsp; <br />public MyThread1(String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.name = name;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;hello &quot; + name);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new MyThread1(&quot;world&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp; 由于这种方法是在创建线程对象的同时传递数据的，因此，在线程运行之前这些数据就就已经到位了，这样就不会造成数据在线程运行后才传入的现象。如果要传递更复杂的数据，可以使用集合、类等数据结构。使用构造方法来传递数据虽然比较安全，但如果要传递的数据比较多时，就会造成很多不便。由于Java没有默认参数，要想实现类似默认参数的效果，就得使用重载，这样不但使构造方法本身过于复杂，又会使构造方法在数量上大增。因此，要想避免这种情况，就得通过类方法或类变量来传递数据。<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>二、通过变量和方法传递数据&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>向对象中传入数据一般有两次机会，第一次机会是在建立对象时通过构造方法将数据传入，另外一次机会就是在类中定义一系列的public的方法或变量（也可称之为字段）。然后在建立完对象后，通过对象实例逐个赋值。下面的代码是对MyThread1类的改版，使用了一个setName方法来设置name变量：</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread2 implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String name;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void setName(String name)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.name = name;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;hello &quot; + name);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread2 myThread = new MyThread2();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myThread.setName(&quot;world&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new Thread(myThread);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>三、</strong><strong>通过回调函数传递数据</strong></p>&nbsp;&nbsp;<p>上面讨论的两种向线程中传递数据的方法是最常用的。但这两种方法都是main方法中主动将数据传入线程类的。这对于线程来说，是被动接收这些数据的。然而，在有些应用中需要在线程运行的过程中动态地获取数据，如在下面代码的run方法中产生了3个随机数，然后通过Work类的process方法求这三个随机数的和，并通过Data类的value将结果返回。从这个例子可以看出，在返回value之前，必须要得到三个随机数。也就是说，这个value是无法事先就传入线程类的。</p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />class Data&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public int value = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Work&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void process(Data data, Integer&hellip; numbers)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int n : numbers)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; data.value += n;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class MyThread3 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Work work;&nbsp;&nbsp;&nbsp;&nbsp; <br />public MyThread3(Work work)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.work = work;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; java.util.Random random = new java.util.Random();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Data data = new Data();&nbsp;&nbsp;&nbsp;&nbsp; <br />int n1 = random.nextInt(1000);&nbsp;&nbsp;&nbsp;&nbsp; <br />int n2 = random.nextInt(2000);&nbsp;&nbsp;&nbsp;&nbsp; <br />int n3 = random.nextInt(3000);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; work.process(data, n1, n2, n3);&nbsp;&nbsp; // 使用回调函数&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(String.valueOf(n1) + &quot;+&quot; + String.valueOf(n2) + &quot;+&quot;&nbsp;&nbsp;&nbsp;&nbsp; <br />+ String.valueOf(n3) + &quot;=&quot; + data.value);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread thread = new MyThread3(new Work());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面代码中的process方法被称为回调函数。从本质上说，回调函数就是事件函数。在Windows API中常使用回调函数和调用API的程序之间进行数据交互。因此，调用回调函数的过程就是最原始的引发事件的过程。在这个例子中调用了process方法来获得数据也就相当于在run方法中引发了一个事件。</p>&nbsp;&nbsp;<p><strong>第八节 从线程返回数据的两种方法</strong></p>&nbsp;&nbsp;<p>从线程中返回数据和向线程传递数据类似。也可以通过类成员以及回调函数来返回数据。但类成员在返回数据和传递数据时有一些区别，下面让我们来看看它们区别在哪。</p>&nbsp;&nbsp;<p><strong>一、</strong><strong>通过类变量和方法返回数据</strong></p>&nbsp;&nbsp;<p>使用这种方法返回数据需要在调用start方法后才能通过类变量或方法得到数据。让我们先来看看例程2-13会得到什么结果。</p>&nbsp;&nbsp;<p><a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html"></a></p>&nbsp;&nbsp;<p>package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String value1;&nbsp;&nbsp;&nbsp;&nbsp; <br />private String value2;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value1 = &quot;通过成员变量返回数据&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; value2 = &quot;通过成员方法返回数据&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread thread = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;value1:&quot; + thread.value1);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;value2:&quot; + thread.value2);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>运行上面的代码有可能输出如下的结果：</p>&nbsp;&nbsp;<p>value1:null&nbsp;&nbsp;&nbsp;&nbsp;<br />value2:null</p>&nbsp;&nbsp;<p>从上面的运行结果看很不正常。在run方法中已经对value1和value2赋了值，而返回的却是null。发生这种情况的原因是调用start方法后就立刻输出了value1和value2的值，而这里run方法还没有执行到为value1和value2赋值的语句。要避免这种情况的发生，就需要等run方法执行完后才执行输出value1和value2的代码。因此，我们可以想到使用sleep方法将主线程进行延迟，如可以在thread.start()后加一行如下的语句：</p>&nbsp;&nbsp;<p>sleep(1000);</p>&nbsp;&nbsp;<p>这样做可以使主线程延迟1秒后再往下执行，但这样做有一个问题，就是我们怎么知道要延迟多长时间。在这个例子的run方法中只有两条赋值语句，而且只创建了一个线程，因此，延迟1秒已经足够，但如果run方法中的语句很复杂，这个时间就很难预测，因此，这种方法并不稳定。</p>&nbsp;&nbsp;<p>我们的目的就是得到value1和value2的值，因此，只要判断value1和value2是否为null。如果它们都不为null时，就可以输出这两个值了。我们可以使用如下的代码来达到这个目的：</p>&nbsp;&nbsp;<p>while (thread.value1 == null &#124;&#124; thread.value2 == null);</p>&nbsp;&nbsp;<p>使用上面的语句可以很稳定地避免这种情况发生，但这种方法太耗费系统资源。大家可以设想，如果run方法中的代码很复杂，value1和value2需要很长时间才能被赋值，这样while循环就必须一直执行下去，直到value1和value2都不为空为止。因此，我们可以对上面的语句做如下的改进：</p>&nbsp;&nbsp;<p>while (thread.value1 == null &#124;&#124; thread.value2 == null)&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; sleep(100);</p>&nbsp;&nbsp;<p>在while循环中第判断一次value1和value2的值后休眠100毫秒，然后再判断这两个值。这样所占用的系统资源会小一些。</p>&nbsp;&nbsp;<p>上面的方法虽然可以很好地解决，但Java的线程模型为我们提供了更好的解决方案，这就是join方法。在前面已经讨论过，join的功能就是使用线程从异步执行变成同步执行。当线程变成同步执行后，就和从普通的方法中得到返回数据没有什么区别了。因此，可以使用如下的代码更有效地解决这个问题：</p>&nbsp;&nbsp;<p>&hellip;&nbsp;&nbsp;&nbsp;&nbsp;<br />thread.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />thread.join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 在thread.join()执行完后，线程thread的run方法已经退出了，也就是说线程thread已经结束了。因此，在thread.join()后面可以放心大胆地使用MyThread类的任何资源来得到返回数据。</p>&nbsp;&nbsp;<p><strong>二、</strong><strong>通过回调函数返回数据</strong></p>&nbsp;&nbsp;<p>其实这种方法已经在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html">《向线程传递数据的三种方法》</a>中介绍了。在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/16/260131.html">《向线程传递数据的三种方法》</a>一文的例子中通过Work类的process方法向线程中传递了计算结果，但同时，也通过process方法从线程中得到了三个随机数。因此，这种方法既可以向线程中传递数据，也可以从线程中获得数据。</p>&nbsp;&nbsp;<p><strong>第九节 为什么要进行数据同步</strong></p>&nbsp;&nbsp;<p>Java中的变量分为两类：局部变量和类变量。局部变量是指在方法内定义的变量，如在run方法中定义的变量。对于这些变量来说，并不存在线程之间共享的问题。因此，它们不需要进行数据同步。类变量是在类中定义的变量，作用域是整个类。这类变量可以被多个线程共享。因此，我们需要对这类变量进行数据同步。</p>&nbsp;&nbsp;<p>数据同步就是指在同一时间，只能由一个线程来访问被同步的类变量，当前线程访问完这些变量后，其他线程才能继续访问。这里说的访问是指有写操作的访问，如果所有访问类变量的线程都是读操作，一般是不需要数据同步的。</p>&nbsp;&nbsp;<p>那么如果不对共享的类变量进行数据同步，会发生什么情况呢？让我们先看看下面的代码会发生什么样的事情：</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static int n = 0;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />int m = n;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yield();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; n = m;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread myThread = new MyThread ();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[100];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new Thread(myThread);&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].join();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(&quot;n = &quot; + MyThread.n);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; 在执行上面代码的可能结果如下：</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp;&nbsp; n = 59</p>&nbsp;&nbsp;<p>看到这个结果，可能很多读者会感到奇怪。这个程序明明是启动了100个线程，然后每个线程将静态变量n加1。最后使用join方法使这100个线程都运行完后，再输出这个n值。按正常来讲，结果应该是n = 100。可偏偏结果小于100。</p>&nbsp;&nbsp;<p>其实产生这种结果的罪魁祸首就是我们经常提到的&ldquo;脏数据&rdquo;。而run方法中的yield()语句就是产生&ldquo;脏数据&rdquo;的始作俑者（不加yield语句也可能会产生&ldquo;脏数据&rdquo;，但不会这么明显，只有将100改成更大的数，才会经常产生&ldquo;脏数据&rdquo;，在本例中调用yield就是为了放大&ldquo;脏数据&rdquo;的效果）。yield方法的作用是使线程暂停，也就是使调用yield方法的线程暂时放弃CPU资源，使CPU有机会来执行其他的线程。为了说明这个程序如何产生&ldquo;脏数据&rdquo;，我们假设只创建了两个线程：thread1和thread2。由于先调用了thread1的start方法，因此，thread1的run方法一般会先运行。当thread1的run方法运行到第一行（int m = n;）时，将n的值赋给m。当执行到第二行的yield方法后，thread1就会暂时停止执行，而当thread1暂停时，thread2获得了CPU资源后开始运行（之前thread2一直处于就绪状态），当thread2执行到第一行(int m = n;)时，由于thread1在执行到yield时n仍然是0，因此，thread2中的m获得的值也是0。这样就造成了thread1和thread2的m获得的都是0。在它们执行完yield方法后，都是从0开始加1，因此，无论谁先执行完，最后n的值都是1，只是这个n被thread1和thread2各赋了一遍值。这个过程如下图如示：</p>&nbsp;&nbsp;<p><img src="../hznter/upload/blog/200907/zsj.jpg" border="0" /></p>&nbsp;&nbsp;<p>也许有人会问，如果只有n++，会产生&ldquo;脏数据&rdquo;吗？答案是肯定的。那么n++只是一条语句，又如何在执行过程中将CPU交给其他的线程呢？其实这只是表面现象，n++在被Java编译器编译成中间语言（也叫做字节码）后，并不是一条语言。让我们看看下面的Java代码将会被编译成什么样的Java中间语言。</p>&nbsp;&nbsp;<p><strong>Java</strong><strong>源代码</strong></p>&nbsp;&nbsp;<p>public void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; n++;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>被编译后的中间语言代码</strong></p>&nbsp;&nbsp;<p>001 public void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />003&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; aload_0&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dup&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />005&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getfield&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iconst_1&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iadd&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; putfield&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />009 return&nbsp;&nbsp;&nbsp;&nbsp; <br />010&nbsp; &#125;</p>&nbsp;&nbsp;<p>大家可以看到在run方法中只有n++一条语句，而在编译后，却有7条中间语言语句。我们并不需要知道这些语句的功能是什么，只看一下第005、007和008行语句。在005行是getfield，根据它的英文含义可知是要得到某个值，因为这里只有一个n，所以毫无疑问，是要得到n的值。而在007行的iadd也不难猜测是将这个得到的n值加1。在008行的putfield的含义我想大家可能已经猜出来了，它负责将这个加1后的n再更新回类变量n。说到这，可能大家还有一个疑惑，执行n++时直接将n加1不就行了，为什么要如此费周折。其实这里涉及到一个Java内存模型的问题。</p>&nbsp;&nbsp;<p>Java的内存模型分为主存储区和工作存储区。主存储区保存了Java中所有的实例。也就是说，在我们使用new来建立一个对象后，这个对象及它内部的方法、变量等都保存在这一区域，在MyThread类中的n就保存在这个区域。主存储区可以被所有线程共享。而工作存储区就是我们前面所讲的线程栈，在这个区域里保存了在run方法以及run方法所调用的方法中定义的变量，也就是方法变量。在线程要修改主存储区中的变量时，并不是直接修改这些变量，而是将它们先复制到当前线程的工作存储区，在修改完后，再将这个变量值覆盖主存储区的相应的变量值。</p>&nbsp;&nbsp;<p>在了解了Java的内存模型后，就不难理解为什么n++也不是原子操作了。它必须经过一个拷贝、加1和覆盖的过程。这个过程和在MyThread类中模拟的过程类似。大家可以想象，如果在执行到getfield时，thread1由于某种原因被中断，那么就会发生和MyThread类的执行结果类似的情况。要想彻底解决这个问题，就必须使用某种方法对n进行同步，也就是在同一时间只能有一个线程操作n，这也称为对n的原子操作。</p>&nbsp;&nbsp;<p><strong>第十节 使用Synchronized关键字同步类方法</strong></p>&nbsp;&nbsp;<p>要想解决&ldquo;脏数据&rdquo;的问题，最简单的方法就是使用synchronized关键字来使run方法同步，代码如下：</p>&nbsp;&nbsp;<p>public synchronized void run()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>从上面的代码可以看出，只要在void和public之间加上synchronized关键字，就可以使run方法同步，也就是说，对于同一个Java类的对象实例，run方法同时只能被一个线程调用，并当前的run执行完后，才能被其他的线程调用。即使当前线程执行到了run方法中的yield方法，也只是暂停了一下。由于其他线程无法执行run方法，因此，最终还是会由当前的线程来继续执行。先看看下面的代码：</p>&nbsp;&nbsp;<p><strong>sychronized</strong><strong>关键字只和一个对象实例绑定</strong></p>&nbsp;&nbsp;<p>class Test&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class Sync implements Runnable&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Test test;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; test.method();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public Sync(Test test)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />this.test = test;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test test1 = new Test();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Test test2 = new Test();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sync sync1 = new Sync(test1);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Sync sync2 = new Sync(test2);&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(sync1).start();&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(sync2).start();&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>在Test类中的method方法是同步的。但上面的代码建立了两个Test类的实例，因此，test1和test2的method方法是分别执行的。要想让method同步，必须在建立Sync类的实例时向它的构造方法中传入同一个Test类的实例，如下面的代码所示：</p>&nbsp;&nbsp;<p>Sync sync1 = new Sync(test1);</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 不仅可以使用synchronized来同步非静态方法，也可以使用synchronized来同步静态方法。如可以按如下方式来定义method方法：</p>&nbsp;&nbsp;<p>class Test&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static synchronized void method() &#123; &hellip; &hellip; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>建立Test类的对象实例如下：</p>&nbsp;&nbsp;<p>Test test = new Test();</p>&nbsp;&nbsp;<p>对于静态方法来说，只要加上了synchronized关键字，这个方法就是同步的，无论是使用test.method()，还是使用Test.method()来调用method方法，method都是同步的，并不存在非静态方法的多个实例的问题。</p>&nbsp;&nbsp;<p>在23种设计模式中的单件（Singleton）模式如果按传统的方法设计，也是线程不安全的，下面的代码是一个线程不安全的单件模式。</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />// 线程安全的Singleton模式&nbsp;&nbsp;&nbsp;&nbsp; <br />class Singleton&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />private static Singleton sample;&nbsp;&nbsp;&nbsp;&nbsp; <br />private Singleton()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static Singleton getInstance()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />if (sample == null)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.yield(); // 为了放大Singleton模式的线程不安全性&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample = new Singleton();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />return sample;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public class MyThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Singleton singleton = Singleton.getInstance();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(singleton.hashCode());&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread threads[] = new Thread[5];&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i] = new MyThread();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 0; i &lt; threads.length; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; threads[i].start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面的代码调用yield方法是为了使单件模式的线程不安全性表现出来，如果将这行去掉，上面的实现仍然是线程不安全的，只是出现的可能性小得多。</p>&nbsp;&nbsp;<p>程序的运行结果如下：</p>&nbsp;&nbsp;<p>25358555&nbsp;&nbsp;&nbsp;&nbsp;<br />26399554&nbsp;&nbsp;&nbsp;&nbsp; <br />7051261&nbsp;&nbsp;&nbsp;&nbsp; <br />29855319&nbsp;&nbsp;&nbsp;&nbsp; <br />5383406</p>&nbsp;&nbsp;<p>上面的运行结果可能在不同的运行环境上有所有同，但一般这五行输出不会完全相同。从这个输出结果可以看出，通过getInstance方法得到的对象实例是五个，而不是我们期望的一个。这是因为当一个线程执行了Thread.yield()后，就将CPU资源交给了另外一个线程。由于在线程之间切换时并未执行到创建Singleton对象实例的语句，因此，这几个线程都通过了if判断，所以，就会产生了建立五个对象实例的情况（可能创建的是四个或三个对象实例，这取决于有多少个线程在创建Singleton对象之前通过了if判断，每次运行时可能结果会不一样）。</p>&nbsp;&nbsp;<p>要想使上面的单件模式变成线程安全的，只要为getInstance加上synchronized关键字即可。代码如下：</p>&nbsp;&nbsp;<p>public static synchronized Singleton getInstance() &#123; &hellip; &hellip; &#125;</p>&nbsp;&nbsp;<p>当然，还有更简单的方法，就是在定义Singleton变量时就建立Singleton对象，代码如下：</p>&nbsp;&nbsp;<p>private static final Singleton sample = new Singleton();</p>&nbsp;&nbsp;<p>然后在getInstance方法中直接将sample返回即可。这种方式虽然简单，但不知在getInstance方法中创建Singleton对象灵活。读者可以根据具体的需求选择使用不同的方法来实现单件模式。</p>&nbsp;&nbsp;<p>在使用synchronized关键字时有以下四点需要注意：<strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p><strong>1.&nbsp; synchronized</strong><strong>关键字不能继承。</strong></p>&nbsp;&nbsp;<p>虽然可以使用synchronized来定义方法，但synchronized并不属于方法定义的一部分，因此，synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字，而在子类中覆盖了这个方法，在子类中的这个方法默认情况下并不是同步的，而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然，还可以在子类方法中调用父类中相应的方法，这样虽然子类中的方法不是同步的，但子类调用了父类的同步方法，因此，子类的方法也就相当于同步了。这两种方式的例子代码如下：</p>&nbsp;&nbsp;<p><strong>在子类方法中加上</strong><strong>synchronized</strong><strong>关键字&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>class Parent&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Child extends Parent&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>在子类方法中调用父类的同步方法</strong></p>&nbsp;&nbsp;<p>class Parent&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public synchronized void method() &#123;&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />class Child extends Parent&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void method() &#123; super.method();&nbsp; &hellip; &hellip;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>2.&nbsp; 在定义接口方法时不能使用</strong><strong>synchronized</strong><strong>关键字。</strong></p>&nbsp;&nbsp;<p><strong>3.&nbsp; 构造方法不能使用</strong><strong>synchronized</strong><strong>关键字，但可以使用下节要讨论的</strong><strong>synchronized</strong><strong>块来进行同步。</strong></p>&nbsp;&nbsp;<p><strong>4.&nbsp; synchronized</strong><strong>可以自由放置。</strong></p>&nbsp;&nbsp;<p>在前面的例子中使用都是将synchronized关键字放在方法的返回类型前面。但这并不是synchronized可放置唯一位置。在非静态方法中，synchronized还可以放在方法定义的最前面，在静态方法中，synchronized可以放在static的前面，代码如下：</p>&nbsp;&nbsp;<p>public synchronized void method();&nbsp;&nbsp;&nbsp;&nbsp;<br />synchronized public void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized static void method();&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized public static void method();</p>&nbsp;&nbsp;<p>但要注意，synchronized不能放在方法返回类型的后面，如下面的代码是错误的：</p>&nbsp;&nbsp;<p>public void synchronized method();&nbsp;&nbsp;&nbsp;&nbsp;<br />public static void synchronized method();</p>&nbsp;&nbsp;<p>synchronized关键字只能用来同步方法，不能用来同步类变量，如下面的代码也是错误的。</p>&nbsp;&nbsp;<p>public synchronized int n = 0;&nbsp;&nbsp;&nbsp;&nbsp;<br />public static synchronized int n = 0;</p>&nbsp;&nbsp;<p>虽然使用synchronized关键字同步方法是最安全的同步方式，但大量使用synchronized关键字会造成不必要的资源消耗以及性能损失。虽然从表面上看synchronized锁定的是一个方法，但实际上synchronized锁定的是一个类。也就是说，如果在非静态方法method1和method2定义时都使用了synchronized，在method1未执行完之前，method2是不能执行的。静态方法和非静态方法的情况类似。但静态和非静态方法不会互相影响。看看如下的代码：</p>&nbsp;&nbsp;<p>package test;&nbsp;&nbsp;&nbsp;&nbsp;<br />public class MyThread1 extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public String methodName;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method(String s)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp;&nbsp;&nbsp; <br />while (true)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;非静态的method1方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public synchronized void method2()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;非静态的method2方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method3()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;静态的method3方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method4()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;静态的method4方法&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />try&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; getClass().getMethod(methodName).invoke(this);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />catch (Exception e)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; MyThread1 myThread1 = new MyThread1();&nbsp;&nbsp;&nbsp;&nbsp; <br />for (int i = 1; i &lt;= 4; i++)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; myThread1.methodName = &quot;method&quot; + String.valueOf(i);&nbsp;&nbsp;&nbsp;&nbsp; <br />new Thread(myThread1).start();&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sleep(100);&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>运行结果如下：</p>&nbsp;&nbsp;<p>非静态的method1方法&nbsp;&nbsp;&nbsp;&nbsp;<br />静态的method3方法</p>&nbsp;&nbsp;<p>从上面的运行结果可以看出，method2和method4在method1和method3未结束之前不能运行。因此，我们可以得出一个结论，如果在类中使用synchronized关键字来定义非静态方法，那将影响这个中的所有使用synchronized关键字定义的非静态方法。如果定义的是静态方法，那么将影响类中所有使用synchronized关键字定义的静态方法。这有点象数据表中的表锁，当修改一条记录时，系统就将整个表都锁住了，因此，大量使用这种同步方式会使程序的性能大幅度下降。</p>&nbsp;&nbsp;<p><strong>第十一节 使用Synchronized块同步方法</strong></p>&nbsp;&nbsp;<p>synchronized关键字有两种用法。第一种就是在<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/nokiaguy/archive/2009/03/20/261020.html">《使用Synchronized关键字同步类方法》</a>一文中所介绍的直接用在方法的定义中。另外一种就是synchronized块。我们不仅可以通过synchronized块来同步一个对象变量。也可以使用synchronized块来同步类中的静态方法和非静态方法。</p>&nbsp;&nbsp;<p>synchronized块的语法如下：</p>&nbsp;&nbsp;<p>public void method()&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(表达式)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p><strong>一、非静态类方法的同步 </strong></p>&nbsp;&nbsp;<p>从<a href="http://www.blogjava.net/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/03/archive/2009/03/archive/2009/03/archive/2009/nokiaguy/archive/2009/nokiaguy/archive/2009/03/20/261020.html">《使用Synchronized关键字同步类方法》</a>一文中我们知道使用synchronized关键字来定义方法就会锁定类中所有使用synchronzied关键字定义的静态方法或非静态方法，但这并不好理解。而如果使用synchronized块来达到同样的效果，就不难理解为什么会产生这种效果了。如果想使用synchronized块来锁定类中所有的同步非静态方法，需要使用this做为synchronized块的参数传入synchronized块国，代码如下：</p>&nbsp;&nbsp;<p><strong>通过</strong><strong>synchronized</strong><strong>块同步非静态方法&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></strong></p>&nbsp;&nbsp;<p>001 public class SyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 synchronized(this)&nbsp; // 相当于对method1方法使用synchronized关键字&nbsp;&nbsp;&nbsp;&nbsp; <br />006&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />008&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />010 public void method2()&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />012 synchronized(this)&nbsp; // 相当于对method2方法使用synchronized关键字&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />014&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017 public synchronized void method3()&nbsp;&nbsp; <br />018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021&nbsp; &#125;</p>&nbsp;&nbsp;<p>在上面的代码中的method1和method2方法中使用了synchronized块。而第017行的method3方法仍然使用synchronized关键字来定义方法。在使用同一个SyncBlock类实例时，这三个方法只要有一个正在执行，其他两个方法就会因未获得同步锁而被阻塞。在使用synchronized块时要想达到和synchronized关键字同样的效果，必须将所有的代码都写在synchronized块中，否则，将无法使当前方法中的所有代码和其他的方法同步。</p>&nbsp;&nbsp;<p>除了使用this做为synchronized块的参数外，还可以使用SyncBlock.this作为synchronized块的参数来达到同样的效果。</p>&nbsp;&nbsp;<p>在内类（InnerClass）的方法中使用synchronized块来时，this只表示内类，和外类(OuterClass)没有关系。但内类的非静态方法可以和外类的非静态方法同步。如在内类InnerClass中加一个method4方法，并使method4方法和SyncBlock的三个方法同步，代码如下：</p>&nbsp;&nbsp;<p><strong>使内类的非静态方法和外类的非静态方法同步</strong></p>&nbsp;&nbsp;<p>public class SyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; class InnerClass&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public void method4()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(SyncBlock.this)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125;</p>&nbsp;&nbsp;<p>在上面SyncBlock类的新版本中，InnerClass类的method4方法和SyncBlock类的其他三个方法同步，因此，method1、method2、method3和method4四个方法在同一时间只能有一个方法执行。</p>&nbsp;&nbsp;<p>Synchronized块不管是正常执行完，还是因为程序出错而异常退出synchronized块，当前的synchronized块所持有的同步锁都会自动释放。因此，在使用synchronized块时不必担心同步锁的释放问题。</p>&nbsp;&nbsp;<p><strong>二、静态类方法的同步</strong></p>&nbsp;&nbsp;<p>由于在调用静态方法时，对象实例不一定被创建。因此，就不能使用this来同步静态方法，而必须使用Class对象来同步静态方法。代码如下：</p>&nbsp;&nbsp;<p><strong>通过</strong><strong>synchronized</strong><strong>块同步静态方法</strong></p>&nbsp;&nbsp;<p>public class StaticSyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(StaticSyncBlock.class)&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static synchronized void method2()&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip; &hellip;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>在同步静态方法时可以使用类的静态字段class来得到Class对象。在上例中method1和method2方法同时只能有一个方法执行。除了使用class字段得到Class对象外，还可以使用实例的getClass方法来得到Class对象。上例中的代码可以修改如下：</p>&nbsp;&nbsp;<p><strong>使用</strong><strong>getClass</strong><strong>方法得到</strong><strong>Class</strong><strong>对象</strong></p>&nbsp;&nbsp;<p>public class StaticSyncBlock&nbsp;&nbsp;&nbsp;&nbsp;<br />&#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static StaticSyncBlock instance;&nbsp; <br />&nbsp;&nbsp;&nbsp; public StaticSyncBlock()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; instance = this;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; public static void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(instance.getClass())&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&#125; </p>&nbsp;&nbsp;<p>在上面代码中通过一个public的静态instance得到一个StaticSyncBlock类的实例，并通过这个实例的getClass方法得到了Class对象（一个类的所有实例通过getClass方法得到的都是同一个Class对象，因此，调用任何一个实例的getClass方法都可以）。我们还可以通过Class对象使不同类的静态方法同步，如Test类的静态方法method和StaticSyncBlock类的两个静态方法同步，代码如下：</p>&nbsp;&nbsp;<p><strong>Test</strong><strong>类的</strong><strong>method</strong><strong>方法和</strong><strong>StaticSyncBlock</strong><strong>类的</strong><strong>method1</strong><strong>、</strong><strong>method2</strong><strong>方法同步</strong></p>&nbsp;&nbsp;<p>public class Test&nbsp;&nbsp;&nbsp;&nbsp;<br />&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />public static void method()&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />synchronized(StaticSyncBlock.class)&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br /><img src="http://www.blogjava.net/Images/dot.gif" border="0" /> <img src="http://www.blogjava.net/Images/dot.gif" border="0" />&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />&nbsp;&nbsp; &#125;</p>&nbsp;&nbsp;<p>注意：在使用synchronized块同步类方法时，非静态方法可以使用this来同步，而静态方法必须使用Class对象来同步。它们互不影响。当然，也可以在非静态方法中使用Class对象来同步静态方法。但在静态方法中不能使用this来同步非静态方法。这一点在使用synchronized块同步类方法时应注意。</p>&nbsp;&nbsp;<p>&nbsp;</p>&nbsp;&nbsp;<p><strong>第十二节 使用Synchronized块同步变量</strong></p>&nbsp;&nbsp;<p>我们可以通过synchronized块来同步特定的静态或非静态方法。要想实现这种需求必须为这些特性的方法定义一个类变量，然后将这些方法的代码用synchronized块括起来，并将这个类变量作为参数传入synchronized块。下面的代码演示了如何同步特定的类方法：</p>&nbsp;&nbsp;<p>001 package mythread;&nbsp;&nbsp;&nbsp;&nbsp;<br />002&nbsp;&nbsp;&nbsp;&nbsp; <br />003 public class SyncThread extends Thread&nbsp;&nbsp;&nbsp;&nbsp; <br />004&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />005 private static String sync = &quot;&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />006 private String methodType = &quot;&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />007&nbsp;&nbsp;&nbsp;&nbsp; <br />008 private static void method(String s)&nbsp;&nbsp;&nbsp;&nbsp; <br />009&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />010 synchronized (sync)&nbsp;&nbsp;&nbsp;&nbsp; <br />011&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />012&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sync = s;&nbsp;&nbsp;&nbsp;&nbsp; <br />013&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(s);&nbsp;&nbsp;&nbsp;&nbsp; <br />014 while (true);&nbsp;&nbsp;&nbsp;&nbsp; <br />015&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />016&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />017 public void method1()&nbsp;&nbsp;&nbsp;&nbsp; <br />018&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />019&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;method1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />020&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />021 public static void staticMethod1()&nbsp;&nbsp;&nbsp;&nbsp; <br />022&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />023&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method(&quot;staticMethod1&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />024&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />025 public void run()&nbsp;&nbsp;&nbsp;&nbsp; <br />026&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />027 if (methodType.equals(&quot;static&quot;))&nbsp;&nbsp;&nbsp;&nbsp; <br />028&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; staticMethod1();&nbsp;&nbsp;&nbsp;&nbsp; <br />029 else if (methodType.equals(&quot;nonstatic&quot;))&nbsp;&nbsp;&nbsp;&nbsp; <br />030&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; method1();&nbsp;&nbsp;&nbsp;&nbsp; <br />031&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />032 public SyncThread(String methodType)&nbsp;&nbsp;&nbsp;&nbsp; <br />033&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />034 this.methodType = methodType;&nbsp;&nbsp;&nbsp;&nbsp; <br />035&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />036 public static void main(String[] args) throws Exception&nbsp;&nbsp;&nbsp;&nbsp; <br />037&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#123;&nbsp;&nbsp;&nbsp;&nbsp; <br />038 SyncThread sample1 = new SyncThread(&quot;nonstatic&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />039 SyncThread sample2 = new SyncThread(&quot;static&quot;);&nbsp;&nbsp;&nbsp;&nbsp; <br />040&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample1.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />041&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; sample2.start();&nbsp;&nbsp;&nbsp;&nbsp; <br />042&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &#125;&nbsp;&nbsp;&nbsp;&nbsp; <br />043&nbsp; &#125;</p>&nbsp;&nbsp;<p>运行结果如下：</p>&nbsp;&nbsp;<p>method1&nbsp;&nbsp;&nbsp;&nbsp;<br />staticMethod1</p>&nbsp;&nbsp;<p>看到上面的运行结果很多读者可能感到惊奇。在上面的代码中method1和staticMethod1方法使用了静态字符串变量sync进行同步。这两个方法只能有一个同时执行，而这两个方法都会执行014行的无限循环语句。因此，输出结果只能是method1和staticMethod1其中之一。但这个程序将这两个字符串都输出了。</p>&nbsp;&nbsp;<p>出现这种结果的愿意很简单，我们看一下012行就知道了。原来在这一行将sync的值改变了。在这里要说一下Java中的String类型。String类型和Java中其他的复杂类型不同。在使用String型变量时，只要给这个变量赋一次值，Java就会创建个新的String类型的实例。如下面的代码所示：</p>&nbsp;&nbsp;<p>String s = &quot;hello&quot;;&nbsp;&nbsp;&nbsp;&nbsp;<br />System.out.println(s.hashCode());&nbsp;&nbsp;&nbsp;&nbsp; <br />s = &quot;world&quot;;&nbsp;&nbsp;&nbsp;&nbsp; <br />System.out.println(s.hashCode());&nbsp; </p>&nbsp;&nbsp;<p>在上面的代码中。第一个s和再次赋值后的s的hashCode的值是不一样的。由于创建String类的实例并不需要使用new，因此，在同步String类型的变量时要注意不要给这个变量赋值，否则会使变量无法同步。</p>&nbsp;&nbsp;<p>由于在013行已经为sync创建了一个新的实例，假设method1先执行，当method1方法执行了013行的代码后，sync的值就已经不是最初那个值了，而method1方法锁定的仍然是sync变量最初的那个值。而在这时，staticMethod1正好执行到synchronized(sync)，在staticMethod1方法中要锁定的这个sync和method1方法锁定的sync已经不是一个了，因此，这两个方法的同步性已经被破坏了。</p>&nbsp;&nbsp;<p>解决以上问题的方法当然是将013行去掉。在本例中加上这行，只是为了说明使用类变量来同步方法时如果在synchronized块中将同步变量的值改变，就会破坏方法之间的同步。为了彻底避免这种情况发生，在定义同步变量时可以使用final关键字。如将上面的程序中的005行可改成如下形式：</p>&nbsp;&nbsp;<p>private final static String sync = &quot;&quot;;</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 使用final关键字后，sync只能在定义时为其赋值，并且以后不能再修改。如果在程序的其他地方给sync赋了值，程序就无法编译通过。在Eclipse等开发工具中，会直接在错误的地方给出提示。</p>&nbsp;&nbsp;<p>&nbsp;&nbsp;&nbsp; 我们可以从两个角度来理解synchronized块。如果从类方法的角度来理解，可以通过类变量来同步相应的方法。如果从类变量的角度来理解，可以使用synchronized块来保证某个类变量同时只能被一个方法访问。不管从哪个角度来理解，它们的实质都是一样的，就是利用类变量来获得同步锁，通过同步锁的互斥性来实现同步。</p>&nbsp;&nbsp;<p>注意：在使用synchronized块时应注意，synchronized块只能使用对象作为它的参数。如果是简单类型的变量(如int、char、boolean等)，不能使用synchronized来同步。</p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/java/" rel="tag">java</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25BA%25BF%25E7%25A8%258B/" rel="tag">线程</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/169/</link>
<title><![CDATA[ORZ……网卡极限了？]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Mon, 27 Apr 2009 05:42:57 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/169/</guid> 
<description>
<![CDATA[ 
	<p>看图说话<br/><a href="http://www.flickr.com/photos/31883702@N08/3479424394/" title="Flickr 上 BennyFeng 的 10M"><img src="http://farm4.static.flickr.com/3368/3479424394_be774d79c8_o.jpg" alt="10M" height="72" width="123"/></a></p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/168/</link>
<title><![CDATA[Java中的字符集编码入门]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[IT杂烩]]></category>
<pubDate>Wed, 24 Dec 2008 07:20:36 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/168/</guid> 
<description>
<![CDATA[ 
	<p><strong style="FONT-SIZE: 24px">（一）Unicode与UCS的历史恩怨<br/></strong> <strong>ASCII及相关标准<br/></strong> 地球人都知道ASCII就是美国标准信息交换码的缩写，也知道ASCII规定用7位二进制数字来表示英文字符，ASCII被定为国际标准之后的代号为 ISO-646。由于ASCII码只使用了7个二进制位，也就是说一个字节可以表示的256个数字中，它仅使用了0~127这128个码位，剩下的128 个码位便可以用来做扩展，用来表示一些特定语言所独有的字符，因此对这多余的128个码位的不同扩展，就形成了一系列ISO-8859-*的标准。例如为英语作了专门扩展的字符集编码标准编号为ISO-8859-1，也叫做Latin-1，为希腊语所作的扩展编号为ISO-8859-7等，完整的列表可以参考《Java Internationalization》一书。<br/><strong>Unicode与UCS<br/></strong> 整个Unicode项目是由多家计算机软件公司，还包括一些出版行业的公司共同发起的，从上世纪八十年代就已经开始。地球人都知道，对于日文，汉字来说，256个码位是远远不够用的（当然，在当时并不是地球人都知道，起码设计计算机的老美们就不知道，甚至直到今天，还有老美以为米国是世界上唯一的国家）。解决方法很直观也很明显，那就是采用码位多到足够包含所需字符数量的编码方案（即俗话说的头痛医头，脚痛医脚嘛）。这也是Unicode的目标之一，能够包含世界上所有语言的字符（包括汉字，日文，数学符号，音乐符号，还包括各种奇奇怪怪看也看不懂的东西比如象形文字，甲骨文，三个代表，科学发展观等等，笑），这个理想，可以说很远大，但很快被发现仅靠Unicode原先的设计无法实现。Unicode的另一个设计目标，对今天影响深远，那就是对所有字符都采用16位编码（即用一个大小不超过2的16次方的整数数字给每个字符编号，注意从这个意义上也可以看出，Unicode 是一种编码字符集，而非字符集编码）。说这个设计目标对现今影响深远，完全不是表扬，因为到后来连Unicode的设计者也发现，16位编码仅有 65536个码位，远远不能容纳世界上所有的字符，但当意识到这个问题的时候，Unicode大部分的规范已经制定完毕，也有相当程度的普及，完全推倒重来是不现实的。这成了一个遗留问题，也是surrogate pair这种蹩脚解决方案的发端。<br/>无独有偶，在1984年，喜欢以繁多的编号糊弄群众的国际标准化组织ISO也开始着手制定解决不同语言字符数量太大问题的解决方案，这一方案被称为 Universal Character Set（UCS），正式的编号是ISO-10646（记得么，ASCII是ISO-646，不知这种安排是否是故意的）。还是ISO高瞻远瞩，一开始就确定了UCS是一个31位的编码字符集（即用一个大小不超过2的31次方的整数数字为每个字符编号），这回真的足以容纳古往今来所有国家，所有语言所包含的字符了（是的，任何国家，任何小语种都包括，也不管这些国家是与台湾建交还是与中国大陆建交，是拥护民主制度还是实行恐怖主义，所以说科学无国界）。虽然后来他们意识到，2的31次方个码位又实在太多了……<br/>天下大势，分久必合。无论Unicode还是UCS，最初的目的都是杜绝各种各样名目繁多形式各异互不兼容老死不相往来的私用扩展编码（好啰嗦的一句话），结果两方确立标准的同时（最初时这两个标准是不兼容的），又形成了割据，这对建设和谐社会是不利的，违反当今世界和平与发展的主旋律，中国政府一向反对任何形式的霸权主义和强权政治，对以米国为首的发达国家……扯远了扯远了。1991年，Unicode联盟与ISO的工作组终于开始讨论 Unicode与UCS的合并问题，虽然其后的合并进行了很多年，Unicode初版规范中的很多编码都需要被改写，UCS也需要对码空间的使用进行必要限制，但成果是喜人的。最终，两者统一了抽象字符集（即任何一个在Unicode中存在的字符，在UCS中也存在），且最靠前的65535个字符也统一了字符的编码。对于码空间，两者同意以一百一十万为限（即两者都认为虽然65536不够，但2的31次方又太大，一百一十万是个双方都可接受的码空间大小，也够用，当然，这里说的一百一十万只是个约数），Unicode将码空间扩展到了一百一十万，而UCS将永久性的不使用一百一十万以后的码位。也就是说，现在再讲Unicode只包含65536个字符是不对的。除了对已经定义的字符进行统一外，Unicode联盟与ISO工作组也同意今后任何的扩展工作两者均保持同步，因此虽然从历史的意义上讲Unicode与UCS不是一回事（甚至细节上说也不是一回事），但现在提起Unicode，指代两者均无不妥。</p><p><strong style="FONT-SIZE: 24px">（二）编码字符集与字符集编码的区别<br/></strong> 需要再一次强调的是，无论历史上的UCS还是现如今的Unicode，两者指的都是<span style="COLOR: #ff0000">编码字符集</span>，而不是<span style="COLOR: #ff0000">字符集编码</span>。花费一点时间来理解好这件事，然后你会发现对所有网页的，系统的，编码标准之间的来回转换等等繁杂事务都会思路清晰，手到擒来。<br/>首先说说最一般意义上的字符集。<br/>一个<span style="COLOR: #0080ff">抽象字符集</span>其实就是指字符的集合，例如所有的英文字母是一个抽象字符集，所有的汉字是一个抽象字符集，当然，把全世界所有语言的符号都放在一起，也可以称为一个抽象字符集，所以这个划分是相当人为的。之所以说"抽象"二字，是因为这里所提及的字符不是任何具体形式的字符，拿汉字中的"汉"这个字符来说，您在这篇文章中看到的这个"汉"其实是这个字符的一种具体表现形式，是它的图像表现形式，而且它是用中文（而非拼音）书写而成，使用宋体外观；而当人们用嘴发出"汉"这个音的时候，他们是在使用"汉"的另一种具体表现形式--声音，但无论如何，两者所指的字符都是"汉"这个字。同一个字符的表现形式可能有无数种（点阵表示，矢量表示，音频表示，楷体，草书等等等等），把每一种表现形式下的同一个字符都纳入到字符集中，会使得集合过于庞大，冗余高，也不好管理。因此抽象字符集中的字符，都是指唯一存在的抽象字符，而忽略它的具体表现形式。<br/>抽象字符集中的诸多字符，没有顺序之分，谁也不能说哪个字符在哪个字符前面，而且这种抽象字符只有人能理解。在给一个抽象字符集合中的每个字符都分配一个整数编号之后（注意这个整数并没有要求大小），这个字符集就有了顺序，就成为了<span style="COLOR: #0080ff">编码字符集</span>。同时，通过这个编号，可以唯一确定到底指的是哪一个字符。当然，对于同一个字符，不同的字符集编码系统所制定的整数编号也不尽相同，例如"儿"这个字，在 Unicode中，它的编号是0x513F，（为方便起见，以十六进制表示，但这个整数编号并不要求必须是以十六进制表示）意思是说它是Unicode这个编码字符集中的第0x513F个字符。而在另一种编码字符集比如Big5中，这个字就是第0xA449个字符了。这种情况的另一面是，许多字符在不同的编码字符集中被分配了相同的整数编号，例如英文字母"A"，在ASCII及Unicode中，均是第0x41个字符。我们常说的Unicode字符集，指的就是这种被分配了整数编号的字符集合，但要澄清的是，编码字符集中字符被分配的整数编号，不一定就是该字符在计算机中存储时所使用的值，计算机中存储的字符到底使用什么二进制整数值来表示，是由下面将要说到的 <span style="COLOR: #0080ff">字符集编码</span>决定的。<br/>字符集编码决定了如何将一个字符的整数编号对应到一个二进制的整数值，有的编码方案简单的将该整数值直接作为其在计算机中的表示而存储，例如英文字符就是这样，几乎所有的字符集编码方案中，英文字母的整数编号与其在计算机内部存储的二进制形式都一致。但有的编码方案，例如适用于Unicode字符集的 UTF-8编码形式，就将很大一部分字符的整数编号作了变换后存储在计算机中。以"汉"字为例，"汉"的Unicode值为0x6C49，但其编码为 UTF-8格式后的值为0xE6B189（注意到变成了三个字节）。这里只是举个例子，关于UTF-8的详细编码规则可以参看《Mapping codepoints to Unicode encoding forms》一文，URL为<a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-AppendixA#sec3"><a href="http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&amp;item_id=IWS-AppendixA#sec3" target="_blank">http://scripts.sil.org/cms...</a></a>。<br/>我们经常听说的另一种编码方案UTF-16,则对Unicode中的前65536个字符编号都不做变换，直接作为计算机存储时使用的值（对65536以后的字符，仍然要做变换），例如"汉"字的Unicode编号为0x6C49，那么经过UTF-16编码后存储在计算机上时，它的表示仍为0x6C49！。我猜，正是因为UTF-16的存在，使得很多人认为Unicode是一种编码（实际上，是一个字符集，再次重申），也因此，很多人说Unicode的时候，他们实际上指的是UTF-16。UTF-16提供了surrogate　pair机制，使得Unicode中码位大于65536的那些字符得以表示。<br/>Surrogate pair机制在目前来说实在不常用，甚至连一些UTF-16的实现都不支持，所以我不打算在这里多加讨论，其基本的思想就是用两个16位的编码表示一个字符（注意，只对码位超过65536的字符这么做）。Unicode如此死抱着16这个数字不放，有历史的原因，也有实用的原因。<br/>当然还有一种最强的编码，UTF-32,他对所有的Unicode字符均不做变换，直接使用编号存储！（俗称的以不变应万变），只是这种编码方案太浪费存储空间（就连1个字节就可以搞定的英文字符，它都必须使用4个字节），因而尽管使用起来方便（不需要任何转换），却没有得到普及。<br/>记得当初Unicode与UCS还没成家之时，UCS也是需要人爱，需要人疼的，没有自己的字符集编码怎么成。UCS-2与UCS-4就扮演了这样的角色。UCS-4与UTF-32除了名字不同以外，思想完全一样。而UCS-2与UTF-16在对前65536个字符的处理上也完全相同，唯一的区别只在于 UCS-2 不支持surrogate pair机制，即是说，UCS-2只能对前65536个字符编码，对其后的字符毫无办法。不过现在再谈起字符编码的时候，UCS-2与UCS-4早已成为计算机史学家才会用到的词汇，就让它们继续留在故纸堆里吧。<br/>下一节我们来说说与中文相关的GB2312和GBK。</p><p><strong style="FONT-SIZE: 24px">（三）GB2312，GBK与中文网页</strong><br/>GB2312是对中国的开发人员来说很重要的一个词汇，它的来龙去脉并不需要我在这里赘述，随便Goolge之便明白无误。我只是想提一句，记得前一节说到编码字符集和字符集编码不是一回事，而有的字符集编码又实际上没有做任何事，GB2312正是这样一种东西！<br/>GB2312最初指的是一个编码字符集，其中包含了ASCII所包含的英文字符，同时加入了6763个简体汉字以及其他一些ASCII之外的符号。与 Unicode有UTF-8和UTF-16一样（当然， UTF-8和UTF-16也没有被限定只能用来对Unicode进行编码，实际上，你用它对视频进行编码都是可以的，只是编出的文件没有播放器支持罢了，哈哈），GB2312也有自己的编码方案，但这个方案直接使用一个字符在GB2312中的编号作为存储值（与UTF-32的做法类似），也因此，这个编码方案甚至没有正式的名称。我们日常说起GB2312的时候，常常即指这个字符集，也指这种编码方案。<br/>GBK是GB2312的后续标准，添加了更多的汉字和特殊符号，类似的是，GBK也是同时指他的字符集和他的编码。<br/>GBK还是现如今中文Windows操作系统的系统默认编码（这正是几乎所有网页上的，文件里的乱码问题的根源）。<br/>我们可以这样来验证，使用以下的Java代码：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,0)">String encoding</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">System.getProperty(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">file.encoding</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">);<br/>System.out.println(encoding);</span></div><p>输出结果为GBK<br/>（什么？你的输出不是这样？怎么可能？完了，我的牌子要砸了，等等，你用的繁体版XP？我说你这同志在这里捣什么乱？去！去！）<br/>说到GB2312和GBK就不得不提中文网页的编码。尽管很多新开发的Web系统和新上线的注重国际化的网站都开始使用UTF-8，仍有相当一部分的中文媒体坚持使用GB2312和GBK，例如新浪的页面。其中有两点很值得注意。<br/>第一，页面中meta标签的部分，常常可以见到<br/>charset=GB2312<br/>这样的写法，很不幸的是，这个"charset"其实是用来指定页面使用的是什么字符集编码，而不是使用什么字符集。例如你见到过有人写 "charset=UTF-8"，见到过有人写"charset=ISO-8859-1"，但你见过有人写"charset=Unicode"么？当然没有，因为Unicode是一个字符集，而不是编码。<br/>然而正是charset这个名称误导了很多程序员，真的以为这里要指定的是字符集，也因而使他们进一步的误以为UTF-8和UTF-16是一种字符集！（万恶啊）好在XML中已经做出了修改，这个位置改成了正确的名称：encoding。<br/>第二，页面中说的GB2312，实际上并不真的是GB2312（惊讶么？）。我们来做个实验，例如找一个GB2312中不存在的汉字"亸"（这个字确实不在GB2312中，你可以到GB2312的码表中去找，保证找不到），这个字在GBK中。然后你把它放到一个html页面中，试着在浏览器中打开它，然后选择浏览器的编码为"GB2312"，看到了什么？它完全正常显示！<br/>结论不用我说你也明白了，浏览器实际上使用的是GBK来显示。<br/>新浪的页面中也有很多这样的例子，到处都写charset=GB2312，却使用了无数个GB2312中并不存在的字符。这种做法对浏览器显示页面并不成问题，但在需要程序抓取页面并保存的时候带来了麻烦，程序将不能依据页面所"声称"的编码进行读取和保存，而只能尽量猜测正确的编码。</p><p><strong style="FONT-SIZE: 24px">（四）网页文件的编码</strong><br/>接着上节的思路说，一个网页要想在浏览器中能够正确显示，需要在三个地方保持编码的一致：网页文件，网页编码声明和浏览器编码设置。<br/>首先是网页文件本身的编码，即网页文件在被创建的时候使用什么编码来保存。这个完全取决于创建该网页的人员使用了什么编码保存，而进一步的取决于该人员使用的操作系统。例如我们使用的中文版WindowsXP系统，当你新建一个文本文件，写入一些内容，并按下ctrl+s进行保存的那一刻，操作系统就替你使用GBK编码将文件进行了保存（没有使用UTF-8，也没有使用UTF-16）。而使用了英文系统的人，系统会使用ISO-8859-1进行保存，这也意味着，在英文系统的文件中如果输入一个汉字，是无法进行保存的（当然，你甚至都无法输入）。<br/>一个在创建XML文件时（创建HTML的时候倒很少有人这么做）常见的误解是以为只要在页面的encoding部分声明了UTF-8，则文件就会被保存为 UTF-8格式。这实在是……怎么说呢，不能埋怨大家。实际上XML文件中encoding部分与HTML文件中的charset中一样，只是告诉"别人 "（这个别人可能是浏览你的页面的人，可能是浏览器，也可能是处理你页面的程序，别人需要知道这个，因为除非你告诉他们，否则谁也猜不出你用了什么编码，仅通过文件的内容判断不出使用了什么编码，这是真的）这个文件使用了什么编码，唯独操作系统不会搭理，它仍然会按自己默认的编码方式保存文件（再一次的，在我们的中文WindowsXP系统中，使用GBK保存）。至于这个文件是不是真的是encoding或者charset所声明的那种编码保存的呢？答案是不一定！<br/>例如新浪的页面就"声称"他是用GB2312编码保存的，但实际上却是GBK，也有无数的二把刀程序员用系统默认的GBK保存了他们的XML文件，却在他们的encoding中信誓旦旦的说是UTF-8的。<br/>这就是我们所说的第二个位置，网页编码声明中的编码应该与网页文件保存时使用的编码一致。<br/>而浏览器的编码设置实际上并不严格，就像我们第三节所说的那样，在浏览器中选择使用GB2312来查看，它实际上仍然会使用GBK进行。而且浏览器还有这样一种好习惯，即它会尽量猜测使用什么编码查看最合适。<br/>我要重申的是，网页文件的编码和网页文件中声明的编码保持一致，这是一个极好的建议（值得遵循，会与人方便，与己方便），但如果不一致，只要网页文件的编码与浏览器的编码设置一致，也是可以正确显示的。<br/>例如有这样一个页面，它使用GBK保存，但声明自己是UTF-8的。这个时候用浏览器打开它，首先会看到乱码，因为这个页面"告诉"浏览器用UTF-8显示，浏览器会很尊重这个提示，于是乱码一片。但当手工把浏览器设为GBK之后，显示正常。<br/>说了以上四节这么多，后面我们就来侃侃Java里的字符编码，你会发现有意思且挠头的事情很多，但一旦弄通，天下无敌（不过不要像东方不败那样才好）。</p><p><span style="FONT-SIZE: 24px"><strong>（五）Java代码中的字符编码转换Part 1</strong></span><br/>如果你是JVM的设计者，让你来决定JVM中所有字符的表示形式，你会不会允许使用各种编码方式的字符并存？<br/>我想你的答案是不会，如果在内存中的Java字符可以以GB2312,UTF-16,BIG5等各种编码形式存在，那么对开发者来说，连进行最基本的字符串打印、连接等操作都会寸步难行。例如一个GB2312的字符串后面连接一个UTF-8的字符串，那么连接后的最终结果应该是什么编码的呢？你选哪一个都没有道理。<br/>因此牢记下面这句话，这也是Java开发者的共同意志：在Java中，字符只以一种形式存在，那就是Unicode（注意到我们没有选择特定的编码，直接使用它们在字符集中的编号，这是统一的唯一方法）。<br/>但"在Java中"到底是指在哪里呢？就是指在JVM中，在内存中，在你的代码里声明的每一个char，String类型的变量中。例如你在程序中这样写</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">;</span></div><p>在内存的相应区域，这个字符就表示为0x6C49。可以用下面的代码证明一下：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">'</span> <span style="COLOR: rgb(0,0,0)">;<br/>System.out.format(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">%x</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">,(</span> <span style="COLOR: rgb(0,0,255)">short</span> <span style="COLOR: rgb(0,0,0)">)han);</span></div><p>输出是：6c49<br/>反过来用Unicode编号来指定一个字符也可以，像这样：</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">han</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,0)">0x6c49</span> <span style="COLOR: rgb(0,0,0)">;<br/>System.out.println(han);</span></div><p>输出是：汉<br/>这其实也是说，只要你正确的读入了"汉"这个字，那么它在内存中的表示形式一定是0x6C49，没有任何其他的值能代表这个字（当然，如果你读错了，那结果是什么就不知道了，范伟说：读，读错了呀，那还等于好几亿呢；本山大哥说：好几亿你也没答上，请听下一题）。<br/>JVM的这种约定使得一个字符存在的世界分为了两部分：JVM内部和OS的文件系统。在JVM内部，统一使用Unicode表示，当这个字符被从JVM内部移到外部（即保存为文件系统中的一个文件的内容时），就进行了编码转换，使用了具体的编码方案（也有一种很特殊的情况，使得在JVM内部也需要转换，不过这个是后话）。<br/>因此可以说，所有的编码转换就只发生在边界的地方，JVM和OS的交界处，也就是你的各种输入输出流（或者Reader，Writer类）起作用的地方。<br/>话头扯到这里就必须接着说Java的IO系统。<br/>尽管看上去混乱繁杂，但是所有的IO基本上可以分为两大阵营：面向字符的Reader啊Wrtier啊，以及面向字节的输入输出流。<br/>下面我来逐一分解，其实一点也不难。<br/>面向字符和面向字节中的所谓"面向"什么，是指这些类在处理输入输入的时候，在哪个意义上保持一致。如果面向字节，那么这类工作要保证系统中的文件二进制内容和读入JVM内部的二进制内容要一致。不能变换任何0和1的顺序。因此这是一种非常"忠实于原著"的做法（偶然间让我想起郭敬明抄袭庄羽的文章，那家伙，太忠实于原著了，笑）。<br/>这种输入输出方式很适合读入视频文件或者音频文件，或者任何不需要做变换的文件内容。<br/>而面向字符的IO是指希望系统中的文件的字符和读入内存的"字符"（注意和字节的区别）要一致。例如我们的中文版WindowsXP系统上有一个GBK的文本文件，其中有一个"汉"字，这个字的GBK编码是0xBABA（而Unicode编号是0x6C49），当我们使用面向字符的IO把它读入内存并保存在一个char型变量中时，我希望IO系统不要傻傻的直接把0xBABA放到这个char型变量中，我甚至都不关心这个char型变量具体的二进制内容到底是多少，我只希望这个字符读进来之后仍然是"汉"这个字。<br/>从这个意义上也可以看出，面向字符的IO类，也就是Reader和Writer类，实际上隐式的为我们做了编码转换，在输出时，将内存中的Unicode 字符使用系统默认的编码方式进行了编码，而在输入时，将文件系统中已经编码过的字符使用默认编码方案进行了还原。我两次提到"默认"，是说Reader和 Writer的聪明也仅此而已了，它们只会使用这个默认的编码来做转换，你不能为一个Reader或者Writer指定转换时使用的编码。这也意味着，如果你使用中文版WindowsXP系统，而上面存放了一个UTF-8编码的文件，当你使用Reader类来读入的时候，它会傻傻的使用GBK来做转换，转换后的内容当然驴唇不对马嘴！<br/>这种笨，有时候其实是一种傻瓜式的功能提供方式，对大多数初级用户（以及不需要跨平台的高级用户）来说反而是件好事。<br/>但我们不一样啦，我们都是国家栋梁，肩负着赶英超美的责任，必须师夷长技以治夷，所以我们总还要和GBK编码以外的文件打交道。<br/>说了上面这些内容，想必聪明的读者已经看出来，所谓编码转换就是一个字符与字节之间的转换，因此Java的IO系统中能够指定转换编码的地方，也就在字符与字节转换的地方，那就是（读者：InputSteamReader和OutputStreamWriter！作者：太强了，都会抢答了！）<br/>这两个类是字节流和字符流之间的适配器类，因此他们肩负着编码转换的任务简直太自然啦！要注意，实际上也只能在这两类实例化的时候指定编码，是不是很好记呢？<br/>下面来写一段小程序，来把"汉"字用我们非常崇拜的UTF-8编码写到文件中！</p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><span style="COLOR: rgb(0,0,255)">try</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>PrintWriter out</span> <span style="COLOR: rgb(0,0,0)">=</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">PrintWriter(</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">OutputStreamWriter(</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">FileOutputStream(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">c:/utf-8.txt</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">),</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">UTF-8</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">));<br/></span> <span style="COLOR: rgb(0,0,255)">try</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>out.write(</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">汉</span> <span style="COLOR: rgb(0,0,0)">"</span> <span style="COLOR: rgb(0,0,0)">);<br/>&#125;</span> <span style="COLOR: rgb(0,0,255)">finally</span> <span style="COLOR: rgb(0,0,0)">&#123;<br/>out.close();<br/>&#125;<br/>&#125;</span> <span style="COLOR: rgb(0,0,255)">catch</span> <span style="COLOR: rgb(0,0,0)">(IOException e)&#123;<br/></span> <span style="COLOR: rgb(0,0,255)">throw</span> <span style="COLOR: rgb(0,0,255)">new</span> <span style="COLOR: rgb(0,0,0)">RuntimeException(e);<br/>&#125;</span></div><p>运行之后到c盘下去找utf-8.txt这个文件，用UltraEdit打开，使用16进制查看，看到了什么？它的值是0xE6B189！噢耶！（读者：这，这有什么好高兴的……）<br/>下一节我们来看看实现这种操作的其他方式，读到这里，你已经基本上是字符编码的高手了哦。</p><p><strong style="FONT-SIZE: 24px">（六）Java中的增补字符<br/></strong>Java号称对Unicode提供天然的支持，这话在很久很久以前就已经是假的了（不过曾经是真的），实际上，到JDK5.0为止Java才算刚刚跟上Unicode的脚步，开始提供对<span style="COLOR: rgb(80,157,255)">增补字符</span>的支持。<br/>现在的Unicode码空间为U+0000到U+10FFFF，一共1114112个码位，其中只有1,112,064 个码位是合法的（我来替你做算术，有2048个码位不合法），但并不是说现在的Unicode就有这么多个字符了，实际上其中很多码位还是空闲的，到 Unicode 4.0 规范为止，只有96,382个码位被分配了字符（但无论如何，仍比很多人认为的65536个字符要多得多了）。其中U+0000 到U+FFFF的部分被称为<span style="COLOR: rgb(80,157,255)">基本多语言面</span>（Basic Multilingual Plane，BMP）。U+10000及以上的字符称为补充字符。在Java中（Java1.5之后），补充字符使用两个char型变量来表示，这两个 char型变量就组成了所谓的surrogate pair（在底层实际上是使用一个int进行表示的）。第一个char型变量的范围称为"高代理部分"（high-surrogates range,从"uD800到"uDBFF，共1024个码位）, 第二个char型变量的范围称为low-surrogates range（从"uDC00到"uDFFF，共1024个码位），这样使用surrogate pair可以表示的字符数一共是1024的平方计1048576个，加上BMP的65536个码位，去掉2048个非法的码位，正好是1,112,064 个码位。<br/><br/>关于Unicode的码空间实际上有一些稍不小心就会让人犯错的地方。比如我们都知道从U+0000到U+FFFF的部分被称为基本多语言面（Basic Multilingual Plane，BMP），这个范围内的字符在使用UTF-16编码时，只需要一个char型变量就可以保存。仔细看看这个范围，应该有65536这么大，因此你会说单字节的UTF-16编码能够表示65536个字符，你也会说Unicode的基本多语言面包含65536个字符，但是再想想刚才说过的surrogate pair，一个UTF-16表示的增补字符（再一次的，需要两个char型变量才能表示的字符）怎样才能被正确的识别为增补字符，而不是两个普通的字符呢？答案你也知道，就是通过看它的第一个char是不是在高代理范围内，第二个char是不是在低代理范围内来决定，这也意味着，高代理和低代理所占的共 2048个码位（从0xD800到0xDFFF）是不能分配给其他字符的。<br/>但这是对UTF-16这种编码方法而言，而对Unicode这样的字符集呢？在Unicode的编号中，U+D800到U+DFFF是否有字符分配？答案是也没有！这是典型的字符集为方便编码方法而做的安排（你问他们这么做的目的？当然是希望基本多语言面中的字符和一个char型的UTF-16编码的字符能够一一对应，少些麻烦，从中我们也能看出UTF-16与Unicode间很深的渊源与结合）。也就是说，无论Unicode还是UTF-16编码后的字符，在0x0000至0xFFFF这个范围内，只有63488个字符。这就好比最初的CPU被勉强拿来做多媒体应用，用得多了，CPU就不得不修正自己从硬件上对多媒体应用提供支持了。<br/><br/>尽管不情愿，但说到这里总还得扯扯相关的概念：代码点和代码单元。<br/><span style="COLOR: rgb(80,157,255)">代码点</span>（Code Point）就是指Unicode中为字符分配的编号，一个字符只占一个代码点，例如我们说到字符"汉"，它的代码点是U+6C49。<br/><span style="COLOR: rgb(80,157,255)">代码单元<span style="COLOR: rgb(6,0,0)">（Code Unit）</span></span>则是针对编码方法而言，它指的是编码方法中对一个字符编码以后所占的最小存储单元。例如UTF-8中，代码单元是一个字节，因为一个字符可以被编码为1 个，2个或者3个4个字节；在UTF-16中，代码单元变成了两个字节（就是一个char），因为一个字符可以被编码为1个或2个char（你找不到比一个char还小的UTF-16编码的字符，嘿嘿）。说得再罗嗦一点，一个字符，仅仅对应一个代码点，但却可能有多个代码单元（即可能被编码为2个 char）。<br/>以上概念绝非学术化的绕口令，这意味着当你想以一种统一的方式指定自己使用什么字符的时候，使用代码点（即你告诉你的程序，你要用Unicode中的第几个字符）总是比使用代码单元更好（因为这样做的话你还得区分情况，有时候提供一个16进制数字，有时候要提供两个）。<br/>例如我们有一个增补字符？？？（哈哈，你看到了三个问号对吧？因为我的系统显示不出这个字符），它在Unicode中的编号是U+2F81A，当在程序中需要使用这个字符的时候，就可以这样来写：<br/></p><div style="BORDER-BOTTOM: rgb(204,204,204) 1px solid; BORDER-LEFT: rgb(204,204,204) 1px solid; PADDING-BOTTOM: 4px; BACKGROUND-COLOR: rgb(238,238,238); PADDING-LEFT: 4px; WIDTH: 98%; PADDING-RIGHT: 5px; FONT-SIZE: 13px; BORDER-TOP: rgb(204,204,204) 1px solid; BORDER-RIGHT: rgb(204,204,204) 1px solid; PADDING-TOP: 4px"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br /><a href="http://www.CodeHighlighter.com/" target="_blank">http://www.CodeHighlighter...</a><br /><br />--><span style="COLOR: rgb(0,0,0)">String s</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">String.valueOf(Character.toChars(</span><span style="COLOR: rgb(0,0,0)">0x2F81A</span><span style="COLOR: rgb(0,0,0)">));<br/></span><span style="COLOR: rgb(0,0,255)">char</span><span style="COLOR: rgb(0,0,0)">[]chars</span><span style="COLOR: rgb(0,0,0)">=</span><span style="COLOR: rgb(0,0,0)">s.toCharArray();<br/></span><span style="COLOR: rgb(0,0,255)">for</span><span style="COLOR: rgb(0,0,0)">(</span><span style="COLOR: rgb(0,0,255)">char</span> <span style="COLOR: rgb(0,0,0)">c:chars)&#123;<br/>System.out.format(</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">%x</span><span style="COLOR: rgb(0,0,0)">"</span><span style="COLOR: rgb(0,0,0)">,(</span><span style="COLOR: rgb(0,0,255)">short</span><span style="COLOR: rgb(0,0,0)">)c);<br/>&#125;</span></div><p>后面的for循环把这个字符的UTF-16编码打印了出来，结果是<br/>d87edc1a<br/>注意到了吗？这个字符变成了两个char型变量，其中0xd87e就是高代理部分的值，0xdc1a就是低代理的值。</p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/167/</link>
<title><![CDATA[全景西湖]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[小特土产]]></category>
<pubDate>Wed, 03 Dec 2008 07:18:16 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/167/</guid> 
<description>
<![CDATA[ 
	<p>哈哈去年12月31日的照片现在才处理好，做了2个全景效果，都是给予微软的东西，估计观看的时候都需要装插件。<br/>基于Silverlight的：<br/><a href="http://www.newtyper.com/hznter/upload/blog/wl/wl_Deep.html" target="_blank"><a href="http://www.newtyper.com/hznter/upload/blog/wl/wl_Deep.html" target="_blank">http://www.newtyper.com/hz...</a></a><br/>基于HD View的：<br/><a href="http://www.newtyper.com/hznter/upload/blog/wl/wl_HD.html" target="_blank"><a href="http://www.newtyper.com/hznter/upload/blog/wl/wl_HD.html" target="_blank">http://www.newtyper.com/hz...</a></a><br/></p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E5%2585%25A8%25E6%2599%25AF/" rel="tag">全景</a> , <a href="http://www.newtyper.com/blog/tags/%25E8%25A5%25BF%25E6%25B9%2596/" rel="tag">西湖</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%2591%2584%25E5%25BD%25B1/" rel="tag">摄影</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/166/</link>
<title><![CDATA[脚架、快门线……]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Fri, 24 Oct 2008 03:45:26 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/166/</guid> 
<description>
<![CDATA[ 
	<p>2个月的地狱终于算度过了，20日乘休假去照相器材市场逛了下，结果……我又败了……RS60E3佳能原装快门线、百诺A-127N6套装……</p><p>下午试拍了几张，看图说话<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><table style="WIDTH: auto"><tbody><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://picasaweb.google.com/lh/photo/s_jYvX7R5hi_BhL9Ey8I6A?authkey=kVM79TxcmXU">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://lh3.ggpht.com/iciness/SQFFsUCcggI/AAAAAAAACh4/PUQWk0nMecs/s400/IMG_4386.JPG" height="274" width="400"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></tbody></table><p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><table style="WIDTH: auto"><tbody><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://picasaweb.google.com/lh/photo/QkccwjJv-oZCyNt2TkihJA?authkey=kVM79TxcmXU">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://lh6.ggpht.com/iciness/SQFFs0M5okI/AAAAAAAACh8/IHjV6l3BDPk/s400/IMG_4396.JPG" height="400" width="274"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></tbody></table>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/165/</link>
<title><![CDATA[100days达成]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Wed, 27 Aug 2008 13:57:10 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/165/</guid> 
<description>
<![CDATA[ 
	<p>自从换了linksys WRT54G v4路由器，刷了DD-WRT自制路由系统，一直稳定工作，100days持续开机无死机重启达成。几乎天天下BT，下电驴没出过啥问题。由于今天发现v24有了sp1修补版，为了升级而不得不重启了路由。<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><table style="width:auto;"><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://picasaweb.google.com/lh/photo/SzEAeYwLZyaqjHAfPGPFNA?authkey=kVM79TxcmXU">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://lh6.ggpht.com/iciness/SLVdY-jxcFI/AAAAAAAACRM/sdyi_-cwCto/s400/snap_1.png"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></table><br/>Tags - <a href="http://www.newtyper.com/blog/tags/linksys/" rel="tag">linksys</a> , <a href="http://www.newtyper.com/blog/tags/dd-wrt/" rel="tag">dd-wrt</a> , <a href="http://www.newtyper.com/blog/tags/%25E8%2587%25AA%25E5%2588%25B6%25E7%25B3%25BB%25E7%25BB%259F/" rel="tag">自制系统</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%25A8%25B3%25E5%25AE%259A/" rel="tag">稳定</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/164/</link>
<title><![CDATA[其实这篇只是测试用的]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Mon, 18 Aug 2008 13:52:49 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/164/</guid> 
<description>
<![CDATA[ 
	<p>使用了Zoundy Raven离线编辑器，顺便还搭建了以wordpress为基础的BLOG，专门用来收集记录技术文档，过段时间再开放出来。<br/>顺便测试下用Zoundy Raven上传图片<br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p><table style="width:auto;"><tr><td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<a href="http://picasaweb.google.com/lh/photo/3Lk66RPgreXpCXnujigD8w?authkey=kVM79TxcmXU">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://lh5.ggpht.com/iciness/SKmASPR7JMI/AAAAAAAABss/1YMAQm82VCs/s400/384692395_400a4cc57d_o.jpg"/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</a>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td></tr></table><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E6%25B5%258B%25E8%25AF%2595/" rel="tag">测试</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%259D%2582%25E8%25B0%2588/" rel="tag">杂谈</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/163/</link>
<title><![CDATA[西湖！第一次发现杭州的天能这么蓝！]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Sat, 09 Aug 2008 17:53:37 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/163/</guid> 
<description>
<![CDATA[ 
	<p>我第一次发现杭州的天能这么蓝！&nbsp;&nbsp;&nbsp;&nbsp;<br />今天和我家狐狸去爬山，由于近中午的一场阵雨、夏日午后的太阳加天上的白云，杭州的天也能如此的漂亮。&nbsp;&nbsp;&nbsp;&nbsp; <br /><span style="font-size: xx-small; color: #0080ff">点击查看大图&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br /></span><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570545266715874" target="_blank"><img src="http://lh5.ggpht.com/iciness/SJ3TMATLbOI/AAAAAAAABqc/2hHyE78WyZ8/s400/IMG_3557.JPG" border="0" /></a>&nbsp;&nbsp;&nbsp;&nbsp; <br /><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570499702713650" target="_blank"><img src="http://lh6.ggpht.com/iciness/SJ3TJWj37TI/AAAAAAAABp8/OR5_jngAiAw/s288/IMG_3545.JPG" border="0" /></a><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570523006576818" target="_blank"><img src="http://lh4.ggpht.com/iciness/SJ3TKtX8qLI/AAAAAAAABqE/i3ME_agm5hM/s288/IMG_3549.JPG" border="0" /></a></p>&nbsp;&nbsp;<p>顺便做了几张HDR图，和原图做个对比。</p>&nbsp;&nbsp;<p align="left">&nbsp;&nbsp; <table border="0" cellspacing="0" cellpadding="0" width="576"><tbody>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <td width="288" valign="top"><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570530033246306" target="_blank"><img src="http://lh5.ggpht.com/iciness/SJ3TLHjPIGI/AAAAAAAABqM/fOgeHtQ8DJs/s288/IMG_3554.JPG" border="0" /></a></td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<td width="288" valign="top"><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570554006866466" target="_blank"><img src="http://lh3.ggpht.com/iciness/SJ3TMg2_iiI/AAAAAAAABqk/o1O6yaM11Hw/s288/IMG_3554_HDR.JPG" border="0" /></a></td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <td width="288" valign="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p align="center">原图</p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<td width="288" valign="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p align="center">HDR</p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tr>&nbsp;&nbsp;&nbsp;&nbsp; </tbody></table>&nbsp;&nbsp;&nbsp;&nbsp;<table border="0" cellspacing="0" cellpadding="0"><tbody>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <td valign="top"><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570540300179362" target="_blank"><img src="http://lh4.ggpht.com/iciness/SJ3TLtzEF6I/AAAAAAAABqU/GgDdwQB4zHY/s288/IMG_3555.JPG" border="0" /></a></td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<td valign="top"><a href="http://picasaweb.google.com/iciness/20080809/photo#5232570571320724306" target="_blank"><img src="http://lh4.ggpht.com/iciness/SJ3TNhW8C1I/AAAAAAAABqs/nOoxpJ2ao5E/s288/IMG_3555_HDR.jpg" border="0" /></a></td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<tr>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <td valign="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p align="center">原图</p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<td valign="top">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <p align="center">HDR+柔光</p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </tr>&nbsp;&nbsp;&nbsp;&nbsp; </tbody></table></p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E8%25A5%25BF%25E6%25B9%2596/" rel="tag">西湖</a> , <a href="http://www.newtyper.com/blog/tags/%25E8%2593%259D%25E5%25A4%25A9/" rel="tag">蓝天</a> , <a href="http://www.newtyper.com/blog/tags/%25E4%25BA%2591/" rel="tag">云</a> , <a href="http://www.newtyper.com/blog/tags/hdr/" rel="tag">hdr</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/162/</link>
<title><![CDATA[一个台湾人眼中“中國大陸2008年的災難”]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[缤纷世界]]></category>
<pubDate>Wed, 21 May 2008 07:19:56 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/162/</guid> 
<description>
<![CDATA[ 
	<p>YST是一个台湾网民(眷村子弟)，也是一名难得的铁杆统派. 他的观点向来以大中华利益为出发点，虽然在台湾并非主流，却往往非常全面深入。&nbsp;&nbsp;&nbsp;&nbsp;<br /> 原帖地址：&nbsp;&nbsp;&nbsp;&nbsp; <br /><a href="http://city.udn.com/3011/2864195"><a href="http://city.udn.com/3011/2864195" target="_blank">http://city.udn.com/3011/2...</a></a>&nbsp;&nbsp;&nbsp;&nbsp;<br /> 中國大陸２００８年的災難&nbsp;&nbsp;&nbsp;&nbsp; <br /> 2008/05/16 15:00 瀏覽464｜回應15｜推薦13&nbsp;&nbsp;&nbsp;&nbsp; <br /> YST&nbsp;&nbsp;&nbsp;&nbsp; <br /> 幾乎所有關心政治的人都預料到２００８年是中國的一個難關，這並不需要什麼智慧，任何有點常識的人都能猜到七、八分。大部分的人都猜到會有人禍，但是沒有人能猜到還有天災，而且是特大天災。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 是的，2008一開年中國就流年不利，先是１月的冰雪災，然後是３月西藏喇嘛的暴亂，緊接著在巴黎和倫敦上演干擾奧運聖火傳遞的&ldquo;人權&rdquo; 表演，同時西方國家開動媒體機器發動舖天蓋地對&ldquo;中共專制暴政&rdquo;的攻擊和對&ldquo;西藏人權壓迫&rdquo;的聲討，是少見的西方媒體大合唱。最近發生的是５月１２日四川超強大地震，已經造成超過一萬九千人死亡而且數字還在增加中。現在才５月，距離８月８日的奧運會還有兩個多月，還會不斷有事件上演的，中國不可掉以輕心。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 冰雪災和地震屬於無法預料的天災，其他都是人禍，而且幾乎都可以預料到，所以一點都不奇怪。&nbsp;</p><p><br /> （一）人禍&nbsp;&nbsp;&nbsp;&nbsp; <br /> 所有的人禍都發源于今年８月將在北京舉辦的奧林匹克運動會。大陸敲鑼打鼓地準備奧運會，西方國家一定會用各種方法來掃興，如果不能攪黃它至少也要攪臭這個盛大派對。大國要來施壓，小國要來揩油，就連台灣的馬英九也來要脅，號稱不排除抵制北京奧運會。你想想，中國高調籌備奧運會，把它當成中國崛起的象徵，西方國家和東方宵小能放過嗎？這種大事引起列強趁機搗亂和宵小趁機勒索是必然的，他們不趁這個機會敲打勒索還等什麼時候？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 世界上重大的人為事件沒有一件能脫離政治運作或政治動機。想想看，中國不是新加坡，不是韓國，也不是日本，中國這個人口眾多、塊頭又大的國家一旦崛起非同小可，是震動整個世界的，連中國人想多吃一點肉都會引起美國的驚恐，宣揚會造成糧食危機。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 事實是，中國的崛起是二十一世紀最重大的事件。這是因為中國的崛起必然引發國際勢力範圍的重新劃分，也必然牽涉到每個國家的基本利益和財富分配。小國只能觀風向，大國必然主動出手，列強對中國的崛起絕對不會坐視。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 李登輝在七年前就說了，北京奧運會過後台灣獨立就不可能了，要早下手。李登輝會想到，難道疆獨、藏獨會想不到？美歐日會想不到？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國高調辦奧運會其實是給自己找麻煩，中國不是傻瓜，怎麼會看不到？但是奧運會一定要舉辦，而且要辦得比其他國家都好，辦得符合自己泱泱大國的身分，展現大國的實力。什麼叫做實力？除了中國進步的面貌還要展現能夠克服一切困難和干擾的信心和能力。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 今年３月藏獨的暴動毫無疑問是達賴喇嘛的策劃，因為藏人在西藏、四川、甘肅的暴動是互相配合與呼應的，不是單一的個別事件，顯然後面有一個操縱的組織。至於西方國家支持在奧運聖火傳遞上演的西藏人權鬧劇就更明顯了，尤其西方新聞媒體對藏獨表演的大合唱成為西方國家政治偏見與說謊造謠的經典之作，所用的手法卑劣不堪。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 總的說來，由藏獨引發的一連串政治事件和西方政府與西方媒體的操作是弄巧成拙，對中國的團結做出正面貢獻，這是西方國家始料不及的。造成西方媒體最後停止操作最重要的原因。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 你想想，在巴黎的大街上藏獨份子公然從輪椅上的殘疾人士手上搶奪火炬是多麼可笑與可鄙的畫面？有什麼正義性？簡直是為西藏獨立作反宣傳。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 英國與法國對北京奧運會表現的粗暴與他們強調的人權完全不搭調。奇怪嗎？一點也不。就是這兩個國家在1860年發動第二次鴉片戰爭，搶掠了我們的皇宮還焚燒了圓明園。他們都是強盜的後裔，流著強盜的血液，這是不會改變的民族性，只有台灣草包才相信他們代表人權。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 西方媒體開動宣傳機器鋪天蓋地的散佈下面的謠言：&nbsp;&nbsp;&nbsp;&nbsp; <br /> 1.西藏從前是天堂，是香格里拉；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 2.西藏是個獨立國家，1950年被中國非法佔領；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 3.達賴喇嘛是和平的象徵；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 4.中國的漢人壓迫藏人，摧毀西藏的宗教和文化；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 5.西藏應該獨立。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 西方媒體的歪曲宣傳終於激發了中國人的憤怒。事實是：&nbsp;&nbsp;&nbsp;&nbsp; <br /> 1.1950年以前的西藏並不是西方媒體所誇耀的香格里拉而是黑暗的農奴社會；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 2.西藏不是獨立國家，達賴都必須接受中國的冊封才能得到治理西藏的正當性；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 3.達賴喇嘛是西藏最大的奴隸主，也是一個殘忍的統治者；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 4.今天西藏人民的生活遠比五十年前好，西藏的宗教與文化被妥善保存；&nbsp;&nbsp;&nbsp;&nbsp; <br /> 5.九成以上的西藏人民滿意自己的生活，鬧獨立的是少數想恢復1950年以前政教合一統治權力的喇嘛，中國當然不能答應。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 西方媒體顛倒是非又用修改做假的照片和尼泊爾的暴動照片為西藏獨立做歪曲報導，暴露西方媒體為政治服務做不實宣傳的本質。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 西方媒體完全自毀形象，我認為這是西藏事件中國的最大收穫。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 在1989年的「六四事件」，中國人民非常崇拜西方媒體，CNN 與 VOA（美國之音）是中國人民，尤其是中國的年輕學生，最信任的新聞媒體。現在這兩個媒體成為中國人民取笑和輕視的對象。西方國家，尤其是美國，在軟實力上的損失是難以估計的。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 所以說，凡事不可過分，西方國家在這次發動的西藏事件做得太過份了，導致中國人的巨大反彈，特別是最代表西方媒體的CNN與VOA完全喪失了信譽，成為中國最大的收穫。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 西方媒體的謊言和惡意宣傳團結了幾乎海內外所有的中國人，除了漂亮、會慢跑、還會說英語的馬英九。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 馬英九跟著西方媒體的歪曲宣傳大放厥詞，甚至大罵大陸總理溫家寶，這是台灣政客做美國鷹犬的經典圖像，非常代表台灣人的&ldquo;尊嚴&rdquo;。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 馬英九給我的最大疑惑就是：一個人臉長得這麼漂亮，腦袋怎麼會這麼不聰明？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 不要告訴我馬英九這麼做是選舉作秀。馬英九勝選後仍然這麼認為，並沒有改口。馬英九已經是準國家領導人，君無戲言，他說什麼就代表他想什麼。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （二）天災&nbsp;&nbsp;&nbsp;&nbsp; <br /> 大陸１月的雪災是小狀況，帶來的不過是短時間的不便，對中國龐大的經濟不過是小小的損失。但是５月１２日的四川大地震就非常嚴重了，財產損失倒在其次，大量人民生命的損失是不能彌補的。&nbsp;&nbsp;&nbsp;&nbsp; <br /> YST承認最初也輕估了這個地震的破壞力。新華社最初發表的數字是7.7級，美國最初發表的數字是7.8級，後來又修正到7.9級，但是最要命的是把震央從地面下29公里修正到地面下10公里，這個差別可大了。要知道，地震威力的傳播是跟距離的平方成反比，所以震央如果從地面下29公里修正到10公里，地面建築承受的力道就是原來估計的九倍。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 說實話，我很不喜歡看台灣記者所做的實況報導，都是一些煽情的報導。她們不斷重複地震威力如何巨大（什麼相當於多少顆原子彈之類的荒唐比喻）、建築物破壞多嚴重度、人民傷亡如何悲慘、救助如何困難．．．．等等。除了煽情沒有任何正面價值。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 台灣記者不斷強調負面的悲慘景況，很少提到大陸人民正面的奮發努力。我總覺得她們在故意誇大和渲染災情的嚴重，因為這是台灣觀眾喜歡看到和聽到的。這些記者我即使不敢說她們是幸災樂禍但至少有看熱鬧的心理。我從她們的報導感覺不出有什麼人溺己溺的人道精神，因為她們報導時的神情與用詞並沒有一絲哀淒。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 我個人認為台灣記者這些煽情的報導主要是嘩眾取寵增加收視率，除此以外完全沒有意義，對社會的貢獻是負面的。台灣人喜歡看這種報導，就像喜歡看災難電影，看了很爽覺得自己很幸福。這是為什麼這種報導在台灣會有市場，這種用別人的悲慘生活來反映自己的幸福日子其心理非常不健康。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 最令我反感的是記者問那些死去親人、房屋家產全毀的生還者：「請問你現在的感覺是甚麼？」&nbsp;&nbsp;&nbsp;&nbsp; <br /> 這是什麼問題？典型的從別人的悲慘尋找自己的滿足。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 還有一個受難者對著電視鏡頭說：「我們希望能夠得到台灣的物資援助」。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 我敢擔保這是在台灣記者誘導式的問題下所產生出來的回答。台灣記者對受難人民的優越感和政治導向的報導非常明顯。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 至於台灣人民在島內的反應就更直接了，有人在 call in節目說：「叫他們把一千四百顆飛彈賣掉換錢來救災」，有人在 call in 節目說：「我們捐錢給他們，他們用這些錢做飛彈打我們怎麼辦？」&nbsp;&nbsp;&nbsp;&nbsp; <br /> 政客發動的捐款援助那就更充滿了政治算計，這不需要我多說。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （三）國際人道援助&nbsp;&nbsp;&nbsp;&nbsp; <br /> 國際上對自然災害所做的援助不可能沒有政治算計，我們用美國與日本做例子。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 美國這次援助緬甸的風災就要求直接派軍隊進入緬甸，一方面時代雜誌有文章說現在是入侵緬甸的最佳時機，一方面美國授予親西方的反對派領袖翁山素姬國會金質獎章，接著加拿大政府再頒發榮譽市民給翁山素姬，美國的政治意圖再明顯不過了。美國的援助是基於人道精神嗎？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 日本在國際援助上從來不給現款，而是用日本生產的實物做為援助。為了得到日本的援助，受援國幾乎都要向日本提出一些資料（或讓日本政府進行調查），表面的理由是為了使日本政府了解什麼樣的物質援助是適合的。所以日本的援助一方面使國內某些工業受益，另一方面可以獲得受援國平時不易獲得的生產資料。日本的情報工作是非常細膩的。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 說實話，這次的地震災害中國根本不需要國際援助。中國有錢、有人、有物質、有技術能力，外國能提供的中國本身都不缺乏。中國答應國際援助不過是為了表達開放和透明的政策，改變西方對中國的刻板印象，營造國際和諧的氣氛，哪裏會是因為中國自己沒有解決的能力所以迫切地需要國外的援助。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 以目前人民幣面對龐大的升值壓力、強大的出超和擁有的巨大外匯，中國政府即使為地震發行一兆人民幣也沒有問題。財產的損失估計大約兩、三百億美元，所以錢根本不是問題。中國政府完全能夠承受所有救災和重建的經費而綽綽有餘。中國政府讓民間發動捐款為的是表現「一方有難、八方支援」的團結精神。這是對的，如果YST是中國領導人也會做同樣的事。四川大地震中國最大的收穫就是全國人民更加緊密地團結在一起。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國有人力就更不用說了。沒有任何國際友人會像解放軍戰士那樣奮不顧身地救災。如果你看了災區的地形圖和瞭解那裏的天氣就知道在那種環境下空降是玩命，我不敢相信中共的空降兵真的在災區進行空降。大概全世界也只有中共解放軍有決心和勇氣幹這種事。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國有強大的生產力，有全世界最龐大的民生工業，諸如帳蓬、被褥、毛毯、衣服、速食麵、飲用水、電池、手電筒．．．等等都有巨大的存貨，完全不是問題。中國基礎建設非常健全和先進，各種需要的重型機械也完全不是問題。如果災區缺乏任何物質那是因為道路被破壞導致運輸困難，並不是沒有這些物質。中國並不需要國外的物質援助 。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 至於技術，地震救難有什麼高深技術？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國基礎建設的經驗豐富，鐵路和公路的修築是第一流，通訊技術也夠先進，尤其橋樑技術領先世界。剩下的就是救難犬、夜視鏡、生命探測器等等沒什麼大學問的東西，救難最終、最重要的其實是救難人員的雙手。地震在搶救倖存人員時是不敢動用重型機械的，主要就是靠兩隻手挖，非常原始。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 國際新聞喜歡炒作，鋪天蓋地的報導災區是如何如何的悲慘，然後再呼籲讀者踴躍捐款，給人一種錯覺，那就是如果沒有國際援助，中國就撐不下去，中國沒有辦法解決她的困難。這種印象是完全錯誤的。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 其實中國並不需要任何外援，但是為了對付西方媒體的&ldquo;民主攻勢&rdquo;，中國決定開放國際新聞媒體作現場採訪，也接受國際的援助。國際也樂得提供援助，因為這些政府都知道中國有沒有他們的援助其實都無所謂，所以樂得享受人道的美名。如果中國（任何國家也一樣）真的是沒有國際援助就沒有辦法解決問題，那麼西方國家反而不會爽快地提供援助給中國，一定會提出很多政治條件要中國答應，就像緬甸。這是國際現實。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 世上絕沒有白吃的午餐，所謂&ldquo;人道援助&rdquo;只是表面好聽的藉口。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （四）中國的動員能力&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國開放國際媒體現場採訪是對的。大陸政府讓全世界看到中國高效率的動員能力，這是令西方世界害怕的。全世界沒有任何國家可以在這麼短的時間動員這麼大的人力和物力到達那麼偏遠的山區。而且在這次動員中沒有包括蘭州軍區和南京軍區的兵力，這就告訴全世界，你們別想趁火打劫。中國非常清楚地展現大國實力。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 2002年中國發生SARS疫情（大陸稱作&ldquo;非典&rdquo;）。台灣對大陸嘲諷，新加坡對中國首先落井下石，西方世界都在看中國的笑話。那時候有什麼國際人道援助？中國大陸展現的實力就是用七天建造了一座有一千張病床的隔離醫院，第八天病人進住，醫生和護士全部到位開始工作。全世界看到中國很快地有效控制了SARS，新加坡總理吳作棟笑不出來了。這就是國際現實。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 中國的動員能力震懾全世界。沒有任何其他國家能在七天之內建成一座有一千個病床的隔離醫院。它告訴全世界一旦中國的戰爭機器開動，中國的戰爭潛力巨大無比，不容挑釁。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 大陸這次在四川特大地震所表現出來的高效率救援行動令西方國家瞠目結舌。全世界沒有一個國家能做到中國所做的，連台灣最佩服的日本都做不到，差遠了。無論日本還是美國都不可能在這麼短的時間把這麼多的軍隊在這麼惡劣的條件下送到災區。舉個例子，在沒有道路的情況下，解放軍在負重的情況下能用強行軍（每小時八公里的速度）徒步幾十公里到達災區然後立即展開救援行動。每小時八公里幾乎是 YST空手慢跑的速度。沒有任何其他國家的軍隊可以做到。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （五）什麼是民主？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 美國與台灣都誇口自己民主並且指責大陸專制。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 什麼是民主？民主不是人民手上有一張選票。民主的意義是「以人民為主」。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 如果用這個作標準，再想想「八掌溪事件」。大家不妨回想一下當時的國軍搜救中心、內政部空中警察隊和海鷗中隊他們之間如何在政府條文中虛耗時間。四位在洪流中手牽手支撐達兩小時最後在全國電視轉播下被洪流沖走的人民，他們手上沒有選票嗎？&nbsp;&nbsp;&nbsp;&nbsp; <br /> 我認為溫家寶政府比陳水扁政府要民主。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 如果用這個作標準，再想想「921地震」。大家不妨回想一下李登輝什麼時候到達災區，他和災民的對話是甚麼態度和什麼內容。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 我認為溫家寶政府比李登輝政府要民主。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 如果用這個作標準，再想想「卡翠娜颱風」。大家不妨回想一下當半個紐奧良泡在水裏、城市陷入無政府狀態的混亂局面的時候，布希總統仍然在打高爾夫球。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 我認為溫家寶政府比小布希政府要民主。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （六）中國人的信心&nbsp;&nbsp;&nbsp;&nbsp; <br /> 大國要表現出來的就是遇到重大困難的時候能夠臨危不亂和有克服困難的信心。這一點我們在溫家寶身上看得很清楚，地震發生後僅僅五小時溫家寶總理已經從兩千公里外的北京趕到了地震現場，開始指揮救援工作，真是分秒都不耽誤。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 溫家寶對災民說：救人是整個工作中的重中之重，只要有一線希望，我們就要付出百倍的努力救他們，絕不輕言放棄。大家要有信心，只要我們還有一雙手就可以重建家園。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 是的，中國是大國，沒有任何其他國家能夠幫助像中國這麼大的國家，不可能的。中國必須有自信、自力更生、克服困難。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 營救地震倖存人員、重建災後的家園，中國要靠自己，也只能靠自己。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 舉辦一個成功的奧運會，中國要靠自己，也只能靠自己。外國人只會搗亂和看我們的笑話。&nbsp;&nbsp;&nbsp;&nbsp; <br /> （七）結論&nbsp;&nbsp;&nbsp;&nbsp; <br /> 2008年的前五個月中國大陸的確是多災多難。但是，無論是上天降下的災害還是國際列強製造的人禍，對中國而言都是正面的考驗，產生了正面的團結力量，更緊密地凝結了中華民族。這是費盡心機的國際列強始料未及的。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 不論西方國家如何想把中國的奧運會攪黃或攪臭，我們自己要有信心把北京奧運會辦得漂亮又風光。譬如法國人和英國人在巴黎和倫敦上演反中的聖火搶奪和用滅火器撲滅聖火的鬧劇與醜劇，中國人在自己的領土就要上演最風光和最漂亮的奧運火炬傳遞。我們來看看中國人的正面反應。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 最風光的奧運火炬傳遞是進行在世界的最高點，喜馬拉雅山的珠穆朗瑪峰。這些鏡頭我們在一星期前都介紹過了。&nbsp;&nbsp;&nbsp;&nbsp; <br /> 最漂亮的奧運火炬傳遞是進行在中國的福州。你看，５月11日福州美眉林姝敏用芭蕾舞般優雅與漂亮的姿態傳遞奧運火炬，這就是中國人如何表達自己的信心與歡樂。</p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E5%258F%25B0%25E6%25B9%25BE/" rel="tag">台湾</a> , <a href="http://www.newtyper.com/blog/tags/%25E7%259C%258B%25E6%25B3%2595/" rel="tag">看法</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/161/</link>
<title><![CDATA[众志成城]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[缤纷世界]]></category>
<pubDate>Tue, 13 May 2008 15:19:01 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/161/</guid> 
<description>
<![CDATA[ 
	<p>实在看不下那些叫闹的脑残了。</p>&nbsp;&nbsp;<p>也不想多说了，自己看吧。</p><p><br /></p>&nbsp;&nbsp;<p>转至百度地震吧</p>&nbsp;&nbsp;<p><a href="http://tieba.baidu.com/f?kz=377229021" title="http://tieba.baidu.com/f?kz=377229021"><a href="http://tieba.baidu.com/f?kz=377229021" target="_blank">http://tieba.baidu.com/f?k...</a></a></p>&nbsp;&nbsp;<p>这几天都不想看报纸，不想看电视，不想上网！&nbsp;&nbsp;&nbsp;&nbsp;<br />看到的消息总是让人心里堵的难受！&nbsp;&nbsp;&nbsp;&nbsp; <br />一开始是南方冻雨！&nbsp;&nbsp;&nbsp;&nbsp; <br />前几天是圣火遇到流氓！&nbsp;&nbsp;&nbsp;&nbsp; <br />接着火车又撞了！&nbsp;&nbsp;&nbsp;&nbsp; <br />上海的公交车又烧了！&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />而五月十二日---------汶川地震了！而且是大地震！&nbsp;&nbsp;&nbsp;&nbsp; <br />更让人难过的是贴吧充斥着谴责政府、谴责军队、谴责地震局的帖子！所谓的众志成城不知道是啥意思！&nbsp;&nbsp;&nbsp;&nbsp; <br />这事情政府有责任吗？或者说现在是问责的时候吗？&nbsp;&nbsp;&nbsp;&nbsp; <br />这事情军队做的不够好吗？或者说现在是用自己悠闲的指头在键盘上轻击，来伤害那些在一线抢险的年轻人的心的时候吗？&nbsp;&nbsp;&nbsp;&nbsp; <br />这事情地震局有所不妥吗？或者说地震局的家属以及地震局的人都已经脱离的险境吗？&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />.&nbsp;&nbsp;&nbsp;&nbsp; <br />伤心---大家都伤心，着急---大家都着急！愤怒---为什么要在这个时候有那么多的愤怒？&nbsp;&nbsp;&nbsp;&nbsp; <br />不要拿十万只蛤蟆来说事！有太多无聊的人拿2005年 2006年 甚至是更早的别处的蛤蟆搬家的事情来印证这次地震了！有更多做事不过脑子甚至就基本上属于没有脑子的人一直在传播这些事情！这种散播谣言的人，远离灾区！煽动愤怒！实在是无聊至极！&nbsp;&nbsp;&nbsp;&nbsp; <br />不要拿预报来说事！地震本来就是没有办法准确预报的！要是能准确预报！&nbsp;&nbsp;&nbsp;&nbsp; <br />2005年11月10日的南亚大地震会死那么多人？仅巴基斯坦8.6万多人死亡，10万多人受伤！--你可以说这是因为巴基斯坦科技不发达！&nbsp;&nbsp;&nbsp;&nbsp; <br />来个科技发达的！&nbsp;&nbsp;&nbsp;&nbsp; <br />1995年1月17日日本神户发生了7.2级地震，死亡人数达到5466人，3万多人受伤，几十万人无家可归，受灾人口达140万人，被毁房屋超过10万栋，生命工程和公共设施严重破坏。这次地震造成的经济损失总计超过960亿美元。---这还是地震震习惯了的日本！抗震做的最好的日本！&nbsp;&nbsp;&nbsp;&nbsp; <br />再来一个这几年的！&nbsp;&nbsp;&nbsp;&nbsp; <br />1994年1月17日凌晨4时31分，洛杉矶地区发生里氏6.6级地震，震中位于市中心西北200多公里的圣费尔南多谷的北岭地区。发生地震时大多数人还处于沉睡之中，还没有来得及反应，灾难就降临了。在持续30秒的震撼中，大约有11000多间房屋倒塌，震中30公里范围内高速公路、高层建筑或毁坏或倒塌，煤气、自来水管爆裂，电讯中断，火灾四起，直接和间接死亡58人，受伤600多人，财产损失300多亿美元。&nbsp;&nbsp;&nbsp;&nbsp; <br />地震的级数每增加一级，破坏力加大30倍！6.6级地震就造成了这么大的伤害！远的我还就不说了！&nbsp;&nbsp;&nbsp;&nbsp; <br />这些如果可以预知，那么有什么理由使这些政府不提前提出预警呢？&nbsp;&nbsp;&nbsp;&nbsp; <br />唯一的理由就是-------地震局的确知道会发生地震----但是不知道震中在哪里，不知道震级有多强，有的没大脑的会说，&ldquo;知道个大概就给大家提个醒呗&rdquo;！&nbsp;&nbsp;&nbsp;&nbsp; <br />最大的灾难是恐惧！---------这是无数次灾难验证出来的，相信很多朋友都有听说过一些踩踏事件！是什么使得踩踏事件出现？没有地震，没有火山爆发，仅仅是因为有了恐惧！&nbsp;&nbsp;&nbsp;&nbsp; <br />元宵灯节的拥挤带来恐慌变为踩踏&nbsp;&nbsp;&nbsp;&nbsp; <br />上下楼的小朋友的恶作剧带来恐慌变为踩踏&nbsp;&nbsp;&nbsp;&nbsp; <br />抢购商品的摔倒变为恐慌带来踩踏&nbsp;&nbsp;&nbsp;&nbsp; <br />我可以想象出来！ 如果地震局不负责任的预警会有地震！那么毫无疑问的会出现以下情况&nbsp;&nbsp;&nbsp;&nbsp; <br />1. 抢购粮食和水、----会有不法商贩囤货！&nbsp;&nbsp;&nbsp;&nbsp; <br />2. 毫无疑问的会有买不到粮食和水趁火打劫的！&nbsp;&nbsp;&nbsp;&nbsp; <br />3. 大量出逃-----交通堵塞---从而引起更大规模更大范围的恐慌&nbsp;&nbsp;&nbsp;&nbsp; <br />4. 谣言四起------因为地震在四川，毫无疑问会有SB和网特夸大将要发生的地震的灾情，毫无疑问会有SB和网特造三峡大坝的谣，毫无疑问的会引起长江中下游地区的恐慌！这将是至少五亿人的恐慌。&nbsp;&nbsp;&nbsp;&nbsp; <br />这一切还是建立在地震100%会发生的情况下！&nbsp;&nbsp;&nbsp;&nbsp; <br />如果事后地震没有发生呢？或者地震发生在荒无人烟的地方呢，或者是发生的震源很深！深到大家以为是地震局的一个玩笑呢？&nbsp;&nbsp;&nbsp;&nbsp; <br />另一场灾难就开始了！&nbsp;&nbsp;&nbsp;&nbsp; <br />指责那些不空降的！-----记住那些士兵也是生命！&nbsp;&nbsp;&nbsp;&nbsp; <br />指责那些政府不作为的！------记住温总理是&nbsp;&nbsp;&nbsp;&nbsp; <br />本来想说很多！突然觉得面对这些不特定的网友！有些可能根本就是唯恐天下不乱的网友！不会因为我几句讲道理的话就停止喧闹的！&nbsp;&nbsp;&nbsp;&nbsp; <br />孔子都说过！和朋友讲道理！讲一两次就行了！讲的多了也不听！就随便他吧！&nbsp;&nbsp;&nbsp;&nbsp; <br />衷心的为灾区的群众感到伤心！由衷的期待自己可以做些什么！&nbsp;&nbsp;&nbsp;&nbsp; <br />那些仔细看了我的这些文字的网友！让我们去做些对灾区有用的事情吧！&nbsp;&nbsp;&nbsp;&nbsp; <br />1. 找出你不穿的衣服、鞋子。&nbsp;&nbsp;&nbsp;&nbsp; <br />2. 年满18岁的去街头找找献血车或者直接去血站献血！&nbsp;&nbsp;&nbsp;&nbsp; <br />3. 有能力发动募捐的发动一次募捐！&nbsp;&nbsp;&nbsp;&nbsp; <br />4. 用你所了解到的方式给灾区捐款！&nbsp;&nbsp;&nbsp;&nbsp; <br />5. 留下祝福的话给灾区的网友和他们的家人！&nbsp;&nbsp;&nbsp;&nbsp; <br />6. 停止响应那些要清算、要找出责任人的帖子！&nbsp;&nbsp;&nbsp;&nbsp; <br />成语:众志成城(zh&ograve;ng zh&igrave; ch&eacute;ng ch&eacute;ng)&nbsp; <br />典故:万众一心，象坚固的城墙一样不可摧毁。比喻团结一致，力量无比强大。&nbsp; <br />出处:《国语&middot;周语下》：&ldquo;众心成城，众口铄金。&rdquo;</p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E5%259C%25B0%25E9%259C%2587/" rel="tag">地震</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/152/</link>
<title><![CDATA[CentOS 5.1安装配置及其服务安装配置]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[小特土产]]></category>
<pubDate>Mon, 31 Dec 2007 18:08:38 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/152/</guid> 
<description>
<![CDATA[ 
	版本：<br />2008-01-01 v1.0 Benny Feng<br /><br />关键字：<br />CentOS5.1 apache2.2 tomcat6.0 php5.2 gd2 mysql5.0 整合 安装<br /><br />规约：<br />1.<span style="color: #0000ff">蓝色字体</span>为执行的命令<br />2.<span style="color: #008000">绿色字体</span>为配置文件添加修改内容<br />3.<span style="color: #ff0000">红色字体</span>为根据所描述需要替换的内容<br /><br /><strong>一、系统安装配置</strong><br />1.安装过程和AS4/5基本一样，安装软件包的时候注意选择，无需安装多余的软件包，需要注意的是在安装后第一次启动设置时防火墙将ssh和www端口勾选（当然这个也可以在安装后修改iptables配置文件），在设置selinux是选者关闭或者允许。<br /><br />2.以简体中文为基础语言安装后需要修改/etc/profile方可在远程SSH时不出现乱码。<br />编辑/etc/profile，结尾加上<br /><span style="color: #008000">export LANG=zh_CN<br />export LC_ALL=zh_CN.GB18030</span><br /><br />3.建议修改ssh端口<br />ssh配置文件为/etc/ssh/sshd_config<br />介绍从略<br /><br />4.修改iptables配置<br />iptables配置文件为/etc/sysconfig/iptables<br />介绍从略<br /><br />5.如果安装好后出现sendmail启动很慢<br />修改/etc/hosts<br />在127.0.0.1后添加主机名<br /><br /><strong>二、服务安装及配置</strong><br />所需的软件包（具体下载地址和软件介绍请搜索google，对baidu没好感）：<br />jdk-6u3-linux-i586.bin<br />apache-tomcat-6.0.14.tar.gz<br />apr-1.2.12.tar.gz<br />apr-util-1.2.12.tar.gz<br />zlib-1.2.3.tar.gz<br />libpng-1.2.24.tar.gz<br />freetype-2.3.5.tar.gz<br />jpegsrc.v6b.tar.gz<br />gd-2.0.33.tar.gz<br />cronolog-1.6.2.tar.gz<br />libtool-1.5.24.tar.gz<br />libxml2-2.6.30.tar.gz<br />mysql-5.0.51.tar.gz<br />httpd-2.2.6.tar.gz<br />php-5.2.5.tar.gz<br />ZendOptimizer-3.3.0a-linux-glibc21-i386.tar.gz<br />eaccelerator-0.9.5.2.tar.bz2<br /><br />1.安装JDK1.6<br /><span style="color: #0000ff">chmod a+x jdk-6u3-linux-i586.bin<br />./jdk-6u3-linux-i586.bin<br />mkdir /usr/java<br />cp -rf jdk1.6.0_03 /usr/java/jdk1.6.0_03</span><br />编辑/etc/profile在文件最后加入<br /><span style="color: #008000">#set java environment<br />JAVA_HOME=/usr/java/jdk1.6.0_03<br />CLASSPATH=.:$JAVA_HOME/lib.tools.jar<br />PATH=$JAVA_HOME/bin:$PATH<br />export JAVA_HOME CLASSPATH PATH</span><br /><br />2.安装Tomcat6<br /><span style="color: #0000ff">tar -zxvf apache-tomcat-6.0.14.tar.gz<br />cp -rf apache-tomcat-6.0.14 /usr/local/tomcat/</span><br />编辑/etc/profile在文件最后加入<br /><span style="color: #008000">export CATALINA_HOME=/usr/local/tomcat</span><br />设置系统启动自启动<br /><span style="color: #0000ff">cd /usr/local/tomcat/bin<br />cp catalina.sh /etc/rc.d/init.d/tomcat</span><br />编辑TOMCAT的启动脚本：<br /><span style="color: #0000ff">vi +2 /etc/rc.d/init.d/tomcat</span><br />在第二行加入以下内容：<br /><span style="color: #008000"># chkconfig: 2345 63 37<br /># description: tomcat server init script<br />JAVA_HOME=/usr/java/jdk1.6.0_03<br />CATALINA_HOME=/usr/local/tomcat</span><br />然后保存退出<br /><span style="color: #0000ff">chkconfig --add tomcat<br />chkconfig --list tomcat</span><br />启动tomcat<br /><span style="color: #0000ff">service tomcat start</span><br /><br />3.安装apr<br /><span style="color: #0000ff">tar xvfz apr-1.2.12.tar.gz<br />cd apr-1.2.12<br />./configure --prefix=/usr/local/apr-httpd<br />make<br />make install</span><br /><br />4.安装apr-util<br /><span style="color: #0000ff">tar xvfz apr-util-1.2.12.tar.gz<br />cd apr-util-1.2.12<br />./configure --prefix=/usr/local/apr-util-httpd --with-apr=/usr/local/apr-httpd<br />make<br />make install</span><br /><br />5.安装zlib<br /><span style="color: #0000ff">tar zxvf zlib-1.2.3.tar.gz<br />cd zlib-1.2.3<br />./configure --prefix=/usr --shared<br />make<br />make install</span><br /><br />6.安装libpng<br /><span style="color: #0000ff">tar zxvf libpng-1.2.24.tar.gz<br />cd libpng-1.2.24<br />cd scripts/<br />mv makefile.linux ../makefile<br />cd ..<br />make<br />make install</span> <br /><br />7.安装freetype<br /><span style="color: #0000ff">tar xzvf freetype-2.3.5.tar.gz<br />cd freetype-2.3.5<br />./configure <br />make<br />make install</span><br /><br />8.安装libjpeg-6b<br /><span style="color: #0000ff">tar xzvf jpegsrc.v6b.tar.gz<br />cd jpeg-6b<br />./configure --enable-static --enable-shared --prefix=/usr<br />make<br />mkdir /usr/man<br />mkdir /usr/man/man1<br />make install</span><br /><br />9.安装GD2<br /><span style="color: #0000ff">tar xzvf gd-2.0.33.tar.gz<br />cd gd-2.0.33<br />./configure --prefix=/usr/local/gd2 --mandir=/usr/share/man --with-png --with-freetype --with-jpeg<br />make<br />make install</span><br /><br />10.安装cronolog<br /><span style="color: #0000ff">tar xzvf cronolog-1.6.2.tar.gz<br />cd cronolog-1.6.2<br />./configure --prefix=/usr/local/cronolog<br />make<br />make install</span><br /><br />11.安装libtool<br /><span style="color: #0000ff">tar xvfz libtool-1.5.24.tar.gz<br />cd libtool-1.5.24<br />./configure<br />make<br />make install</span><br /><br />12.安装libxml2<br /><span style="color: #0000ff">tar xzvf libxml2-2.6.30.tar.gz <br />cd libxml2-2.6.30<br />./configure --prefix=/usr/local/libxml2<br />make<br />make install</span><br /><br />13.安装MYSQL5.0<br /><span style="color: #0000ff">tar xzvf mysql-5.0.51.tar.gz<br />cd mysql-5.0.51<br />CHOST=&quot;i686-pc-linux-gnu&quot; CFLAGS=&quot;-O3 -msse2 -mmmx -mfpmath=sse -mcpu=pentium4 -march=pentium4 -pipe -fomit-frame-pointer&quot; CXXFLAGS=&quot;-O3 -msse2 -mmmx -mfpmath=sse -funroll-loops -mcpu=pentium4 -march=pentium4 -pipe -fomit-frame-pointer&quot; ./configure --prefix=/usr/local/mysql --localstatedir=/var/lib/mysql --with-comment=Source --with-server-suffix=-Community --with-mysqld-user=mysql --without-debug --with-big-tables --with-charset=latin1 --with-collation=latin1_swedish_ci --with-extra-charsets=all --with-pthread --enable-static --enable-thread-safe-client --with-client-ldflags=-all-static --with-mysqld-ldflags=-all-static --enable-assembler --without-innodb --without-ndb-debug<br />make<br />make install<br />groupadd mysql<br />useradd -g mysql mysql <br />cd /usr/local/mysql<br />bin/mysql_install_db --user=mysql<br />chown -R root:mysql . <br />chown -R mysql /var/lib/mysql <br />chgrp -R mysql . <br />cp share/mysql/my-huge.cnf /etc/my.cnf<br />cp share/mysql/mysql.server /etc/rc.d/init.d/mysqld <br />chmod 755 /etc/rc.d/init.d/mysqld<br />chkconfig --add mysqld</span><br />启动mysql<br /><span style="color: #0000ff">/etc/rc.d/init.d/mysqld start <br />bin/mysqladmin -u root password &quot;<span style="color: #ff0000">需要设置的root密码</span>&quot;<br />service mysqld stop</span> <br /><br />14.安装apache2.2<br /><span style="color: #0000ff">tar xzvf httpd-2.2.6.tar.gz <br />cd httpd-2.2.6<br />CHOST=&quot;i686-pc-linux-gnu&quot; CFLAGS=&quot;-O3 -msse2 -mmmx -mfpmath=sse -funroll-loops -mcpu=pentium4 -march=pentium4 -pipe -fomit-frame-pointer&quot; ./configure --prefix=/usr/local/apache2 --enable-mods-shared=all --enable-module=most --enable-deflate=shared --enable-expires=shared --enable-rewrite=shared --enable-cache --enable-file-cache --enable-mem-cache --enable-disk-cache --enable-static-support --enable-static-htpasswd --enable-static-htdigest --enable-static-rotatelogs --enable-static-logresolve --enable-static-htdbm --enable-static-ab --enable-static-checkgid --disable-cgid --disable-cgi --disable-userdir --with-mpm=worker --enable-authn-dbm=shared --enable-authn-anon=shared --enable-authn-dbd=shared --enable-auth-basic=shared --with-apr=/usr/local/apr-httpd --with-apr-util=/usr/local/apr-util-httpd --enable-so --enable-proxy=shared --enable-proxy-balancer=shared --enable-proxy-http=shared --enable-proxy-ajp --enable-deflate <br />make<br />make install</span><br />设置系统启动自启动<br /><span style="color: #0000ff">cp /usr/local/apache2/bin/apachectl /etc/rc.d/init.d/httpd</span><br />然后在/etc/rc.d/init.d/httpd 添加(#!/bin/sh下面)<br /><span style="color: #008000"># chkconfig: 2345 10 90<br /># description: Activates/Deactivates Apache Web Server</span><br />最后，运行chkconfig把Apache添加到系统的启动服务组里面：<br /><span style="color: #0000ff">chkconfig --add httpd<br />chkconfig httpd on<br />service httpd start</span><br /><br />15.安装php5.2<br /><span style="color: #0000ff">tar xzvf php-5.2.5.tar.gz<br />cd php-5.2.5<br />CHOST=&quot;i686-pc-linux-gnu&quot; CFLAGS=&quot;-O3 -msse2 -mmmx -mfpmath=sse -mcpu=pentium4 -march=pentium4 -pipe -fomit-frame-pointer&quot; CXXFLAGS=&quot;-O3 -msse2 -mmmx -mfpmath=sse -funroll-loops -mcpu=pentium4 -march=pentium4 -pipe -fomit-frame-pointer&quot; ./configure --prefix=/usr/local/php --with-apxs2=/usr/local/apache2/bin/apxs --with-zlib-dir --with-bz2 --with-tiff-dir --with-libxml-dir=/usr/local/libxml2 --with-gd=/usr/local/gd2 --with-freetype-dir --with-jpeg-dir --with-png-dir --with-ttf --enable-mbstring --with-mysql=/usr/local/mysql --with-mysqli=/usr/local/mysql/bin/mysql_config --with-config-file-path=/etc --with-iconv --disable-ipv6 --enable-static<br />make<br />make install<br />cp php.ini-recommended /etc/php.ini</span><br /><br />16.apache和php整合<br />修改/usr/local/apache2/conf/httpd.conf在最后一行加上：<br /><span style="color: #008000">AddType application/x-httpd-php .php</span><br />查找：(设置 WEB 默认文件)<br /><span style="color: #008000">DirectoryIndex index.html</span><br />替换为：<br /><span style="color: #008000">DirectoryIndex index.php index.html index.htm</span><br />找到这一段：<br /><span style="color: #008000"># AllowOverride controls what directives may be placed in .htaccess files.<br /># It can be &quot;All&quot;, &quot;None&quot;, or any combination of the keywords:<br /># Options FileInfo AuthConfig Limit<br />#<br />AllowOverride None</span><br />更改为<br /><span style="color: #008000">AllowOverride All</span><br /><br />17.安装Zend Optimizer<br /><span style="color: #0000ff">tar xzvf ZendOptimizer-3.3.0a-linux-glibc21-i386.tar.gz<br />cd ZendOptimizer-3.3.0a-linux-glibc21-i386<br />./install.sh</span><br />基本上一路回车就是了<br /><br />18.安装eaccelerator<br /><span style="color: #0000ff">tar -jxvf eaccelerator-0.9.5.2.tar.bz2<br />cd eaccelerator-0.9.5.2<br />export PHP_PREFIX=&quot;/usr/local/php&quot;<br />$PHP_PREFIX/bin/phpize<br />./configure --enable-eaccelerator=shared --with-php-config=$PHP_PREFIX/bin/php-config<br />make<br />make install</span><br />编译安装后得到地址 /usr/local/php/lib/php/extensions/no-debug-zts-20060613/<br />修改/usr/local/Zend/etc/php.ini <br />在文件最后，zend之前，注意，这部分内容务必放在zend之前，不然可能会出现不可预期的服务器问题。添加下列信息：<br /><span style="color: #008000">[eaccelerator]<br />extension=&quot;/usr/local/php/lib/php/extensions/no-debug-zts-20060613/eaccelerator.so&quot;<br />eaccelerator.shm_size=&quot;32&quot;<br />eaccelerator.cache_dir=&quot;/tmp/eaccelerator&quot;<br />eaccelerator.enable=&quot;1&quot;<br />eaccelerator.optimizer=&quot;1&quot;<br />eaccelerator.check_mtime=&quot;1&quot;<br />eaccelerator.debug=&quot;0&quot;<br />eaccelerator.filter=&quot;&quot;<br />eaccelerator.shm_max=&quot;0&quot;<br />eaccelerator.shm_ttl=&quot;0&quot;<br />eaccelerator.shm_prune_period=&quot;0&quot;<br />eaccelerator.shm_only=&quot;0&quot;<br />eaccelerator.compress=&quot;1&quot;<br />eaccelerator.compress_level=&quot;9&quot;</span><br />解释:<br />zend_extension 是安装完程序自动指示给我们的<br />eaccelerator.shm_size=&quot;32&quot; 缓存大小单位MB<br />eaccelerator.cache_dir=&quot;/tmp/eaccelerator&quot; 缓存路径<br />执行<br /><span style="color: #0000ff">mkdir /tmp/eaccelerator<br />chmod 777 /tmp/eaccelerator</span><br /><br />19.apache和tomcat整合<br />修改/usr/local/apache2/conf/httpd.conf<br />添加：<br /><span style="color: #008000">ProxyPass /<span style="color: #ff0000">运行JSP和servlet目录</span> ajp://127.0.0.1:8009/<span style="color: #ff0000">运行JSP和servlet目录</span><br />ProxyPassReverse /<span style="color: #ff0000">运行JSP和servlet目录</span> ajp://127.0.0.1:8009/</span><span style="color: #ff0000">运行JSP和servlet目录</span><br />修改/usr/local/tomcat/conf/server.xml<br />修改<br /><span style="color: #008000">&lt;Host name=&quot;localhost&quot; appBase=&quot;webapps&quot;<br />unpackWARs=&quot;true&quot; autoDeploy=&quot;true&quot;<br />xmlValidation=&quot;false&quot; xmlNamespaceAware=&quot;false&quot;&gt;</span><br />为<br /><span style="color: #008000">&lt;Host name=&quot;localhost&quot; appBase=&quot;/usr/local/apache2/htdocs&quot;<br />unpackWARs=&quot;true&quot; autoDeploy=&quot;true&quot;<br />xmlValidation=&quot;false&quot; xmlNamespaceAware=&quot;false&quot;&gt;</span><br /><br /><strong>三、其他</strong><br />1.如果在编译中出现提示libapr不存在<br /><span style="color: #0000ff">mkdir /usr/local/apache2/lib<br />ln -s /usr/local/apr-httpd/lib/libapr-1.so.0.2.12 /usr/local/apache2/lib/libapr-1.so<br />ln -s /usr/local/apr-httpd/lib/libapr-1.so.0.2.12 /usr/local/apache2/lib/libapr-1.so.0<br />ln -s /usr/local/apr-util-httpd/lib/libaprutil-1.so.0.2.12 /usr/local/apache2/lib/libaprutil-1.so<br />ln -s /usr/local/apr-util-httpd/lib/libaprutil-1.so.0.2.12 /usr/local/apache2/lib/libaprutil-1.so.0</span><br/>Tags - <a href="http://www.newtyper.com/blog/tags/centos5.1/" rel="tag">centos5.1</a> , <a href="http://www.newtyper.com/blog/tags/apache2.2/" rel="tag">apache2.2</a> , <a href="http://www.newtyper.com/blog/tags/tomcat6.0/" rel="tag">tomcat6.0</a> , <a href="http://www.newtyper.com/blog/tags/php5.2/" rel="tag">php5.2</a> , <a href="http://www.newtyper.com/blog/tags/gd2/" rel="tag">gd2</a> , <a href="http://www.newtyper.com/blog/tags/mysql5.0/" rel="tag">mysql5.0</a> , <a href="http://www.newtyper.com/blog/tags/%25E6%2595%25B4%25E5%2590%2588/" rel="tag">整合</a> , <a href="http://www.newtyper.com/blog/tags/%25E5%25AE%2589%25E8%25A3%2585/" rel="tag">安装</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/151/</link>
<title><![CDATA[Google 坚信的 10 大信条]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[缤纷世界]]></category>
<pubDate>Wed, 26 Dec 2007 04:35:21 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/151/</guid> 
<description>
<![CDATA[ 
	<p>1. Focus on the user and all else will follow. 关注使用者,则一切将水到渠成.<br />(H:对我来说这就是客户关系的经营,设身处地的为客户着想,而不是把这些行为都当成新案子的酬庸;至于新案子?对我而言这绝对是第二个考虑... 我想只要用心,客户是可以感受到并给予回馈.) </p><p>2. It's best to do one thing really, really well. 尽力将一件事做到最好.<br />(H:手上正在进行的工作,别只是把他做完就好,也不要有太多的权宜、妥协!尽可能真的把他的「做好」,因为老想着之后再回过头来怎样又怎样,经验告诉我,通常回过头来怎样又怎样的机会是非常低的,然而这个不完美的作品却已经被客户在心中悄悄的打了分数.)<br />3. Fast is better than slow. 快比慢好.<br />(H:这不是指把事情做的快的意思,比如说:你写的程序跑起来效率要高、有新的点子想到就要快点实作、.... 太多了,说不完.) <br />4. Democracy on the web works. 网络的民主效应.<br />(H: Web 2.0!!! Wikipedia, Blog..... )<br />5. You don't need to be at your desk to need an answer. 您不一定要在桌子前找答案.<br />(手机通讯,行动通讯,3G....) <br />6. You can make money without doing evil. 不做坏事也能赚钱.<br />7. There's always more information out there. 「信息」也无涯.<br />8. The need for information crosses all borders. 对信息的需求没有国界.<br />9. You can be serious without a suit. 不穿西装也可以很正经.<br />(H: 早上你可以晚点进公司,上班时间你可以开心的大声说笑,傍晚你也可以早点下班回家,这些我都不在乎;但是你不能上班时间不工作而回家报边加班边工作,是因为公司风水比较差吗?你必需要对自己开出来的Schedule负责,当你面对自己开的Schedule是一再的Delay,那我会势必质疑你的能力!也许我什么都没说,但请相信我,很多事情是看在眼里,放在心底的.我的重点是--你要对你自己的决定/行为负责,形式上的一些规定对我来讲并不是重点.)<br />10. Great just isn't good enough. 精益求精.<br />(H:没啥好说说的,尽可能做到下面这句话吧 -- Be the only one, not number one.) </p><p>&nbsp;</p> <p>PS:加一句 <a href="http://www.xici.net/b426687/d61638651.htm" target="_blank">无商不<span style="color: #ff0000">尖</span></a></p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E4%25BF%25A1%25E6%259D%25A1/" rel="tag">信条</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/150/</link>
<title><![CDATA[弃山口山一周年半纪念]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[游戏之路]]></category>
<pubDate>Wed, 26 Dec 2007 02:27:10 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/150/</guid> 
<description>
<![CDATA[ 
	<p>无耻自爆后撒手人寰&nbsp;&nbsp; <br/>送死我去背黑锅你来&nbsp;&nbsp; <br/>躺在地上仰望着天空&nbsp;&nbsp; <br/>原来死也是一种幸福</p> <p>当年参加farmer有感而诌的打油诗</p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/149/</link>
<title><![CDATA[2小时环湖夜行]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Sat, 15 Dec 2007 17:53:32 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/149/</guid> 
<description>
<![CDATA[ 
	<p>上周周三由于下雨没有去夜爬，但是周四去参加了环湖夜行，路线是少年宫-保俶塔-岳坟-苏堤-南山路-湖滨-少年宫这样一个环线。</p> <p>晚上7点半出发，我用了2小时05分完成全程，不过有人1小时20分就走完了。有之前2次夜爬的锻炼，爬宝石山相对要轻松的多。</p> <p>以后我基本上还是参加夜爬，因为起点终点都离开公司比较近，而且结束的也比较早。</p> <p><a href="http://picasaweb.google.com/iciness/20071213/photo#5144258606128100546" target="_blank"><img src="http://lh5.google.com/iciness/R2QT8mMC9MI/AAAAAAAABYA/lNZdh3Oq8AY/s400/2007-12-13.jpg" border="0" /><br /></a></p><p><a href="http://picasaweb.google.com/iciness/20071213/photo#5144258606128100546" target="_blank"><a href="attachment.php?fid=4">点击这里下载文件</a></a></p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E6%2588%25B7%25E5%25A4%2596/" rel="tag">户外</a> , <a href="http://www.newtyper.com/blog/tags/%25E5%25A4%259C%25E8%25A1%258C/" rel="tag">夜行</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/148/</link>
<title><![CDATA[再临九溪]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Mon, 03 Dec 2007 12:35:51 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/148/</guid> 
<description>
<![CDATA[ 
	<p>话说今年已经第三次去九溪，是近10年来去九溪次数的总和。这次陪2个上海的朋友晃悠。</p> <p>九溪的深秋也是不错的，还是拍了些上的了台面的片子，朋友的片子就罢了，说实话我被某位萌到了（哈哈原因就免了免了）。</p> <p><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336826663525762" target="_blank"><img src="http://lh4.google.com/iciness/R1KXnU5c3YI/AAAAAAAABV4/Pxh1QQs1cDM/s144/IMG_1940.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336861023264146" target="_blank"><img src="http://lh4.google.com/iciness/R1KXpU5c3ZI/AAAAAAAABWA/xv9uSEKf4Yg/s144/IMG_1945.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336895383002530" target="_blank"><img src="http://lh4.google.com/iciness/R1KXrU5c3aI/AAAAAAAABWI/qYc7_p8mJmc/s144/IMG_1949.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336934037708210" target="_blank"><img src="http://lh5.google.com/iciness/R1KXtk5c3bI/AAAAAAAABWQ/5Jjfcy7iF6A/s144/IMG_1951.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336964102479298" target="_blank"><img src="http://lh4.google.com/iciness/R1KXvU5c3cI/AAAAAAAABWY/HzOj8TyflDU/s144/IMG_1957.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139336998462217682" target="_blank"><img src="http://lh4.google.com/iciness/R1KXxU5c3dI/AAAAAAAABWg/Sjr6VD3GcZc/s144/IMG_1959.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139337007052152290" target="_blank"><img src="http://lh6.google.com/iciness/R1KXx05c3eI/AAAAAAAABWo/L3KHkdK6JLY/s144/IMG_1961.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139337032821956082" target="_blank"><img src="http://lh4.google.com/iciness/R1KXzU5c3fI/AAAAAAAABWw/u6R5Alcmdkk/s144/IMG_1964.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139337067181694466" target="_blank"><img src="http://lh4.google.com/iciness/R1KX1U5c3gI/AAAAAAAABW4/Gtssn3L2g5I/s144/IMG_1965.JPG" /></a><a href="http://picasaweb.google.com/iciness/2007_12_01/photo#5139337071476661778" target="_blank"><img src="http://lh5.google.com/iciness/R1KX1k5c3hI/AAAAAAAABXA/eiCevtLvZXk/s144/IMG_1970.JPG" /></a></p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/147/</link>
<title><![CDATA[夜爬老和山]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Sun, 02 Dec 2007 09:55:52 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/147/</guid> 
<description>
<![CDATA[ 
	<p>跟着专业人士爬上真累啊，那个不是爬山那个是跑山。</p> <p>周三晚上和同事参加了某个论坛的日常夜爬活动，本想一路好好拍照，结果和急行军一样。</p> <p>来回8.19公里，用来了近3个小时从古荡上山终点北高峰，原路返回。差不多向上爬了637米，最高处是302米。呵呵，有GPS就是好，连路径也记录下来了。</p> <p><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138203175664392354" target="_blank"><img src="http://lh5.google.com/iciness/R06QkJ1CmKI/AAAAAAAABUg/lK6HrMMHtqg/s400/google.jpg" border="0"></a></p>&nbsp;&nbsp;<p>这里顺带附上google erarh用的kml文件。<a href="attachment.php?fid=3">点击这里下载文件</a></p> <p>原路回来的时候比较轻松，没有同专业人士一同行动，慢悠悠的还是稍微拍了点，不过也明白了一个道理，脚架是不可缺的。</p> <p><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138198811977619522" target="_blank"><img src="http://lh5.google.com/iciness/R06MmJ1CmEI/AAAAAAAABS8/IudlsSBliXs/s288/IMG_1912.JPG" border="0"></a><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138198816272586834" target="_blank"><img src="http://lh6.google.com/iciness/R06MmZ1CmFI/AAAAAAAABTE/n-vMTVI-owg/s288/IMG_1923.JPG" border="0"></a><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138198820567554146" target="_blank"><img src="http://lh3.google.com/iciness/R06Mmp1CmGI/AAAAAAAABTM/MNQzbWtV4e4/s288/IMG_1924.JPG" border="0"></a><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138198820567554162" target="_blank"><img src="http://lh3.google.com/iciness/R06Mmp1CmHI/AAAAAAAABTU/ieLWPn8Go6U/s288/IMG_1930.JPG" border="0"></a><a href="http://picasaweb.google.com/iciness/2007_11_28/photo#5138198824862521474" target="_blank"><img src="http://lh4.google.com/iciness/R06Mm51CmII/AAAAAAAABTc/fEjw7_7Oszg/s288/IMG_1937.JPG" border="0"></a></p><br/>Tags - <a href="http://www.newtyper.com/blog/tags/%25E7%2588%25AC%25E5%25B1%25B1/" rel="tag">爬山</a>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/146/</link>
<title><![CDATA[帝都4日]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[N日怪谈]]></category>
<pubDate>Sun, 25 Nov 2007 13:25:48 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/146/</guid> 
<description>
<![CDATA[ 
	<p>原定计划1天返杭的计划被彻底打破，一直被帝都挽留，直到周五还得回乡。连续2次48小时通宵（都是加班）……一到家倒头就睡了24小时。嗯，眼袋没了，脸上发的东西也没了，疲劳不单单对女性的皮肤有害……</p> <p>19日晚6点多出发的Z10，貌似我在Z10上从来没有好好睡着过。次日早上7点多列车顺利到达北京，走出车厢的时候我发觉……上了老爸老妈的当，北京并没有那么冷。</p> <p>分公司在国贸桥边上，所以直接坐地铁到达，到达公司，发现公司人去楼空状……心中一惊，打电话给在北京老大居然不接，打电话给在杭的CTO竟然说你没走错啊……我当场囧了。也罢，好在人去楼空的办公室没有锁门，于是就闯进去透过窗户拍了几张早晨的北京。</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457550631442210" target="_blank"><img src="http://lh4.google.com/iciness/R0hc7Z1ClyI/AAAAAAAABPc/5_P7svFf53Q/s400/IMG_1814.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457559221376818" target="_blank"><img src="http://lh6.google.com/iciness/R0hc751ClzI/AAAAAAAABPk/dAMDEDbHkv4/s400/IMG_1817.JPG"></a></p>&nbsp;&nbsp;<p>拍了会不久在北京的老大给我电话了，分公司刚搬到楼下了而已，一场虚惊……（或者说我真沉着冷静啊，哈哈哈哈）</p> <p>下午同事开车带我去公干，沿着长安街往西客站走，路过了雄伟的天安门（=。=虽然不是第一次路过）、政府的心脏新华门（恩看到非常河蟹的一幕，具体是啥私下交流）、做人不能太CCTV的中央电视台……其他就不一一描述了。</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457563516344130" target="_blank"><img src="http://lh3.google.com/iciness/R0hc8J1Cl0I/AAAAAAAABPs/mLzMG4kxJGE/s288/IMG_1822.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457576401246034" target="_blank"><img src="http://lh6.google.com/iciness/R0hc851Cl1I/AAAAAAAABP0/xvwxgdcDv9Y/s288/IMG_1826.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457584991180642" target="_blank"><img src="http://lh4.google.com/iciness/R0hc9Z1Cl2I/AAAAAAAABP8/pPPwzJPUnrs/s288/IMG_1837.JPG"></a></p> <p>由于RP不佳等了一下午处理好事情后，发现合同有误。好在甲方很爽快的说明天解决于是我被迫出差追加一天。</p> <p>当晚配合移不动多数省份割接预演，晚上通宵……48小时没好好睡了，和CTO抱怨，得到的答复：“你是铁人嘛！”</p> <p>铁人就铁人吧，半夜从兄弟公司返回后就在公司的椅子上入睡了，第二天一早吓到卫生阿姨和同事各一人，笑。</p> <p>北京室内的空调没得说，一个字热，二个字太热了………21日上午和甲方联系，我被迫出差继续追加一天，“今天不行，要明天下午”对方如是说。下午继续睡，同事称你已经快成美国时间了，我只能报以苦笑。傍晚去24小时小超市7-11买了洗刷用品、咖啡和一盒朗姆口味的冰淇淋，花了42RMB……上楼才发现冰淇淋价值14RMB，呃，很好吃，真的很好吃（第2天我又去买了合绿茶味的）。</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457597876082546" target="_blank"><img src="http://lh3.google.com/iciness/R0hc-J1Cl3I/AAAAAAAABQE/3OOvAYsFl38/s400/IMG_1839.JPG"></a></p> <p>晚上跑去朋友这里蹭饭，水煮鱼非常的好吃，说为蹭饭实际结果最后还是我掏的钱包。朋友在传媒大学，当晚传媒大学在举行世界小姐红十字公益晚会。由于无法闯入晚会现场，我只得在门口拍了世界小姐们的亲笔签名……</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457610760984450" target="_blank"><img src="http://lh6.google.com/iciness/R0hc-51Cl4I/AAAAAAAABQM/0YOW8Jf5GO8/s400/IMG_1840.JPG"></a></p> <p>回去的时候已经有点晚了，再加上八通线上的人不多，轻轨站上几乎没人。</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457619350919058" target="_blank"><img src="http://lh4.google.com/iciness/R0hc_Z1Cl5I/AAAAAAAABQU/89GeOCprjrE/s400/IMG_1843.JPG"></a></p> <p>当天晚上就不用在公司里歪着脖子熬夜了，和在北京的老大去公司自己的宿舍投宿，宿舍在公司不远（……其实有3站路之遥，在北京真的不能算远）的华腾园中某高层的27楼。（呃，片子是没支架曝光3.2秒出来的，那天出奇的端的稳）</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457623645886370" target="_blank"><img src="http://lh5.google.com/iciness/R0hc_p1Cl6I/AAAAAAAABQc/whNbwFCZQu0/s400/IMG_1852.JPG"></a></p> <p>22日是最顺利的一天，出差的主要目的完成，第二日一早回杭的机票也定好。剩下就是当晚还有移不动其他省份的割接预演要参加，又是一个不眠夜。公司搬了楼层后视线不好，夜景也拍不到多少。</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457636530788274" target="_blank"><img src="http://lh4.google.com/iciness/R0hdAZ1Cl7I/AAAAAAAABRo/MhOZSELt2fo/s400/IMG_1866.JPG"></a></p> <p>早晨只睡了1个小时就爬起坐上TAXI赶往机场，由于前一天我已经在网站上办好值机手续，下了出租车我就直奔安检。</p> <p>凌晨的首都机场有点薄雾，不知道为什么我对机场的指挥塔特别有感情，难不到是因为以前玩模拟飞行老喜欢撞指挥塔？</p> <p>6点半到的机场，飞机原定是8点15分起飞，航空公司称由于萧山机场大雾所以航班延误，整整延误了快2个小时……</p> <p><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457640825755586" target="_blank"><img src="http://lh5.google.com/iciness/R0hdAp1Cl8I/AAAAAAAABQw/ROS3Ray2rts/s288/IMG_1874.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457645120722898" target="_blank"><img src="http://lh6.google.com/iciness/R0hdA51Cl9I/AAAAAAAABQ4/k37qDwVBFQ4/s288/IMG_1878.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457653710657506" target="_blank"><img src="http://lh4.google.com/iciness/R0hdBZ1Cl-I/AAAAAAAABRA/sy1iGSzXpK8/s288/IMG_1880.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457662300592114" target="_blank"><img src="http://lh6.google.com/iciness/R0hdB51Cl_I/AAAAAAAABRI/6FTriF0p60I/s288/IMG_1889.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457670890526722" target="_blank"><img src="http://lh4.google.com/iciness/R0hdCZ1CmAI/AAAAAAAABRQ/V_CDSkh_qzk/s288/IMG_1895.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457679480461330" target="_blank"><img src="http://lh6.google.com/iciness/R0hdC51CmBI/AAAAAAAABRY/snqiGeQbpJw/s288/IMG_1896.JPG"></a><a href="http://picasaweb.google.com/iciness/Beijing/photo#5136457683775428642" target="_blank"><img src="http://lh3.google.com/iciness/R0hdDJ1CmCI/AAAAAAAABRg/9uXIhRUw-Sw/s288/IMG_1900.JPG"></a></p> <p>中午12点我终于回到了杭州……</p>
]]>
</description>
</item><item>
<link>http://www.newtyper.com/blog/post/145/</link>
<title><![CDATA[海豹突击队 战术打击 第一关录像（测试PSP视频录像和TUDO）]]></title> 
<author>毫无逻辑 &lt;iciness@gmail.com&gt;</author>
<category><![CDATA[小特土产]]></category>
<pubDate>Sun, 25 Nov 2007 10:16:46 +0000</pubDate> 
<guid>http://www.newtyper.com/blog/post/145/</guid> 
<description>
<![CDATA[ 
	<p>新P终于可以录像了，用irshell 3.81将PSP视频输出到电脑上然后用录屏软件录像。第一次上传视频到tudo，youtube上传真慢...</p> <p><embed src="http://www.tudou.com/v/n6Km6PfHINQ" width="400" height="300" type="application/x-shockwave-flash" allowfullscreen="true" wmode="transparent" allowscriptaccess="always"></embed></p>
]]>
</description>
</item>
</channel>
</rss>