Evan's Blog

Java, software development and others.

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  28 随笔 :: 0 文章 :: 73 评论 :: 0 Trackbacks

#

J2SE 5.0在多线程上做了很大的改进,提供了更多的API,包括:
Atomic variables: A set of classes that provide threadsafe operations without synchronization
Explicit locks: Synchronization locks that can be acquired and released programmatically
Condition variables: Variables that can be the subject of a targeted notification when certain conditions exist
Queues: Collection classes that are thread-aware Synchronization primitives: New classes that perform complex types of synchronization
Thread pools: Classes that can manage a pool of threads to run certain tasks
Thread schedulers: Classes that can execute tasks at a particular point in time

在《Java Threads》一书中将其归纳为三类:
1. 对现有功能的新实现;
2. 提供了重要的多线程工具,如线程池(pool)和计划(schedule);
3. 最小化同步工具(Minimal synchronization utilities)。

这些功能的妙处我现在自然是无法体会得到,但对于JDK 5.0中提供的这些多线程工具,会不会也遭遇JDK 1.4提供的Log API的命运,因敌不过第三方工具而成为摆设呢(至少目前我还在用Log4J,且其也没有停止开发的迹象)?
posted @ 2006-03-05 23:25 Evan 阅读(398) | 评论 (0)编辑 收藏

Chapter2: Thread Creation and Management

2.1 What Is a Thread?

介绍了什么是线程,以及线程(thread, multithread)与进程(process, mutltitask)的区别。其中的一个重要区别是对共享数据的访问。进程可以共享操作系统级别的某些数据区域,如剪贴板;而线程是对程序自有的数据进行共享。进程之间对共享数据的存取方法是特殊的,因此自然能够得到程序员的注意;而线程之间对共享数据的存取与对线程自己的数据的存取方法是一样的,所以常常比较隐蔽,而被程序员忽略。其实,这也是多线程开发比较难的地方。所以,这一节最后说:"A thread, then, is a discrete task that operates on data shared with other threads.(线程就是一个在与其它线程共享的数据上进行操作的单独任务。)"

2.2 Creating a Thread

1. 有两种方法创建线程:使用Thread类或者Runnable接口
2. 示例程序竟然是编一个打字游戏,所以,这本书的门槛还是有点高的:(。当然可以从该书的站点直接下载代码

3. Thread class
package java.lang;
public class Thread implements Runnable {
public Thread( );
public Thread(Runnable target);
public Thread(ThreadGroup group, Runnable target);
public Thread(String name);
public Thread(ThreadGroup group, String name);
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name);
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize);
public void start( );
public void run( );
}
Thread name: 线程名,用以打印线程信息时用,缺省为Thread-N。
Runnable target:可运行的目标。一个可运行的对象就是一串可由线程执行的指令。缺省就是在run()方法中编写的内容。
Thread group:线程组。对于大多数应用来说没什么意义,缺省在同一组。
Stack size:线程堆栈大小,与平台相关。为了使程序更具可移植性,并不推荐使用。

2.3 The Lifecycle of a Thread

 

1. Creating a Thread:创建一个线程对象,并没有开始执行一个线程
2. Starting a Thread:在调用线程对象的start()方法前,一直处于等待状态。可以通过调用isAlive()方法来获取线程是否正在运行;
3. Terminating a Thread:线程在以下情况下结束  
  1)执行完run()中的语句;
  2)抛出一个异常,包括其没有捕获的异常;
  3)其所在的程序(或者容器)调用System.exit()。
注意:一个线程结束后,就不能再次启动,该线程就变成一个普通的对象,如果试图再次调用start()方法,将抛出java.lang.IllegalThreadStateException异常。如果想多次运行,要么创建新的对象;要么就是不要结束该线程。
4. Java并没有提供可靠的方法来暂停、挂起或者重启一个线程的方法,线程本身能通过sleep()函数来暂停执行。目前依然只能确保若干毫秒级别的精度。
5. Thread Cleanup:线程终止后,线程对象依然存在。可以通过join()方法以阻塞方式(blocked)来等待某个线程终止。

2.4 Two Approches to Stop a Thread

