技术开发 频道

J2SE综合--有关JAVA 的多线程浅析

  线程运行的顺序以及从处理器中获得的时间数量主要取决于开发者,处理器给每个线程分配一个时间片,而且线程的运行不能影响整个系统。处理器线程的系统或者是抢占式的,或者是非抢占式的。抢占式系统在任何给定的时间内将运行最高优先级的线程,系统中的所有线程都有自己的优先级。Thread.NORM_PRIORITY是线程的缺省值,Thread类提供了setPriority和getPriority方法来设置和读取优先权,使用setPriority方法能改变Java虚拟机中的线程的重要性,它调用一个整数,类变量Thread.MIN_PRIORITY和Thread.MAX_PRIORITY决定这个整数的有效范围。Java虚拟机是抢占式的,它能保证运行优先级最高的线程。在JAVA虚拟机中我们把一个线程的优先级改为最高,那么他将取代当前正在运行的线程,除非这个线程结束运行或者被一条休眠命令放入waiting状态,否者将一直占用所有的处理器的时间。如果遇到两个优先级相同的线程,操作系统可能影响线程的执行顺序。而且这个区别取决于时间片(time slicing)的概念。管理几个线程并不是真正的难题,对于上百个线程它是怎样管理的呢?当然可以通过循环,来执行每一个线程,但是这显然是冗长、乏味。JAVA创建了线程组。线程组是线程的一个谱系组,每个组包含的线程数不受限制,能对每个线程命名并能在整个线程组中执行(Suspend)和停止(Stop)这样的操作。

  信号标志:保护其它共享资源

  这种类型的保护被称为互斥锁。某个时间只能有一个线程读取或修改这个数据值。在对文件尤其是信息数据库进行处理时,读取的数据总是多于写数据,根据这个情况,可以简化程序。下面举一例,假设有一个雇员信息的数据库,其中包括雇员的地址和电话号码等信息,有时要进行修改,但要更多的还是读数据,因此要尽可能防止数据被破坏或任意删改。我们引入前面互斥锁的概念,允许一个读取锁(red lock)和写入锁(write lock),可根据需要确定有权读取数据的人员,而且当某人要写数据时,必须有互斥锁,这就是信号标志的概念。信号标志有两种状态,首先是empty()状态,表示没有任何线程正在读或写,可以接受读和写的请求,并且立即提供服务;第二种状态是reading()状态,表示有线程正在从数据库中读信息,并记录进行读操作的线程数,当它为0时,返回empty状态,一个写请求将导致这个线程进入等待状态。只能从empty状态进入writing状态,一旦进入writing状态后,其它线程都不能写操作,任何写或读请求都必须等到这个线程完成写操作为止,而且waiting状态中的进程也必须一直等到写操作结束。完成操作后,返回到empty状态,发送一个通知信号,等待的线程将得到服务。

  下面实现了这个信号标志

1 class Semaphore{
2
3   final static int EMPTY=0;
4
5   final static int READING=1;
6
7   final static int WRITING=2;
8
9   protected int state=EMPTY;
10
11   protected int readCnt=0;
12
13   public synchronized void readLock(){
14
15   if(state==EMPTY){
16
17   state=READING;
18
19   }
20
21   else if(state==READING){
22
23   }
24
25   else if(state==WRITING){
26
27   while(state==WRITING){
28
29   try {wait();}
30
31   catch(InterruptedException e){;}
32
33   }
34
35   state=READING;
36
37   }
38
39   readCnt++;
40
41   return;
42
43   }
44
45   public synchronized void writeLock(){
46
47   if(state==EMPTY){
48
49   state=WRITING;
50
51   }
52
53   else{
54
55   while(state!=EMPTY){
56
57   try {wait();}
58
59   catch(InterruptedException e) {;}
60
61   }
62
63   }
64
65   }
66
67   public synchronized void readUnlock(){
68
69   readCnt--;
70
71   if(readCnt==0){
72
73   state=EMPTY;
74
75   notify();
76
77   }
78
79   }
80
81   public synchronized void writeUnlock(){
82
83   state=EMPTY;
84
85   notify();
86
87   }
88
89   }
90
91

现在是测试信号标志的程序:

1 class Process extends Thread{
2
3   String op;
4
5   Semaphore sem;
6
7   Process(String name,String op,Semaphore sem){
8
9   super(name);
10
11   this.op=op;
12
13   this.sem=sem;
14
15   start();
16
17   }
18
19   public void run(){
20
21   if(op
22
23   catch(InterruptedException e){;}
24
25   System.out.println("Unlocking readLock:"+getName());
26
27   sem.readUnlock();
28
29   }
30
31   else if(op
32
33   catch(InterruptedException e){;}
34
35   System.out.println("Unlocking writeLock:"+getName());
36
37   sem.writeUnlock();
38
39   }
40
41   }
42
43   }
44
45   public class testSem{
46
47   public static void main(String argv[]){
48
49   Semaphore lock = new Semaphore();
50
51   new Process("1","read",lock);
52
53   new Process("2","read",lock);
54
55   new Process("3","write",lock);
56
57   new Process("4","read",lock);
58
59   }
60
61   }
62

 testSem 类从process类的四个实例开始,它是个线程,用来读或写一个共享文件。Semaphore类保证访问不会破坏文件,执行程序,输出结果如下:
 

1  Trying to get readLock:1
2
3   Read op:1
4
5   Trying to get readLock:2
6
7   Read op:2
8
9   Trying to get writeLock:3
10
11   Trying to get readLock:4
12
13   Read op:4
14
15   Unlocking readLock:1
16
17   Unlocking readLock:2
18
19   Unlocking readLock:4
20
21   Write op:3
22
23   Unlocking writeLock:3


  死锁以及怎样避免死锁:

  为了防止数据项目的并发访问,应将数据项目标为专用,只有通过类本身的实例方法的同步区访问。为了进入关键区,线程必须取得对象的锁。假设线程要独占访问两个不同对象的数据,则必须从每个对象各取一个不同的锁。现在假设另一个线程也要独占访问这两个对象,则该进程必须得到这两把锁之后才能进入。由于需要两把锁,编程如果不小心就可能出现死锁。假设第一个线程取得对象A的锁,准备取对象B的锁,而第二个线程取得了对象B的锁,准备取对象A的锁,两个线程都不能进入,因为两者都不能离开进入的同步块,既两者都不能放弃目前持有的锁。避免死锁要认真设计。线程因为某个先决条件而受阻时,如需要锁标记时,不能让线程的停止本身禁止条件的变化。如果要取得多个资源,如两个不同对象的锁,必须定义取得资源的顺序。如果对象A和B的锁总是按字母顺序取得,则不会出现前面说道的饿死条件。

  Java多线程的优缺点

  由于JAVA的多线程功能齐全,各种情况面面具到,它带来的好处也是显然易见的。多线程带来的更大的好处是更好的交互性能和实时控制性能。当然实时控制性能还取决于系统本(UNIX,Windows,Macintosh 等),在开发难易程度和性能上都比单线程要好。当然一个好的程序设计语言肯定也难免有不足之处。由于多线程还没有充分利用基本OS的这一功能。这点我在前面已经提到,对于不同的系统,上面的程序可能会出现截然不同的结果,这使编程者偶会感到迷惑不解。希望在不久的将来JAVA的多线程能充分利用到操作系统,减少对编程者的困惑。我期待着JAVA会更好.

0
相关文章