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

 public class SimpleThread extends Thread
public class SimpleThread extends Thread {
{
 private int count=3;
    private int count=3;
 private static int threadCount=0;
    private static int threadCount=0;
 
    

 public SimpleThread()
    public SimpleThread()  {
{
 super("" + ++threadCount);                    //store the name of the thread
        super("" + ++threadCount);                    //store the name of the thread
 start();
        start();                                       
 }
    }
 
    

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

 public void run()
    public void run()  {
{

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

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

 }
}
        我的运行结果是这样的:
        
 Thread1: 3
Thread1: 3
 Thread2: 3
Thread2: 3
 Thread2: 2
Thread2: 2
 Thread2: 1
Thread2: 1
 Thread1: 2
Thread1: 2
 Thread3: 3
Thread3: 3
 Thread1: 1
Thread1: 1
 Thread3: 2
Thread3: 2
 Thread3: 1
Thread3: 1
        从上面的结果我们就可以看出线程调度的情况,当然不同的机器上结果很可能是不一样的。如果你的机器上的结果显示是线程1到线程3是按顺序完成的,很可能是由于程序中count的值太小了,以至于线程可以是被调度之前就完成了自己的运行,你可以将count值改的大一点,比如100,你就会看出明显的结果。线程是交叉调度运行的(早期的jdk经常不是切片时间的,所以很可能是线程陆续执行)。
        在main()里创建了并运行了一些线程。Thread类的start()方法将为线程执行特殊的初始化操作,然后调用run()方法,开始线程的运行。
        如果run()的代码改为:  
1
 public void run()
public void run()  {
{
2
 while(true)
        while(true)  {
{
3 System.out.println(this);
            System.out.println(this);
4 if (--count == 0) return;
            if (--count == 0) return;
5 yield();
            yield();               
6 }
        }
7 }
    } 
        运行结果将变为:
 Thread1: 3
Thread1: 3
 Thread2: 3
Thread2: 3
 Thread3: 3
Thread3: 3
 Thread1: 2
Thread1: 2
 Thread2: 2
Thread2: 2
 Thread3: 2
Thread3: 2
 Thread1: 1
Thread1: 1
 Thread3: 1
Thread3: 1
 Thread2: 1
Thread2: 1
        yield()让线程让出处理器。一般来说,用yield()进行程序的调整是危险的,因为调度机制是抢占式的,它会决定它认为在最应该的时候切换占用处理器的线程。在以上的程序中如果改变toString()方法,是输出很长,它就会觉得输入输出占用了太多时间了,改换人了,所以很可能还有执行到yield()的时候,线程已经被迫让出处理器了。
        同样,sleep()也不是控制线程执行顺序的好方法,他只是让线程停止执行一段时间。如果你必须控制线程的执行顺序,最好是根本不用线程,而是自己编写特定顺序彼此控制的协作子程序。
优先权
        如果许多线程被阻塞等待运行,那么调度程序倾向于让优先级最高的线程运行。
        下面是一个示例:
 1
 2
 public class ThreadPriority extends Thread
public class ThreadPriority extends Thread {
{
 3 
    
 4 private int count=3;
    private int count=3;
 5 private volatile double d=0;             //不要优化
    private volatile double d=0;             //不要优化
 6 
    