1. Setting a Flag:在线程的执行过程中判断其它线程是否将标志置位。其缺点是有时间延迟,尤其是当进入了某些被阻塞的函数如:sleep(), wait()等;
2. 调用线程的interrupt()方法,线程的执行过程中改为判断isInterrupted()的返回值。可以解决线程进入阻塞导致的延迟,但依然解决不了超长时间计算而导致的延迟。interrupt()有两个作用:1)终止sleep()和wait(),抛出InterruptedException;2)使isInterrupted()返回true。下面这段代码将演示interrupt()的作用:

public class TestInterrupted {
public static void main(String args[]) throws InterruptedException{
Foo foo = new Foo();
foo.start();
Thread.currentThread().sleep(100); //注释掉这句有什么影响呢?
System.out.println("before interrupt");
foo.interrupt();
System.out.println("after interrupt");
} }

class Foo extends Thread {
public void run() {
try{
while(!isInterrupted()) {
System.out.println("start calculating...");
double pi = 3.1415926;
for(int i=0; i<5; i++) {
for(long j=0; j<1000000; j++) {
pi *= pi;
}
System.out.println("i="+i);
}
System.out.println("before sleep");
sleep(5000); //注释掉这句及相关的try...catch语句,又怎样呢?
System.out.println("after sleep");
}
} catch(InterruptedException ex) {
ex.printStackTrace(System.out);
}
}
}

 

2.5 The Runnable Interface

为什么有了Thread类,还要有Runnable接口呢?最主要的原因是为了解决Java不能多重继承的问题。线程继承自Thread,还是仅仅实现Runnable接口,取决于这个线程的作用。不过,如果仅仅实现Runnable接口,则在线程里不能使用Thread类的方法,比如interrupt()和isInterrupted()。当然,这个还是要取决于实际情况。

2.6 Threads and Objects

主要讲解线程与对象的关系。线程根本上还是个对象,其可以存取任何对象包括其它线程对象,当然也能被其它对象存取,只要没有因为争夺公共资源而被锁定,线程对象中的任何属性和方法都有可能同时执行。并且Java中的锁只针对对象本身,并不包括对象下的属性;而对方法同步,则等同于对对象同步。更进一步的说明还可以参考《Practical Java》中的"实践46:面对instance函数,synchronized锁定的是对象(object)而非函数(methods)或代码(code)"。下面用两段代码来说明一下:
代码1:

public class TestLock {
public static void main(String args[])
throws InterruptedException{
Foo foo = new Foo();
foo.start();
Thread t = Thread.currentThread();
for(int i=0; i<10; i++) {
foo.setInt2("Main", i, i+20);
}
}
}

class Foo extends Thread{
protected int arrI[] = new int[10];

public void run() {
try {
for(int i=0; i<10; i++) {
setInt("Foo", i, i);
}
} catch(InterruptedException ex) {}
}

public synchronized void setInt(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}

public void setInt2(String from, int pos, int val)
throws InterruptedException {
synchronized(arrI){
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}
}
}
结果:非线程安全,setInt()在对象上加锁,而setInt2()在属性arrI上加锁,不同的锁不能保证线程安全。可能的结果如下:
Foo:arrI[0]=0 
Main:arrI[0]=0
Main:arrI[1]=21
Main:arrI[2]=22
Foo:arrI[1]=21
Main:arrI[3]=23
Main:arrI[4]=24
Main:arrI[5]=25
Foo:arrI[2]=2
Main:arrI[6]=26
Main:arrI[7]=27
Foo:arrI[3]=3
Foo:arrI[4]=4
Main:arrI[8]=28
Main:arrI[9]=29
Foo:arrI[5]=5
Foo:arrI[6]=6
Foo:arrI[7]=7
Foo:arrI[8]=8
Foo:arrI[9]=9

代码2:
public class TestLock1 {
public static void main(String args[])
throws InterruptedException{
Foo1 foo = new Foo1();
foo.start();
Thread t = Thread.currentThread();
for(int i=0; i<10; i++) {
foo.setInt2("Main", i, i+20);
}
}
}

