java中的并发和线程

来源:互联网 发布:淘宝小号购买1元一个 编辑:程序博客网 时间:2024/05/06 18:32

(来自Thinking in Java 第三版)
1:通常你想使用interrupt()来中断一个挂起的线程,那么挂起的时候最好使用wait()而不是sleep(),这样就不可能在catch子句里结束了。
2:调用线程的join()方法,可以控制线程执行的顺序,这样线程就不会任意顺序执行了。
3:你必须在线程启动之前(start())调用setDaemon()方法,才能把它设置成后台线程。
你可以通过调用isDaemon()方法来确定线程是否是一个后台线程。
如果是一个后台线程,那么由它创建的任何线程将被自动设置成后台线程:其isDaemon()的值为 true。
4:在run()中,sleep()方法有可能在指定的时间期满时返回,但也可能被中断。例如在线程上显示地调用interrupt()方法。
5:没有理由吧整个类写成是Runnable类型的,因此,使用内部类把和线程有关的代码隐藏在类的内部,似乎更合理。
6:使用Timer一般来说比直接写代码调用sleep()要简单和清晰。此外,设计Timer的目的就是承担大量并发调度任务,所以它能成为很有用的工具。
7:在线程的run()方法中常采取死循环的方式,但是要控制好循环结束的条件。
System.exit(0);可以终止所有的线程,即终止程序。
8:要控制对共享资源的访问,你得先把它包装进一个对象,然后把所有要访问这个资源的方法标记为synchronized。
所以,对于某个对象,其所有synchronized方法共享同一个锁,这能防止多个线程同时访问对象所在的内存。
9:显然,只有首先获得了这个对象的锁的线程才能允许继续获取多个锁。每次线程在这个对象上获得了锁,计数都会增加1。
每当线程离开一个synchronized方法,计数减少,当计数为零,锁被完全释放,此时别的线程就可以使用此资源了。
注意,每个访问关键共享资源的方法必须全部是synchronized的,否则就会出错。
10:你只要给long或 double 类型的变量加上关键字 volatile ,那么对这种变量进行简单的赋值或是返回值操作,操作就是原子性的了。
11:一个产生序列号的类,每次调用nextSerialNumber(),它必须返回一个唯一值:
public class SerialNumberGenerator {
priate static volatile int serialNumber = 0;
public static synchronized int nextSerialNumber(){
return ++serialNumber;
}
}
注意,nextSerialNumber()必须加上关键字:synchronized。原因,在JM中的自增/减操作并不是原子的,它牵涉到一次读和一次写。
12:如果你要对类中的某个方法进行同步控制,最好同步所有方法。
13:同步控制块:
synchronized(sync Object){
  --临界区(critical section)代码;
}
使用这种同步方式,要记住它仅仅锁定对象/资源 Object,仅此而已。
14:注意wait(),notify(),以及notifyAll()是基类Object的方法,而sleep()是属于Thread的方法。
调用sleep()的时候,锁并没有释放,而在wait()期间锁是释放的。

实际上,你只能在同步控制方法或同步控制块里调用wait(),notify(),和notifyAll(),你要是在非同步控制方法里调用这些方法,程序在运行期间会报错的:调用wait(),notify(),和notifyAll()的线程在调用这些方法前必须拥有(获取)对象的锁。
而sleep()方法因为不用操作锁,它可以在非同步控制方法里调用。
wait()和notify()以及notifyAll()为线程之间的协作提供了一种方式。
15:线程死亡的通常方式是从run()方法中返回。
16:线程进入阻塞状态的常见原因有:
 1》调用了sleep(milliseconds),线程在指定的时间内不会运行。
 2》调用了wait()或wait(milliseconds)使线程被挂起。
 3》线程视图在某个对象上调用其某个同步方法,但是对象锁当前不可用。
 4》线程在等待某个I/O的完成。
17:一个程序可能看起来运行正确,但确实存在死锁的危险。因此,在编写并发程序的时候,进行仔细的程序设计以防止死锁是一个关键部分。
18:只有当以下四个条件同时满足时,就会发生死锁:
1》互斥条件:线程使用的资源中至少有一个是不能共享的。这里,一根筷子一次就只能被一个哲学家使用。
2》至少有一个进程使用一个资源,并且它在等待获取一个当前被别的进程持有的资源。这里,哲学家必须拿着一根筷子并且等待另一根。
3》资源不能被进程抢占。这里,哲学家不会从其他哲学家那里抢筷子。
4》必须有循环等待。这里,因为每个哲学家都试图先得到左手的筷子,然后得到右手的筷子,所以发生了循环等待。
19:要防止死锁,只需破坏其中一个条件即可。在此程序中,防止死锁最容易的方法是破坏条件四。这里,让最后一个哲学家先去拿右手的筷子,然后再拿左手的筷子,这样就不会发生死锁。
20:
public static void main(){
final CanStop stoppable = new CanStop();
stoppable.start();
new Timer(true).schedule(new TimerTask(){
public void run(){
System.out.println("Requesting stop");
stoppable.requestStop();
}
},500);
}
在main()中启动了CanStop线程,然后设置了一个Timer,用来在半秒后调用requestStop()。
Timer的构造器传入一个参数true,这使其成为一个后台线程,使得它不会阻碍程序的终止。
21:当线程A调用了a.wait()阻塞后,你可以人为的调用a.interrupt()来中断这个阻塞的线程。
追加上 a == null;这行代码来释放线程a占有的内存资源。
22:多余的线程上下文切换(例如:很短暂的延迟)将导致性能降低,而使用yield()或sleep()能防止这种多余的切换。

原创粉丝点击