 7
 public ThreadPriority(int p)
    public ThreadPriority(int p)  {
{
 8 this.setPriority(p);
        this.setPriority(p);
 9 this.start();
        this.start();
10 }
    }
11 
    
12
 public String toString()
    public String toString()  {
{
13 return super.toString() + ": "+ count;
        return super.toString() + ": "+ count;
14 }
    }
15 
    
16
 public void run()
    public void run()  {
{
17
 while(true)
        while(true)  {
{
18
 for (int i=1; i<99999; i++)
            for (int i=1; i<99999; i++)  {
{
19 d = d + (Math.PI + Math.E)/(double)i;                 //重体力活
                d = d + (Math.PI + Math.E)/(double)i;                 //重体力活
20 }
            }
21 System.out.println(this);
            System.out.println(this);
22 if (--count ==0)return;
            if (--count ==0)return;
23 }
        }
24 }
    }
25
 /** *//**
    /** *//**
26 * @param args
     * @param args
27 */
     */
28
 public static void main(String[] args)
    public static void main(String[] args)  {
{
29 // TODO Auto-generated method stub
        // TODO Auto-generated method stub
30 new ThreadPriority(Thread.MAX_PRIORITY);
        new ThreadPriority(Thread.MAX_PRIORITY);
31
 for (int i=0; i<3; i++)
        for (int i=0; i<3; i++)  {
{
32 new ThreadPriority(Thread.NORM_PRIORITY);
            new ThreadPriority(Thread.NORM_PRIORITY);
33 }
        }
34 }
    }
35
36 }
} 
        如果现在让你猜结果,也许你会觉得是优先级最高的应该先完成,然后优先级一样的几个线程交织运行直到结束,其实我也是这么认为的,但是事与愿违,结果是这样的(我的机器):
 Thread[Thread-0,10,main]: 3
Thread[Thread-0,10,main]: 3
 Thread[Thread-1,5,main]: 3
Thread[Thread-1,5,main]: 3
 Thread[Thread-0,10,main]: 2
Thread[Thread-0,10,main]: 2
 Thread[Thread-1,5,main]: 2
Thread[Thread-1,5,main]: 2
 Thread[Thread-0,10,main]: 1
Thread[Thread-0,10,main]: 1
 Thread[Thread-1,5,main]: 1
Thread[Thread-1,5,main]: 1
 Thread[Thread-2,5,main]: 3
Thread[Thread-2,5,main]: 3
 Thread[Thread-3,5,main]: 3
Thread[Thread-3,5,main]: 3
 Thread[Thread-2,5,main]: 2
Thread[Thread-2,5,main]: 2
 Thread[Thread-3,5,main]: 2
Thread[Thread-3,5,main]: 2
 Thread[Thread-2,5,main]: 1
Thread[Thread-2,5,main]: 1
 Thread[Thread-3,5,main]: 1
Thread[Thread-3,5,main]: 1
        请不要诧异,这就是多线程的困扰。这是因为,如果让优先级最高的执行完成再让其他线程运行实在是太残忍了,因为它的耗时实在无法让人容忍,所以即便他的优先级是最高也要接受组织的安排。JDK有十个优先级,但通常使用的是以下三个:
 MAX_PRIORITY,
MAX_PRIORITY,
 NORM_PRIOITY,
NORM_PRIOITY,
 MIN_PRIORITY
MIN_PRIORITY
        分别是最大,普通,最小优先级。
后台线程
        “后台”(Daemon)线程,是指程序运行的时候,在后台提供一种通用服务的线程,并且这种服务并不属于程序中不可或缺的部分。因此,当所有的非后台进程结束,程序就终止了。反过来说,只要有任何非后台线程还在运行,程序就不会终止,比如,执行main()的就是一个非后台进程。下面是一个示例:
 1
 public class SimpleDaemon extends Thread
public class SimpleDaemon extends Thread {
{
 2
 public SimpleDaemon()
    public SimpleDaemon()  {
{
 3 this.setDaemon(true);                       //在线程运行之前调用
        this.setDaemon(true);                       //在线程运行之前调用
 4 start();
        start();
 5 }
    }
 6 
    
 7
 public void run()
    public void run()  {
{
 8
 while(true)
        while(true)  {
{
 9
 try
            try  {
{
10 this.sleep(100);
                this.sleep(100);
11
 } catch (InterruptedException e)
            } catch (InterruptedException e)  {
{
12 // TODO Auto-generated catch block
                // TODO Auto-generated catch block
13 e.printStackTrace();
                e.printStackTrace();
14 }
            }
15 System.out.println(this);
            System.out.println(this);
16 }
        }
17 }
    }
18
 /** *//**
    /** *//**
19 * @param args
     * @param args
20 */
     */
21
 public static void main(String[] args)
    public static void main(String[] args)  {
{
22 // TODO Auto-generated method stub
        // TODO Auto-generated method stub
23 for (int i=0; i<10; i++)
        for (int i=0; i<10; i++)
24 new SimpleDaemon();
            new SimpleDaemon();
25 }
    }
26
27 }
}
28
 
        该程序没有运行结果,因为在main()中创建完所有线程以后,程序已经没有执行下去的理由,因为所有的非后台进程都已经结束,所以剩下的十个后台进程将没有机会运行他们的打印语句。这里需要注意的是,把线程设置为后台进程的操作一定要在线程执行前进行。由一个后台线程创建的所有线程将都默认是后台线程。
        机房快关门了,今天就到此为止了,以上是我的学习笔记,希望能给大家提供一定的方便。
        2008年3月24日21:46:56
	posted on 2008-03-24 21:48 
piggytommy 阅读(89) 
评论(0)  编辑  收藏  所属分类: 
Java Basic