class Foo1 extends Thread{
protected int arrI[] = new int[10];

public void run() {
try{
for(int i=0; i<10; i++) {
setInt("Foo", i, i);
}
}catch(InterruptedException ex){}
}

public synchronized void setInt(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
}

public synchronized void setInt2(String from, int pos, int val)
throws InterruptedException{
arrI[pos] = val;
sleep((long)(Math.random()*5000));
System.out.println(from+":arrI["+pos+"]="+arrI[pos]);
  }
}
结果:线程安全
posted @ 2006-03-05 23:25 Evan 阅读(673) | 评论 (0)编辑 收藏

Chapter 3. Data synchronization

第二章中介绍了如何创建线程对象、启动和终止线程。但多线程编程的关键在于多个线程之间数据的共享和同步,从这一章开始,将详细介绍线程之间数据的共享和同步的各种方法。

3.1 The Synchronized Keywor
1. synchronized是Java中最基本也最常用的用来编写多线程安全代码的关键字,用以保护对多线程共享的数据的操作总是完整的;
2. Atomic: 当一个操作被定义成原子操作时,意味着该操作在执行过程中不会被打断;原子操作可以由硬件保证或者通过软件来模拟;
3. Mutex Lock: 在很多多线程系统,通过互斥锁来保护的共享数据。Java中的任何一个对象都有一个与之相关的锁,当一个方法被声明成synchronized时,就表示当线程进入该方法前,必须获得相应对象的锁,在执行完毕再释放这个锁。从而保证同一时刻只有一个线程调用该对象上的被声明为synchronized的方法。注意:Java的互斥锁只能加在对象级上,获得某个对象的锁,并不能保证该对象的属性和其它非synchronized的方法是线程安全的;也不能保证受保护的方法里调用的其它对象是多线程安全的,除非任何调用这些没有被保护的方法或者对象只通过受保护的方法进行调用。所以,编写线程安全的代码关键就在于规划方法和对象之间的调用关系,并尽量采用相同对象的锁来进行同步控制。

3.2 The Volatile Keyword
1. Scope of a Lock: 锁的作用范围即获得和释放锁之间的那段时间。
2. Java标准虽然声明存取一个非long和double变量的操作是原子操作,但由于不同虚拟机实现的差异,在多线程环境下每个线程可能会保留自己的工作拷贝,而导致变量的值产生冲突。为了避免这种情况的发生,可以有两种方法:
  1) 为变量创建声明为synchronized的setter和getter方法,然后任何调用(包括在类类部)该变量的地方都通过setter和getter方法;
  2) 采用volatile声明,确保每次存取这些属性时都从主内存中读入或者写入主内存中;
  3) volatile仅仅用于解决Java内存模式导致的问题,只能运用在对该变量只做一个单一装载或写入操作且该方法的其它操作并不依赖该变量的变化。如:在一个循环体中作为递增或递减变量时就不能使用volatile来解决线程同步的问题。
3. volatile的使用是有限的,一般而言,仅仅将其作为强制虚拟机总是从主内存读写变量的一个手段,或者某些需要其参数声明为volatile的函数。

3.3 More on Race Conditions
本节以打字游戏中显示成绩的例子来解释了可能存在的race condition。关键要注意以下几点:
1. 操作系统会随机的切换多个线程的运行,因此当多个线程调用同一个方法时,可能在任何地方被暂停而将控制权交给另外的线程;
2. 为了减少被误用的可能,总是假设方法有可能被多个线程调用;
3. 锁仅仅与某个特定实例相关,而与任何方法和类都无关,这一点当需要存取类属性或方法时要特别注意;
4. 任何时候只有一个线程能够运行某个类中一个被声明为synchronized的静态方法;一个线程只能运行某个特定实例中一个被声明为synchronized的非静态方法。

3.4 Explicit Locking
1. 学过Win32下编写多线程的朋友刚开始可能会被Java的Synchronized关键词搞糊涂。因为Java中的任何一个对象都有一个与之相关的锁,而不象在Win32下要先定义一个互斥量,然后再调用一个函数进入或者离开互斥区域。在JDK 1.5以后也开始提供这种显示声明的锁。JDK 1.5中定义了一个Lock接口和一些类,允许程序员显示的使用锁对象。
2. 在Lock接口里有两个方法:lock()和unlock()用来获取和释放锁对象,从而保证受保护的代码区域是线程安全的。
3. 使用锁对象的一个好处在于可以被保存、传递和抛弃,以在比较复杂的多线程应用中使用统一的锁。
在使用锁对象时,总是将lock()和unlock()调用包含在try/finally块中,以防止运行时异常的抛出而导致死锁的情况。

