• 线程的两种实现方法


    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接口的类的实例。 

     

  • 相关阅读:
    css选择符有哪些?哪些属性可以继承?优先级算法如何计算?内联和important哪个优先
    移动端重要的几个CSS3属性设置
    关于移动端的Click事件
    JS根据key值获取URL中的参数值,以及把URL的参数转换成json对象
    动态添加JS文件到页面
    JS 模拟C# 字符串格式化操作
    JS cookie 读写操作
    很好用的Tab标签切换功能,延迟Tab切换。
    SpringMVC 原理
    SpringMVC 三种异常处理方式
  • 原文地址:https://www.cnblogs.com/nucdy/p/5041208.html
Copyright © 2020-2023  润新知