Java线程

在Java中,每个线程的创建和控制都是由java.lang.Thread类的独特对象实现的。一个独立 的应用运行时,会自动创建一个用户线程,执行main()方法。这个线程叫作主线程。 在Java中,实现线程有以下两种方式:

  •  通过实现java.lang.Runnable接口;
  •  通过扩展java.lang.Thread类。

死锁及死锁的预防

死锁(deadlock)是这样一种情形:第一个线程在等待第二个线程持有的某个对象锁,而第 二个线程又在等待第一个线程持有的对象锁(或是由两个以上线程形成的类似情形)。由于每个线程都在等其他线程释放锁,以致每个线程都会一直这么等下去。于是,这些线程就陷入了所谓 的死锁。 死锁的出现必须同时满足以下四个条件。

  • (1) 互斥:某一时刻只有一个进程能访问某一资源。(或者,更准确地说,对某一资源的访问 有限制。若资源数量有限,也可能出现死锁。)

  • (2) 持有并等待:已持有某一资源的进程不必释放当前拥有的资源,就能要求更多的资源。

  • (3) 没有抢占:一个进程不能强制另一个进程释放资源。

  • (4) 循环等待:两个或两个以上的进程形成循环链,每个进程都在等待循环链中另一进程持 有的资源。

若要预防死锁,只需避免上述任一条件,但这很棘手,因为其中有些条件很难满足。比如, 想要避免条件1就很困难,因为许多资源同一时刻只能被一个进程使用(如打印机)。大部分预防 死锁的算法都把重心放在避免条件4即循环等待上。

16.1 线程和进程有何区别?(第 103 页)

解法

进程和线程彼此有关联,但两者有着根本上的不同。

进程可以看作是程序执行时的实例,是一个分配了系统资源(比如CPU时间和内存)的独立实体。每个进程都在各自独立的地址空间里执行,一个进程无法访问另一个进程的变量和数据结 构。如果一个进程想要访问其他进程的资源,就必须使用进程间通信机制,包括管道、文件、套接字(socket)及其他形式。

线程存在于进程中,共享进程的资源(包括它的堆空间)。同一进程里的多个线程将共享同 一个堆空间。这跟进程大不相同,一个进程不能直接访问另一个进程的内存。不过,每个线程仍然会有自己的寄存器和栈,而其他线程可以读写堆内存。

线程是进程的某条执行路径。当某个线程修改进程资源时,其他兄弟线程就会立即看到由此 产生的变化。

16.3 在著名的哲学家就餐问题中,一群哲学家围坐在圆桌周围,每两位哲学家之间有一 根筷子。每位哲学家需要两根筷子才能用餐,并且一定会先拿起左手边的筷子,然后才会去拿 右手边的筷子。如果所有哲学家在同一时间拿起左手边的筷子,就有可能造成死锁。请使用线 程和锁,编写代码模拟哲学家就餐问题,避免出现死锁。(第 103 页)

如果所有哲学家都拿起左手边的一根筷子,并都等着拿右手边的另一根筷子,运行上面的代 码就可能造成死锁。

为了防止发生死锁,我们的实现可以采用如下策略:如有哲学家拿不到右手边的筷子,就让 他放下已拿到的左手边的筷子。

在上面的代码里,要确保拿不到右手边的筷子时就要放下左手边的筷子;如果手上根本没有 筷子,就不该调用putDown()。

16.4 设计一个类,只有在不可能发生死锁的情况下,才会提供锁。(第 103 页)

防止死锁有几种常见的方法,其中常用的做法之一是,要求进程事先声明它需要哪些锁。

这个类需要一个 declare 方法, 线程和进程会以该方法声明它们请求资源的顺序。 这个 declare方法将迭代访问声明顺序,将邻近的每对元素(v, w)加到图里。然后,它会检查是否存 在环。如果存在环,它就会原路返回,从图中移除这些边,然后退出

:如何检测有无环?我们可以通过对每个连接起来的部分(也就 是图中每个连接在一起的部分)执行深度优先搜索来检测有没有环。也有算法能选择图中所有连 接的部分,但那样就会更复杂了。就此题而言,还没必要复杂到这个程度。

我们可以确定,如果出现了环,就表明是某一条新加入的边造成的。

进程间通信

本地进程间通信的方式有很多,可以总结为下面四类:

  • 消息传递(管道、FIFO、消息队列)
  • 同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量)
  • 共享内存(匿名的和具名的)
  • 远程过程调用(Solaris门和Sun RPC)

results matching ""

    No results matching ""