3.5 Lock Scope
利用lock()和unlock()方法,我们可以在任何地方使用它们,从一行代码到跨越多个方法和对象,这样就能根据程序设计需要来定义锁的作用(scope of lock)范围,而不是象以前局限在对象的层次上。

3.5.1 Synchronized Blocks

1. 利用synchronized关键字也能建立同步块,而不一定非得同步整个方法。
2. 同步一段代码时,需要明确的指定获取哪个对象的锁(这就类似于锁对象了),这样,就可以在多个方法或对象中共享这个锁对象。

3.6 Choosing a Locking Mechanism
使用锁对象还是同步关键字(synchronized),这个取决于开发员自己。使用synchronized比较简单,但相对而言,比较隐晦,在比较复杂的情况下(如要同时同步静态和非静态方法时),没有锁对象来得直观和统一。一般而言,synchronized简单,易于使用;但同时相对而言效率较低,且不能跨越多个方法。

3.6.1 The Lock Interface
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit)
throws InterruptedException;
void unlock();
Condition newCondition();
}
其中的tryLock()和tryLock(long, TimeUnit)将尝试获取锁对象,如果不能获取或在指定时间内不能获取,将立即返回。调用者可以根据返回值来判断是否获得了锁对象以做进一步的操作。

3.7 Nested Locks
1. 在一个同步方法内调用同一个对象上的另外的同步方法的情况,称之为嵌套锁(nested locking)。系统将自动执行嵌套的同步方法,而无须等待锁的释放。因此,有的时候,即使一些私有方法仅仅被已同步的方法调用,我们也给其加上synchronized关键字,以减少后续维护时可能产生的误导。
2. ReenterantLock类也支持嵌套锁。在ReenterantLock类维持一个计数器,只有当这个计数器为0时,才会释放锁。注意:这个特性是ReenterantLock类的特性,而不是所有实现Lock接口的类的特性。
3. 需要支持嵌套锁的一个原因是方法之间交叉调用(cross-calling)。设想对象a的方法M1调用对象b的方法N1,然后N1再调用对象a的方法M2,而M1和M2都是同步方法,如果不支持嵌套锁,则N1将在调用M2时等待M1释放锁,而M1则由于N1没有返回永远也不会释放锁,这样就产生了死锁。
4. synchronized和Lock接口并没有提供锁对象被嵌套获取的次数,但ReentrantLock则提供了这样一种机制:
public class ReentrantLock implements Lock {
public int getHoldCount();
public boolean isLocked();
public boolean isHeldByCurrentThread();
public int getQueueLength();
...
}
其中:
    1) getHoldCount()返回当前线程的获取次数,返回0并不表示该锁是可获取的,有可能没有被当前线程获得;
    2) isLocked()判断该锁对象是否被任何线程获得;
    3) isHeldByCurrentThread()判断是否由当前线程获得;
    4) getQueueLength()用来估计当前有多少线程在等待获取这个锁对象。

3.8 Deadlock
介绍了死锁的概念,并修改例子代码来演示deadlock的产生。死锁一般产生在多个线程在多个锁上同步时产生的;当然,多个线程在判断多个条件的时候,也有可能产生死锁。

3.9 Lock Fairness
1. Java里锁的获取机制依赖于底层多线程系统的实现,并不保证一个特定的顺序;
2. ReentrantLock类提供一个先进先出(first-in-first-out)的获取锁的选项(在创建锁对象时传入true值)。
文章来源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616068.aspx
posted @ 2006-03-05 23:25 Evan 阅读(498) | 评论 (0)编辑 收藏

从2006年第五期的《读者》上看到下面几段言论,觉得颇有点意思,摘录如下:
互联网就像是酒,看你怎么用,如果你生性孤僻,互联网会让你更加孤僻,如果你要和别人交流,它也会很容易让你和别人交流。
——某位技术专家说
其实,按我的说法,互联网应该是个加速器,它往往会加剧你性格中不太容易控制的方面而变得益发不可收拾。当然,这只是对于12px的普通网友而言;而少数精英却能从中获得不菲的名利。

