线程的两种实现方法

1.

java中实现多线程操作有两种方法:继承Thread类和实现Runnable接口

一、继承Thread类

   class MyThread extends Thread {//继承Thread类

   private String name ;

   public MyThread(String name) {

     this.name = name;

   }

  public void run() {//覆写Thread类中的run方法

    System.out.println("MyThread-->"+ name);

  }

}

public class TestThread {

 public static void main(String args[]) {

  MyThread t1 = new MyThread("线程1");

  MyThread t2 = new MyThread("线程2");

  t1.start();//调用线程启动方法

  t2.start();//调用线程启动方法

 }

}

二、实现Runnable接口

  class MyThread implements Runnable {

     private String name ;

   public MyThread(String name) {

     this.name = name;

   }

  public void run() {//覆写Thread类中的run方法,这是线程的主体

    System.out.println("MyThread-->"+ name);

  }

}

public class TestThread {

  MyThread t = new MyThread("线程");

  new Thread(t).start();

  new Thread(t).start();

}

三、两种方法的比较

不论是那种方式,最后都需要通过Thread类的实例调用start()方法来开始线程的执行,start()方法通过java虚拟机调用线程中定义的run方法来执行该线程。通过查看java源程序中的start()方法的定义可以看到,它是通过调用操作系统的start0方法来实现多线程的操作的。

但是一般在系统的开发中遇到多线程的情况的时候,以实现Runnable接口的方式为主要方式。这是因为实现接口的方式有很多的优点:

  1、就是通过继承Thread类的方式时,线程类就无法继承其他的类来实现其他一些功能,实现接口的方式就没有这中限制;

  2.也是最重要的一点就是,通过实现Runnable接口的方式可以达到资源共享的效果。

但是其实两种方式是有密切联系的:

  我们通过实现接口Runnable建立的MyThread类,而Thread类也是实现了Runnable接口的子类。如果我们想启动线程,需要通过Thread类中的start()方法,Thread类中的start()方法来调用MyThread类中run方法来执行该线程。这个实现是典型的代理模式。

2.

1、在用户空间中实现线程

(1)特点:内核对线程包一无所知。从内核角度考虑,就是按正常的方式管理,即单线程进程(存在运行时系统)

(2)优点

用户级线程包可以在不支持线程的操作系统上实现

保存线程状态的过程和调用程序都只是本地过程,故启动它们比进程内核调用效率更高

不需要陷阱,不需要上下文切换,也不需要对内存高速缓存进行刷新,使得线程调用非常快捷

(3)缺点

线程发生I/O或页面故障引起的阻塞时,如果调用阻塞系统调用则内核由于不知道有多线程的存在,而会阻塞整个进程从而阻塞所有线程

一个单独的进程内部,没有时钟中断,所以不可能用轮转调度的方式调度线程

2、在内核中实现线程

(1)特点

当某个线程希望创建一个新线程或撤销一个已有线程时,它进行一个系统调用

(2)优点

所有能够阻塞线程的调用都以系统调用的形式实现,代价可观

当一个线程阻塞时,内核根据选择可以运行另一个进程的线程,而用户空间实现的线程中,运行时系统始终运行自己进程中的线程

说明:由于内核创建线程代价大,故有线程回收

3、信号是发给进程而不是线程的,当一个信号到达时,应该由哪一个线程处理它?线程可以“注册”它们感兴趣的信号。但如果两个或更多的线程注册了相同的信号,会发生什么呢?

 

下面是线程包实现图

 

4、混合实现

在这种模型中,每个内核级线程有一个可以轮流使用的用户级线程集合

 

补充:在用户级线程中,每个进程里的线程表由运行时系统管理。当一个线程转换到就绪状态或阻塞状态时,在该线程表中存放重新启动该线程所需的信息,与内核在进程表中存放的进程的信息完全一样

3.   解释3

       线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。一个线程可以创建撤消另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。

在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位。由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统内多个程序间并发执行的程度,从而显著提高系统资源的利用率和吞吐量。

 

线程是程序中一个单一的顺序控制流程。在单个程序中同时运行多个线程完成不同的工作,称为多线程

多线程主要是为了节约CPU时间,发挥利用,线程的运行中需要使用计算机的内存资源和CPU。

多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

 

Java对多线程的支持是非常强大的,他屏蔽掉了许多的技术细节,让我们可以轻松的开发多线程的应用程序。

 

  Java里面实现多线程,有2个方法

1 继承 Thread类

  class MyThread extends Thread {

 

  public void run() {

 

  // 这里写上线程的内容

 

  }

 

  public static void main(String[] args) {

 

  // 使用这个方法启动一个线程

 

  new MyThread().start();

 

  }

 

  }

2 实现 Runnable接口

  class MyThread implements Runnable{

 

  public void run() {

 

  // 这里写上线程的内容

 

  }

 

  public static void main(String[] args) {

 

  // 使用这个方法启动一个线程

 

  new Thread(new MyThread()).start();

 

  }

 

  }

 

  一般鼓励使用第二种方法,因为Java里面只允许单一继承,但允许实现多个接口。第二个方法更加灵活。

例如:第二种方法

public class TestThread1 {
 
