• 【多线程学习(2)】继承Thread类和实现Runnable接口、Callable接口的区别


    1)Runnable和Callable同是接口

    * Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void);call方法可以抛出异常,run方法不可以
    * 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
    * 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

    2)Thread类和Runnable接口

    基于java的单继承限制,这两者第一个区别:

    * 避免java单继承的局限性

    * runnable接口可以自动实现资源的共享,对于Thread类,如果想要实现共享,需要将共享资源变成静态资源。这一点以卖票案例为例,如下:

    继承Thread类

     1 class ExtendsThreadextends Thread{
     2         private  int ticket = 5;
     3         private String name;
     4         public FirstThread(String name){
     5             this.name = name;
     6         }
     7         public void run(){
     8             for(int i=0;i<10;i++){
     9                 if(ticket > 0){
    10                     System.out.println("继承Thread-->"+name +" 卖票: "  + (ticket--));
    11                 }
    12             }
    13         }
    14     }
    15 public class InitThread {
    16     public static void main(String[] args) {
    17                 new ExtendsThreadextends ("一号窗口").start();
    18                 new ExtendsThreadextends ("二号窗口").start();
    19                 new ExtendsThreadextends ("三号窗口").start(); 
    20         }
    21 }       
    View Code

    output:

        

    实现Runnable接口

     1 class SecondThread implements Runnable{
     2         private  int ticket = 5;
     3         @Override
     4         public void run() {
     5             for(int i=0;i<10;i++){
     6                 if(ticket > 0){
     7                     System.out.println("实现Runnable-->"+ Thread.currentThread().getName()+" 卖票: " + (ticket--));
     8                 }
     9             }
    10         }
    11         
    12     }
    13 
    14 public static void main(String[] args) {
    15                 SecondThread secondThread = new SecondThread();
    16         new Thread(secondThread,"一号窗口").start();
    17         new Thread(secondThread,"二号窗口").start();
    18         new Thread(secondThread,"三号窗口").start();
    19 
    20 }    
    View Code

    output:

       

     1.以上第一种new 了3个Thread 对象,可以看到相当于三个独立的线程在执行卖票任务;第二种也是new了3个对象,但是只有一个Runnable对象,3个Thread共享这个Runnable对象的代码,因此出现了三个线程共同执行卖票任务的结果。如果new出3个Runnable对象执行的话,也会出现3个线程独自各卖5张票。

     2.第二种就达到了资源共享的目的,如果想要第一种也实现第二种的效果的话,把ticket变量改成static即可,所以说资源共享是相对的。

     3.资源共享就设计到线程安全问题,ticket--操作并不具有原子性。ticket有可能输出负数,在System.out...前面加上线程休眠操作,会出现以下结果:

       

    当ticket=1的时候,窗口一执行到ticket>0,窗口二执行到ticket--,此时ticket已经是0了,但是窗口三、一线程还是会继续执行ticket--操作,导致输出ticket为0,甚至为负数。
    要解决这个问题,需要引入线程的同步操作即互斥锁。

  • 相关阅读:
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
    Live2D 看板娘
  • 原文地址:https://www.cnblogs.com/superFish2016/p/5954172.html
Copyright © 2020-2023  润新知