爱情之酒甜而苦。两人喝,是甘露;三人喝,是酸醋;随便喝,要中毒。
——陶行知

不是怀乡,没有乡愁;不是近乡,没有情怯;不是还乡,没有衣锦;不是林黛玉,没有眼泪。
——李敖自述访问大陆的心情
不管怎样说,李大师还是很有才气的,并且他特立独行、有仇必报的性格——不管好坏——也是颇令人钦佩的。只是毕竟岁月不饶人,李大师越来越有点叨唠的问题了。同样的一些话、一些事会在一系列相关的场合连续不断的说,不怕你耳朵不起茧。就像如果人家问他的风流趣事,他基本上都会说那个17朵玫瑰的故事一样。然后一众主持和现场观众都一定会做出钦佩至极,大呼高明状。

情如鱼水是夫妻双方最高的追求,但是我们都容易犯一个错误,即总认为自己是水,而对方是鱼。
——BBS经典语录
其实,总有一个是鱼,一个是水的。再说,也不一定是水的必定占上风呀。无水的鱼固然活不了,但聪明的鱼总会在干死前跳到另外的水中;而无鱼的水,也必会少了生机,甚而至于发臭了。

人要有三个头脑:天生的一个头脑,从书中得来的一个头脑,从生活中得来的一个头脑。
——契珂夫
人往往比较容易受第一个头脑的控制,受第三个头脑的诱惑,第二个头脑往往比较难以获得,在决策时也比较难以取得主动权。

除非你是比尔.盖茨,否则你跟破产之间仅有一场重病之隔。
——美国进行的一项调查发现,美国有半数破产者是被猛涨的医药费所拖累,主持这项调查的一位教授如此感叹。
如果这不是托,那么中国的现状似乎也不是个别的现象。但是,在中国,也许还有另外一个结局,那就是等死,即使得的不是绝症

posted @ 2006-03-05 23:25 Evan 阅读(265) | 评论 (0)编辑 收藏


播放视频文件

引用内容
我在这儿等着你回来
等着你回来
看那桃花开
把那花儿采
桃花朵朵开
暖暖的春风迎面吹
枝头鸟儿成双对
情人心花儿开
啊哟啊哟
你比花还美妙
叫我忘不了
秋又去春又来
记得我的爱
给你把花戴
尝尝家乡菜
团圆乐开怀
暖暖的春风迎面收
别把我忘怀
把那花儿拣
【听后感】对我而言,阿牛的歌,属于还能听一听的范围,至少可以让人轻松一点,开心一下。阿牛春节前去台湾宣传,上吴宗宪的节目,被吴恶搞了一把,喝掉大半扎各种调料混在一起的东东,主持和其他嘉宾在前面自娱自乐去了,他一个人在旁边默默地喝着。然后吴回过来头大吃一惊,说:“谁让你全喝掉的。”为表风范,吴表示要陪喝,可是连一口也喝不下去的。可见阿牛有多么老实天真了。估计,阿牛以后看到吴宗宪的背影就怕了,呵呵。



文章来源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616108.aspx
posted @ 2006-03-05 23:25 Evan 阅读(328) | 评论 (0)编辑 收藏

播放视频文件
 

故事背景是很感人的网络小说《你的肩上有蜻蜓么》
引用内容
有一对非常恩爱的恋人,他们每天都去海边看日出,晚上去海边送夕阳,可是有一天,在一场车祸中,女孩不幸受了重伤,几天几夜都没有醒过来,男孩日夜守在床前,哭干了眼泪。

一个月过去了,女孩仍然昏睡,而男孩早已憔悴不堪了,看他仍苦苦支撑。上帝被他感动了,决定给这个执着的男孩一个例外,上帝问他:“你愿意用你自己的生命作为交换吗?”男孩毫不犹豫的回答“我愿意”上帝说:“那好吧,我可以让你的恋人很快醒来,看你要答应我 化做三年的蜻蜓。”

天亮了,南海变成了一只蜻蜓,他匆匆地飞到了医院,女孩真的醒了,她还在跟身旁的一名医生在交谈。

