posts - 6,  comments - 0,  trackbacks - 0
        前不久参加了一个IBM的面试,两个小时进两百的选择题,让我感到前所未有的挫折感,虽说拼命做了出来,但是还是感到自己的基础知识漏洞无数。其中java部分的多线程由于平时涉及较少,异常吃力,令我诧异的居然考了很多,所以我觉得有必要研究一下。希望与大家交流。
        所谓“进程”(process), 是一个独立运行的程序,它有自己的地址空间。而线程是进程内部的单一控制流。因此一个进程内可以具有多个并发执行的线程。
        使用并发最强制性的原因之一就是要产生能够做出响应的用户界面。一个程序需要在处理自己内部计算的同时响应用户的操作。
        写一个简单的线程最简单的做法是从java.lang.Thread继承。下面是一个小例子.
       
public class SimpleThread extends Thread{
    
private int count=3;
    
private static int threadCount=0;
    
    
public SimpleThread() {
        
super("" + ++threadCount);                    //store the name of the thread
        start();                                       
    }

    
    
public String toString() {
        
return "Thread" + this.getName() + "" + count;
        
    }

    
    
public void run() {
        
while(true{
            System.out.println(
this);
            
if (--count == 0return;
        }

    }

    
public static void main(String[] args) {
        
// TODO Auto-generated method stub
        for (int i=0; i<3; i++
            
new SimpleThread();
    }


}
        我的运行结果是这样的:
        
Thread1: 3
Thread2: 
3
Thread2: 
2
Thread2: 
1
Thread1: 
2
Thread3: 
3
Thread1: 
1
Thread3: 
2
Thread3: 
1
        从上面的结果我们就可以看出线程调度的情况,当然不同的机器上结果很可能是不一样的。如果你的机器上的结果显示是线程1到线程3是按顺序完成的,很可能是由于程序中count的值太小了,以至于线程可以是被调度之前就完成了自己的运行,你可以将count值改的大一点,比如100,你就会看出明显的结果。线程是交叉调度运行的(早期的jdk经常不是切片时间的,所以很可能是线程陆续执行)。
        在main()里创建了并运行了一些线程。Thread类的start()方法将为线程执行特殊的初始化操作,然后调用run()方法,开始线程的运行。
        如果run()的代码改为:  
1public void run() {
2        while(true{
3            System.out.println(this);
4            if (--count == 0return;
5            yield();               
6        }

7    }
        运行结果将变为:
Thread1: 3
Thread2: 
3
Thread3: 
3
Thread1: 
2
Thread2: 
2
Thread3: 
2
Thread1: 
1
Thread3: 
1
Thread2: 
1
        yield()让线程让出处理器。一般来说,用yield()进行程序的调整是危险的,因为调度机制是抢占式的,它会决定它认为在最应该的时候切换占用处理器的线程。在以上的程序中如果改变toString()方法,是输出很长,它就会觉得输入输出占用了太多时间了,改换人了,所以很可能还有执行到yield()的时候,线程已经被迫让出处理器了。
        同样,sleep()也不是控制线程执行顺序的好方法,他只是让线程停止执行一段时间。如果你必须控制线程的执行顺序,最好是根本不用线程,而是自己编写特定顺序彼此控制的协作子程序。

优先权
        如果许多线程被阻塞等待运行,那么调度程序倾向于让优先级最高的线程运行。
        下面是一个示例:
 1
 2public class ThreadPriority extends Thread{
 3    
 4    private int count=3;
 5    private volatile double d=0;             //不要优化
 6    
 7    public ThreadPriority(int p) {
 8        this.setPriority(p);
 9        this.start();
10    }

11    
12    public String toString() {
13        return super.toString() + ""+ count;
14    }

15    
16    public void run() {
17        while(true{
18            for (int i=1; i<99999; i++{
19                d = d + (Math.PI + Math.E)/(double)i;                 //重体力活
20            }

21            System.out.println(this);
22            if (--count ==0)return;
23        }

24    }

25    /**
26     * @param args
27     */

28    public static void main(String[] args) {
29        // TODO Auto-generated method stub
30        new ThreadPriority(Thread.MAX_PRIORITY);
31        for (int i=0; i<3; i++{
32            new ThreadPriority(Thread.NORM_PRIORITY);
33        }

34    }

35
36}
        如果现在让你猜结果,也许你会觉得是优先级最高的应该先完成,然后优先级一样的几个线程交织运行直到结束,其实我也是这么认为的,但是事与愿违,结果是这样的(我的机器):
Thread[Thread-0,10,main]: 3
Thread[Thread
-1,5,main]: 3
Thread[Thread
-0,10,main]: 2
Thread[Thread
-1,5,main]: 2
Thread[Thread
-0,10,main]: 1
Thread[Thread
-1,5,main]: 1
Thread[Thread
-2,5,main]: 3
Thread[Thread
-3,5,main]: 3
Thread[Thread
-2,5,main]: 2
Thread[Thread
-3,5,main]: 2
Thread[Thread
-2,5,main]: 1
Thread[Thread
-3,5,main]: 1
        请不要诧异,这就是多线程的困扰。这是因为,如果让优先级最高的执行完成再让其他线程运行实在是太残忍了,因为它的耗时实在无法让人容忍,所以即便他的优先级是最高也要接受组织的安排。JDK有十个优先级,但通常使用的是以下三个:
MAX_PRIORITY,
NORM_PRIOITY,
MIN_PRIORITY
        分别是最大,普通,最小优先级。

后台线程
        
“后台”(Daemon)线程,是指程序运行的时候,在后台提供一种通用服务的线程,并且这种服务并不属于程序中不可或缺的部分。因此,当所有的非后台进程结束,程序就终止了。反过来说,只要有任何非后台线程还在运行,程序就不会终止,比如,执行main()的就是一个非后台进程。下面是一个示例:
 1public class SimpleDaemon extends Thread{
 2    public SimpleDaemon() {
 3        this.setDaemon(true);                       //在线程运行之前调用
 4        start();
 5    }

 6    
 7    public void run() {
 8        while(true{
 9            try {
10                this.sleep(100);
11            }
 catch (InterruptedException e) {
12                // TODO Auto-generated catch block
13                e.printStackTrace();
14            }

15            System.out.println(this);
16        }

17    }

18    /**
19     * @param args
20     */

21    public static void main(String[] args) {
22        // TODO Auto-generated method stub
23        for (int i=0; i<10; i++)
24            new SimpleDaemon();
25    }

26
27}

28
        该程序没有运行结果,因为在main()中创建完所有线程以后,程序已经没有执行下去的理由,因为所有的非后台进程都已经结束,所以剩下的十个后台进程将没有机会运行他们的打印语句。这里需要注意的是,把线程设置为后台进程的操作一定要在线程执行前进行。由一个后台线程创建的所有线程将都默认是后台线程。
        机房快关门了,今天就到此为止了,以上是我的学习笔记,希望能给大家提供一定的方便。
        2008年3月24日21:46:56


posted on 2008-03-24 21:48 piggytommy 阅读(84) 评论(0)  编辑  收藏 所属分类: Java Basic

只有注册用户登录后才能发表评论。


网站导航: