线程

来源:互联网 发布:算法设计与分析考试题 编辑:程序博客网 时间:2024/06/06 04:59
---------------------- android培训、java培训、期待与您交流! ----------------------

进程是一个正在执行中的程序,每一个进程执行都有一个执行顺序,或则叫一个控制单元,而线程就是进程中的一个独立的控制单元,线程在控制着进程的执行,所以一个进程中至少有一个线程。

我们知道Java程序在编译的时候会有一个进程javac.exe, java VM在启动的时候也会有一个进程java.exe。该进程中至少有一个线程负责java程序代码的执行,而且这个线程运行的代码存在于main方法中,该线程称之为主线程。对于一个简单的Hello World程序,我们往往理解为它在执行的时候就是单线程的,其实往更深细节说明javaVM,它启动时不止一个线程,还有一个负责垃圾回收的线程。

使用多线程可以达到多个程序“同时”执行的效果,那么如何在自定义的代码中,自定义一个线程?通过API的查找可知,java中已经提供了对线程的这类事物的描述,也就是Thread类。API文档是这样叙述的:

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的run 方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成:

class PrimeThread extends Thread 
{   
        long minPrime;
        PrimeThread(long minPrime) 
        {
             this.minPrime = minPrime;
        }
 
        public void run() 
        {
               // compute primes larger than minPrime
                . . .
         }
}

然后,下列代码会创建并启动一个线程:

     PrimeThread p = new PrimeThread(143);
     p.start();
从上面的叙述中可以看出创建线程的步骤是:
1,  定义一个类继承Thread类
2,  复写thread类中的run方法
为什么要覆盖方法呢?这是因为thread类用于描述线程,该类就定义了一个功能,就是用于存储线程要执行的代码。该存储功能就是run方法。也就是说thread类中run方法用于存储要执行的代码,而创建线程正是我们要执行的东西,故将要执行的代码放在run方法中复写以执行。
3,  调用线程的start方法
下面一个简单的多线程示例程序:
class Demo extends Thread
{
         public void run()
         {
                 for (int i=0; i<60; i++)
                          System.out.println("Demo run"+x);
         }
}
class ThreadDemo
{
         public static void main(String[] args) 
         {        
                 Demo d = new Demo();//每new一个对象就创建了一个进程
                 d.start();//调用执行,与d.run();是不同的,run()仅仅是调用方法
                 for (int i=0; i<60; i++)
                          System.out.println("mian run"+x);  
                 
         }
}
这个程序执行后会有两个程序在交互的执行,运行结果具有随机性,每一次都不同,因为多个线程都在获取cpu的执行权,cpu执行到谁谁就执行,每一时刻只可能有一个程序在执行(多核除外)。
 
下面是一个简单的买票程序
class Ticket extends Thread
{
         private int tick = 100;
         public void run()
         {
                 while (true)
                 {
                          if (tick>0)
                          {
                                   System.out.println(currentThread().getName()+"sale:"+tick--);
                          }
                          
                 }
                          
         }
}
class ThreadDemo
{
         public static void main(String[] args) 
         {        
                 Ticket t1 = new Ticket();
                 Ticket t2 = new Ticket();
                 Ticket t3 = new Ticket();
                 Ticket t3 = new Ticket();
 
                 t1.start();
                 t2.start();
                 t3.start();
                 t4.start();               
                 
         }
}
有程序可以看出是四个线程在卖100张票,但结果出现了与现实不相符的结果,即每一个线程都卖了100张票,那么我们可以通过static修饰票数的方法来使票数共享,或者只创建一个线程,start 4次的做法,但是对于后一种做法在运行时却出现了线程状态的异常,因为连续start了4次,这是不符合代码执行要求的,也即一个线程一旦执行了就不需要再次调用start来启动它了。这就要用到第二种创建线程的方法,实现Runnable接口,示例代码如下:
class Ticket implements Runnable
{
         private int tick = 100;
         public void run()
         {
                 while (true)
                 {
                          if (tick>0)
                          {
                                   System.out.println(currentThread().getName()+"sale:"+tick--);
                          }
                          
                 }
                          
         }
}
class ThreadDemo
{
         public static void main(String[] args) 
         {        
                 Ticket t = new Ticket();
                 Thread t1 = new Thread (t);//创建一个线程,并将ticket对象传递给thread的构造函数
                 Thread t2 = new Thread (t);
                 Thread t3 = new Thread (t);
                 Thread t4 = new Thread (t);
 
                 t1.start();
                 t2.start();
                 t3.start();
                 t4.start();               
                 
         }
}
第二种方式很好的解决了方式一中的不足,从以上示例程序看出,第二种方式的步骤是:
1,  定义类实现Runnable接口
2,  覆盖Runnable接口中的run方法
3,  通过Thread类建立线程的对象,
4,  将Runnable接口的子类对象作为实际参数传递给thread类的构造函数
这步的原因是:自定义的run方法,所属的对象是Runnable接口的子类对象,所以要让线程区定义指定对象的run方法,就必须明确该方法所属的对象
5,  调用thread类的start方法开启线程并调用Runnable接口子类的run方法
 
 
实现方式与继承方式有什么区别?
继承thread:线程代码放在thread子类run方法中
实现Runnable:线程代码存放在接口的子类run方法中
实现的好处:避免了单继承的局限性,在定义线程时,建议使用实现方式。
 

---------------------- android培训、java培训、期待与您交流! ----------------------
原创粉丝点击