女孩出院了,但是她并不快乐。她四处打听男孩的下落,女孩整天不停地寻找,早已化成蜻蜓的男孩却无时无刻不围绕在的身边,只是他不会呼喊,不会拥抱,他只能默默地承受着她视而不见。夏天过去了,蜻蜓不得不离开这里,他最后一次飞落在女孩的肩上。他想用细小的嘴来亲亲她的额头,然而他弱小的身体不足以被她发现。春天来了,蜻蜓迫不及待地飞回来,然而,她那熟悉的身影旁站着高大而英俊的男人,那一刹那,蜻蜓几乎快从半空中跌落下来。蜻蜓伤心极了,他常常会看到那个男人带着自己的恋人在海边看日出,晚上又在海边看日落,而他自己除了偶然能停落在的肩上之外,什么也做不了。第三年夏天,蜻蜓已不再常常去看望自己的恋人了。她的肩被男医生拥着,脸被男医生轻轻地吻着,根本没时间去留意一只伤心的蜻蜓,更没有心情去怀念过去。三年的期限很快就要到了。最后一天,蜻蜓昔日的恋人跟那个男医生举行了婚礼。

蜻蜓悄悄地飞进了教堂,落在上帝的肩膀上,他听到下面的恋人对上帝发誓说“我愿意”他看着男医生把戒指戴到昔日恋人的手上,然后他看见他们甜蜜地亲吻。蜻蜓流下了伤心的眼泪。

上帝叹息着:“你后悔了吗?”蜻蜓擦干了眼泪:“没有!”上帝又带着一丝愉悦说:“明天你就可以变回你自己了。”蜻蜓摇摇头说:“就让我一辈子做蜻蜓吧……”

有些缘分是注定要失去的,爱一个人不一定要拥有,但是拥有一个人就一定要好好去爱他。你的肩上有蜻蜓吗?
【听后感】歌曲还没有细听,但觉得没有那篇小说来得感人,就像电影怎么拍也拍不出原著的味道;小说比较感人,但换了我,宁愿坚持等她醒来;或者让上帝给我另外一个选择,那就是双双化成蝴蝶,所以梁山伯与祝英台的故事比较符合中国人的口味。另外就是,爱人之间的爱一定要是双向的;如果三年后发现女孩不爱我了,用不着后悔,但我必然会再变成人,去找我的真爱。
posted @ 2006-03-05 23:25 Evan 阅读(1828) | 评论 (6)编辑 收藏


昨天晚上上了床,拿着遥控器随便转着台,突然看到周星驰的《喜剧之王》,又碰巧看到飘飘(张柏芝饰)去看伊天仇(周星驰饰)自导自演精武门,然后飘飘也自说自话地上台凑热闹,被伊天仇摔了个四脚朝天,然后坐在海滩边聊天。这时,他们之间有这样一段对话:
  飘飘:前面漆黑一片,什么也看不到。
  伊天仇:可是等到天亮,景色就会很美丽了。
这个对话大概就是这样子,是啊,如果你现在眼前一片漆黑,那倒真用不着怎样害怕,天也许就快亮了。怕的就是,眼前白茫茫一片,分不清到底是快天亮了呢,还是天要黑了。

接下来,飘飘就和伊天仇一夜风流,处于人生最低谷的伊天仇不知道这是爱情呢,还是仅仅一夜情,于是翻箱倒柜,将能找到的所有钱、手表之类的全部放在了飘飘的衣服上,继续装睡。而飘飘呢,也以为伊天仇仅仅是将自己当作风尘女子,也只能潇洒地走开。然而,当伊天仇追出来说:“我养你”的时候,我觉得应该是全剧最精彩的情节之一了。两个处于社会底层且又在自己最失意的时候,终于能够找到相互欣赏和珍惜的爱人,的确是比较感人的。难怪飘飘在出租车里拿出伊天仇一直看的那本怎样当喜剧演员的书时会泪流满面了。

唉,现在看片子,有的时候连这样一句两句令人欣赏的台词也没有了。

文章来源:http://blog.csdn.net/evanwhj/archive/2006/03/05/616120.aspx
posted @ 2006-03-05 23:25 Evan 阅读(368) | 评论 (0)编辑 收藏

仅列出标题
共3页: 上一页 1 2 3