 public static void main(String[] args) { 

   Runner1 r = new Runner1();
   r.run();
   Thread t = new Thread(r);
   t.start();
  
  for(int i=0 ; i <100; i++)
      System.out.println("Main Thread: --------"+ i);
  }
 }
 
 class Runner1 implements Runnable {   //实现Runnable接口,能使用接口就用接口,接口比较灵活
   public void run() {
      for(int i=0 ; i <100; i++)
                     System.out.println("Runner1:"+ i);
    }
  }

第一种方法

public class TestThread1 {
 
 public static void main(String[] args) {
         Runner1 r = new Runner1();
         r.start();
  
  for(int i=0 ; i <100; i++)
      System.out.println("Main Thread: --------"+ i);
  }
 }
 
 class Runner1 extends Thread { //继承Thread类
  
   public void run() {
      for(int i=0 ; i <100; i++)
                     System.out.println("Runner1:"+ i);
    }
  }

4.解释4 

Java中有两种实现多线程的方式。一是直接继承Thread类,二是实现Runnable接口。那么这两种实现多线程的方式在应用上有什么区别呢? 

        为了回答这个问题,我们可以通过编写一段代码来进行分析。我们用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。 

        我们首先这样编写这个程序: 

Java代码  收藏代码
    1. class ThreadTest extends Thread{  
    2.   private int ticket = 100;  
    3.   public void run(){  
    4.     while(true){  
    5.       if(ticket > 0){  
    6.         System.out.println(Thread.currentThread().getName() +  
    7.           "is saling ticket" + ticket--);  
    8.       }else{  
    9.         break;  
    10.       }  
    11.     }  
    12.   }  
    13. }  


main测试类: 

Java代码  收藏代码
    1. public class ThreadDome1{  
    2.   public static void main(String[] args){  
    3.     ThreadTest t = new ThreadTest();  
    4.     t.start();  
    5.     t.start();  
    6.     t.start();  
    7.     t.start();  
    8.   }  
    9. }  


上面的代码中,我们用ThreadTest类模拟售票处的售票过程,run方法中的每一次循环都将总票数减1,模拟卖出一张车票,同时该车票号打印出来,直接剩余的票数到零为止。在ThreadDemo1类的main方法中,我们创建了一个线程对象,并重复启动四次,希望通过这种方式产生四个线程。从运行的结果来看我们发现其实只有一个线程在运行,这个结果告诉我们:一个线程对象只能启动一个线程,无论你调用多少遍start()方法,结果只有一个线程。 

      我们接着修改ThreadDemo1,在main方法中创建四个Thread对象: 

Java代码  收藏代码
    1. public class ThreadDemo1{  
    2.   public static void main(String[] args){  
    3.     new ThreadTest().start();  
    4.     new ThreadTest().start();  
    5.     new ThreadTest().start();  
    6.     new ThreadTest().start();  
    7.   }  
    8. }  


Java代码  收藏代码
    1. class ThreadTest extends Thread{  
    2.   private int ticket = 100;  
    3.   public void run(){  
    4.     while(true){  
    5.       if(ticket > 0){  
    6.         System.out.println(Thread.currentThread().getName() +   
    7.            " is saling ticket" + ticket--);  
    8.       }else{  
    9.         break;  
    10.       }  
    11.     }  
    12.   }  
    13. }  



     这下达到目的了吗? 

     从结果上看每个票号都被打印了四次,即四个线程各自卖各自的100张票,而不去卖共同的100张票。这种情况是怎么造成的呢?我们需要的是,多个线程去处理同一个资源,一个资源只能对应一个对象,在上面的程序中,我们创建了四个ThreadTest对象,就等于创建了四个资源,每个资源都有100张票,每个线程都在独自处理各自的资源。 

     经过这些实验和分析,可以总结出,要实现这个铁路售票程序,我们只能创建一个资源对象,但要创建多个线程去处理同一个资源对象,并且每个线程上所运行的是相同的程序代码。在回顾一下使用接口编写多线程的过程。

Java代码  收藏代码
    1. public class ThreadDemo1{  
    2.   public static void main(String[] args){  
    3.     ThreadTest t = new ThreadTest();  
    4.     new Thread(t).start();  
    5.     new Thread(t).start();  
    6.     new Thread(t).start();  
    7.     new Thread(t).start();  
    8.   }  
    9. }  


Java代码  收藏代码
    1. class ThreadTest implements Runnable{  
    2.   private int tickets = 100;  
    3.   public void run(){  
    4.     while(true){  
    5.       if(tickets > 0){  
    6.         System.out.println(Thread.currentThread().getName() +  
    7.           " is saling ticket " + tickets--);  
    8.       }  
    9.     }  
    10.   }  
    11. }  



上面的程序中,创建了四个线程,每个线程调用的是同一个ThreadTest对象中的run()方法,访问的是同一个对象中的变量(tickets)的实例,这个程序满足了我们的需求。在Windows上可以启动多个记事本程序一样,也就是多个进程使用同一个记事本程序代码。 

       可见,实现Runnable接口相对于继承Thread类来说,有如下显著的好处: 

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。 

(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。 

(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。 

 

posted @ 2015-12-12 14:37  dy9776  阅读(435)  评论(0编辑  收藏  举报