精华内容
下载资源
问答
  • Runnable和Thread比较

    2021-06-02 16:10:35
    1.Runnable和Thread比较 如上所述,Runnable相比Thread存在明显的优点,同时也是两者最大的区别。这点就不再做阐述,这里对于网络很多文章中存在的明显的错误文字总结进行一下论证: Runnable可以实现多个相同的程序...

    在线程使用过程中,我们肯定会用到Runnable与Thread,前者的实现方式是实现其接口即可,后者的实现方式是继承其类。两者实现方式带来最明显的区别就是,由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以。

    1.Runnable和Thread比较
    如上所述,Runnable相比Thread存在明显的优点,同时也是两者最大的区别。这点就不再做阐述,这里对于网络很多文章中存在的明显的错误文字总结进行一下论证:
    Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread不可以?
    我们以实际的代码样例来论证这个观点:

    package test;

    /**

    • Thread 实现资源共享
    • @author itbird

    */
    public class Test2 {

    public static void main(String[] args) {
        MyThread t1 = new MyThread();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }
    
    public static class MyThread extends Thread {
        private int total = 10;
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (this) {
                    if (total > 0) {
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + "卖票---->" + (this.total--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    }
    Thread实现共享同一个资源.png
    package test;

    /**

    • Runnable 实现资源共享
    • @author itbird

    */
    public class Test3 {

    public static void main(String[] args) {
        MyRunable t1 = new MyRunable();
        new Thread(t1, "线程1").start();
        new Thread(t1, "线程2").start();
    }
    
    public static class MyRunable implements Runnable {
        private int total = 10;
    
        @Override
        public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (this) {
                    if (total > 0) {
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName() + "卖票---->" + (this.total--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
    

    }

    Runnable实现共享同一个资源.png
    经过实际样例代码编写以及运行结果对比,我们知道很多人说的Thread类不能共享资源,其实并不是不能,只是不适合
    其实我们从Thread源码中也可以看到,当以Thread方式去实现资源共享时,实际上源码内部是将thread向下转型为了Runnable,实际上内部依然是以Runnable形式去实现的资源共享
    new Thread源码.png
    2.Runnable为什么不可以直接run
    如果问出这个问题,代表您对Java的线程基础知识以及多线程理念还不太熟悉或者说并没有真正理解,这里做简单阐述。

    多线程原理:相当于玩游戏机,只有一个游戏机(cpu),可是有很多人要玩,于是,start是排队!等CPU选中你就是轮到你,你就run(),当CPU的运行的时间片执行完,这个线程就继续排队,等待下一次的run()。

    调用start()后,线程会被放到等待队列,等待CPU调度,并不一定要马上开始执行,只是将这个线程置于可动行状态。然后通过JVM,线程Thread会调用run()方法,执行本线程的线程体。先调用start后调用run,这么麻烦,为了不直接调用run?就是为了实现多线程的优点,没这个start不行。

    1.start()方法来启动线程,真正实现了多线程运行。这时无需等待run方法体代码执行完毕,可以直接继续执行下面的代码;通过调用Thread类的start()方法来启动一个线程, 这时此线程是处于就绪状态, 并没有运行。 然后通过此Thread类调用方法run()来完成其运行操作的, 这里方法run()称为线程体,它包含了要执行的这个线程的内容, Run方法运行结束, 此线程终止。然后CPU再调度其它线程
    2.run()方法当作普通方法的方式调用。程序还是要顺序执行,要等待run方法体执行完毕后,才可继续执行下面的代码; 程序中只有主线程——这一个线程, 其程序执行路径还是只有一条, 这样就没有达到写线程的目的

    解释到这里,相信各位看官心里有种“了然大明白”的感觉,runnable其实相对于一个task,并不具有线程的概念,如果你直接去调用runnable的run,其实就是相当于直接在主线程中执行了一个函数而已,并未开启线程去执行,所以显而易见,如果你在代码中直接通过这种方式run了一个runnable,明显您的程序的主线程就直接悲催了,各种资源不足现象的崩溃日志会接踵而至,而且遇到这种问题的时候,如果没有一定的研发经验和坚持,很有可能采取错误的解决策略。

    例如:

    java.lang.OutOfMemoryError<>

    很多开发遇到这个问题时,问一下度娘,知道原因是资源没有合理利用,使用完了没有释放,从而导致内存溢出,立马就想到了两种解决方案
    1、把用完的程序的资源释放
    2、加大虚拟机的存储容量
    明显这时采取哪种解决策略都是错误的,因为真正的问题在于您的代码中错误使用方法,导致资源的不合理利用
    所以有时解决问题,还是要去找根本原因,不能为了解决问题而解决问题
    总结
    1.Runnable和Thread相比优点有:
    (1)由于Java不允许多继承,因此实现了Runnable接口可以再继承其他类,但是Thread明显不可以
    (2)Runnable可以实现多个相同的程序代码的线程去共享同一个资源,而Thread并不是不可以,而是相比于Runnable来说,不太适合,具体原因文章中有。
    2.Runnable为什么不可以直接run
    阐述文章中已有,Runnable其实相对于一个Task,并不具有线程的概念,如果你直接去调用Runnable的run,其实就是相当于直接在主线程中执行了一个函数而已,并未开启线程去执行,带来的“灾害”文章中有。

    展开全文
  • 在我们开发的过程中常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、继承Thread类。对于这两种多线程的实现方式也是有着一些差异。既然实现了多线程那必然离不开管理这些线程,当问题比...

    在我们开发的过程中常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、继承Thread类。对于这两种多线程的实现方式也是有着一些差异。既然实现了多线程那必然离不开管理这些线程,当问题比简单时一个或者几个线程就OK了,也涉及不到效率问题。一旦线程数量多起来的时候,必然躲不过这些线程的创建与销毁,而往往这是很浪费时间的。这时就需要利用线程池来进行管理,既免去了我们创建线程和销毁线程的代码,也提高了程序的效率。下面针对以上问题做出相关讲解。

    一、Runnable、Thread比较

    首先阐述实现Runnable的好处:

    • java不允许多继承,因此实现了Runnable接口的类可以再继承其他类。
    • 方便资源共享,即可以共享一个对象实例???(从很多博客中看到这样描述,但是此处有疑问,例子如下)

    下面来通过具体代码来解释上述优点,网上很流行的买票系统,假设有10张票,首先通Thread来进行购买。代码如下:

    public class TicketThread extends Thread{
    
        private int ticket = 10;
    
        public void run(){
            for(int i =0;i<10;i++){
                synchronized (this){
                    if(this.ticket>0){
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] arg){
            TicketThread t1 = new TicketThread();
            new Thread(t1,"线程1").start();
            new Thread(t1,"线程2").start();
            //也达到了资源共享的目的,此处网上有各种写法,很多写法都是自圆其说,举一些特殊例子来印证自己的观点,然而事实却不尽如此。
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    输出: 
    线程1卖票—->10 
    线程1卖票—->9 
    线程1卖票—->8 
    线程2卖票—->7 
    线程2卖票—->6 
    线程1卖票—->5 
    线程1卖票—->4 
    线程2卖票—->3 
    线程2卖票—->2 
    线程1卖票—->1

    实现Runnable接口:

    package threadTest;
    
    public class TicketRunnable implements Runnable{
    
        private int ticket = 10;
    
        @Override
        public void run() {
            for(int i =0;i<10;i++){
                //添加同步快
                synchronized (this){
                    if(this.ticket>0){
                        try {
                            //通过睡眠线程来模拟出最后一张票的抢票场景
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] arg){
            TicketRunnable t1 = new TicketRunnable();
            new Thread(t1, "线程1").start();
            new Thread(t1, "线程2").start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出: 
    线程1卖票—->10 
    线程1卖票—->9 
    线程1卖票—->8 
    线程1卖票—->7 
    线程2卖票—->6 
    线程2卖票—->5 
    线程2卖票—->4 
    线程2卖票—->3 
    线程2卖票—->2 
    线程2卖票—->1

    从这两个例子可以看出,Thread也可以资源共享啊,为什么呢,因为Thread本来就是实现了Runnable,包含Runnable的功能是很正常的啊!!至于两者的真正区别最主要的就是一个是继承,一个是实现;其他还有一些面向对象的思想,Runnable就相当于一个作业,而Thread才是真正的处理线程,我们需要的只是定义这个作业,然后将作业交给线程去处理,这样就达到了松耦合,也符合面向对象里面组合的使用,另外也节省了函数开销,继承Thread的同时,不仅拥有了作业的方法run(),还继承了其他所有的方法。综合来看,用Runnable比Thread好的多。

    针对本例再补充一点,在以上程序中,如果去掉同步代码块,则会出现其中一人购买第0张票的情况,所以我们在做多线程并行的时候一定要时刻考虑到边界值的问题,在关键代码处必须要做好同步处理。

    二、线程池

    创建线程池主要有三个静态方法供我们使用,由Executors来进行创建相应的线程池:

    public static ExecutorSevice newSingleThreadExecutor()
    public static ExecutorSevice newFixedThreadPool(int nThreads)
    public static ExecutorSevice newCachedThreadPool()
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    • 1
    • 2
    • 3
    • 4
    • newSingleThreadExecutor返回以个包含单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。
    • newFixedThreadPool返回一个包含指定数目线程的线程池,如果任务数量多于线程数量,那么没有执行的任务必须等待,直到有任务完成为止。
    • newCachedThreadPool根据用户的任务数创建相应的线程来处理,该线程池不会对线程数目加以限制,完全依赖于JVM能创建线程的数量,可能引起内存不足。
    • newScheduledThreadPool创建一个至少有n个线程空间大小的线程池。此线程池支持定时以及周期性执行任务的需求。

    我们只需要把实现了Runnable的类的对象实例放入线程池,那么线程池就自动维护线程的启动、运行、销毁。我们不需要自行调用start()方法来开启这个线程。线程放入线程池之后会处于等待状态直到有足够空间时会唤醒这个线程。

    private ExecutorService threadPool = Executors.newFixedThreadPool(5);
    threadPool.execute(socketThread);
    
    //至少维护5个线程容量的空间
    private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    //函数意义:一个线程开始之后和下一个线程开始的时间间隔
    //第一个时间参数表示初始化执行延迟1000毫秒,第二个时间参数表示每隔1000毫秒执行一次
    //第二个线程必须等到第一个线程执行完成才能继续执行,尽管时间间隔小于线程执行时间
    threadPool.scheduleAtFixedRate(socketThread, 1000, 1000, TimeUnit.MILLISECONDS);
    //基本参数和上面的类似,函数意义不一样:一个线程结束之后和下一个线程开始的时间间隔
    threadPool.scheduleWithFixedDelay(socketThread, 1000, 1000, TimeUnit.MILLISECONDS);
    //线程池不接收新加的线程,但是执行完线程池内部的所有线程
    threadPool.shutdown();
    //立即关闭线程池,停止线程池内还未执行的线程并且返回一个未执行的线程池列表
    threadPool.shutdownNow();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013755987/article/details/51855098
    展开全文
  • 在我们开发的过程中常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、继承Thread类。对于这两种多线程的实现方式也是有着一些差异。既然实现了多线程那必然离不开管理这些线程,当问题...

    在我们开发的过程中常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、继承Thread类。对于这两种多线程的实现方式也是有着一些差异。既然实现了多线程那必然离不开管理这些线程,当问题比简单时一个或者几个线程就OK了,也涉及不到效率问题。一旦线程数量多起来的时候,必然躲不过这些线程的创建与销毁,而往往这是很浪费时间的。这时就需要利用线程池来进行管理,既免去了我们创建线程和销毁线程的代码,也提高了程序的效率。下面针对以上问题做出相关讲解。

    一、Runnable、Thread比较

    首先阐述实现Runnable的好处:

    • java不允许多继承,因此实现了Runnable接口的类可以再继承其他类。
    • 方便资源共享,即可以共享一个对象实例???(从很多博客中看到这样描述,但是此处有疑问,例子如下)

    下面来通过具体代码来解释上述优点,网上很流行的买票系统,假设有10张票,首先通Thread来进行购买。代码如下:

    public class TicketThread extends Thread{
    
        private int ticket = 10;
    
        public void run(){
            for(int i =0;i<10;i++){
                synchronized (this){
                    if(this.ticket>0){
                        try {
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] arg){
            TicketThread t1 = new TicketThread();
            new Thread(t1,"线程1").start();
            new Thread(t1,"线程2").start();
            //也达到了资源共享的目的,此处网上有各种写法,很多写法都是自圆其说,举一些特殊例子来印证自己的观点,然而事实却不尽如此。
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    输出:
    线程1卖票—->10
    线程1卖票—->9
    线程1卖票—->8
    线程2卖票—->7
    线程2卖票—->6
    线程1卖票—->5
    线程1卖票—->4
    线程2卖票—->3
    线程2卖票—->2
    线程1卖票—->1

    实现Runnable接口:

    package threadTest;
    
    public class TicketRunnable implements Runnable{
    
        private int ticket = 10;
    
        @Override
        public void run() {
            for(int i =0;i<10;i++){
                //添加同步快
                synchronized (this){
                    if(this.ticket>0){
                        try {
                            //通过睡眠线程来模拟出最后一张票的抢票场景
                            Thread.sleep(100);
                            System.out.println(Thread.currentThread().getName()+"卖票---->"+(this.ticket--));
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        public static void main(String[] arg){
            TicketRunnable t1 = new TicketRunnable();
            new Thread(t1, "线程1").start();
            new Thread(t1, "线程2").start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    输出:
    线程1卖票—->10
    线程1卖票—->9
    线程1卖票—->8
    线程1卖票—->7
    线程2卖票—->6
    线程2卖票—->5
    线程2卖票—->4
    线程2卖票—->3
    线程2卖票—->2
    线程2卖票—->1

    从这两个例子可以看出,Thread也可以资源共享啊,为什么呢,因为Thread本来就是实现了Runnable,包含Runnable的功能是很正常的啊!!至于两者的真正区别最主要的就是一个是继承,一个是实现;其他还有一些面向对象的思想,Runnable就相当于一个作业,而Thread才是真正的处理线程,我们需要的只是定义这个作业,然后将作业交给线程去处理,这样就达到了松耦合,也符合面向对象里面组合的使用,另外也节省了函数开销,继承Thread的同时,不仅拥有了作业的方法run(),还继承了其他所有的方法。综合来看,用Runnable比Thread好的多。

    针对本例再补充一点,在以上程序中,如果去掉同步代码块,则会出现其中一人购买第0张票的情况,所以我们在做多线程并行的时候一定要时刻考虑到边界值的问题,在关键代码处必须要做好同步处理。

    二、线程池

    创建线程池主要有三个静态方法供我们使用,由Executors来进行创建相应的线程池:

    public static ExecutorSevice newSingleThreadExecutor()
    public static ExecutorSevice newFixedThreadPool(int nThreads)
    public static ExecutorSevice newCachedThreadPool()
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
    • 1
    • 2
    • 3
    • 4
    • newSingleThreadExecutor返回以个包含单线程的Executor,将多个任务交给此Exector时,这个线程处理完一个任务后接着处理下一个任务,若该线程出现异常,将会有一个新的线程来替代。
    • newFixedThreadPool返回一个包含指定数目线程的线程池,如果任务数量多于线程数量,那么没有执行的任务必须等待,直到有任务完成为止。
    • newCachedThreadPool根据用户的任务数创建相应的线程来处理,该线程池不会对线程数目加以限制,完全依赖于JVM能创建线程的数量,可能引起内存不足。
    • newScheduledThreadPool创建一个至少有n个线程空间大小的线程池。此线程池支持定时以及周期性执行任务的需求。

    我们只需要把实现了Runnable的类的对象实例放入线程池,那么线程池就自动维护线程的启动、运行、销毁。我们不需要自行调用start()方法来开启这个线程。线程放入线程池之后会处于等待状态直到有足够空间时会唤醒这个线程。

    private ExecutorService threadPool = Executors.newFixedThreadPool(5);
    threadPool.execute(socketThread);
    
    //至少维护5个线程容量的空间
    private ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(5);
    //函数意义:一个线程开始之后和下一个线程开始的时间间隔
    //第一个时间参数表示初始化执行延迟1000毫秒,第二个时间参数表示每隔1000毫秒执行一次
    //第二个线程必须等到第一个线程执行完成才能继续执行,尽管时间间隔小于线程执行时间
    threadPool.scheduleAtFixedRate(socketThread, 1000, 1000, TimeUnit.MILLISECONDS);
    //基本参数和上面的类似,函数意义不一样:一个线程结束之后和下一个线程开始的时间间隔
    threadPool.scheduleWithFixedDelay(socketThread, 1000, 1000, TimeUnit.MILLISECONDS);
    //线程池不接收新加的线程,但是执行完线程池内部的所有线程
    threadPool.shutdown();
    //立即关闭线程池,停止线程池内还未执行的线程并且返回一个未执行的线程池列表
    threadPool.shutdownNow();
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    展开全文
  • 继承Thread类,2.实现Runnable接口。这两种方式一般都是适用于线程数量较少的情况,由于线程的创建销毁会降低程序性能,所以当线程数量较多情况,建议使用线程池。 介绍 实现多线程基础方式。 方式一:实现...

    实现多线程基础的两种方式有:1.继承Thread类,2.实现Runnable接口。这两种方式一般都是适用于线程数量较少的情况,由于线程的创建和销毁会降低程序性能,所以当线程数量较多情况,建议使用线程池。

    介绍

    实现多线程基础方式。

    方式一:实现Runnable接口。Runnable接口中只有一个方法Run()方法。

    public interface Runnable {
    	 public abstract void run();
    }

    方式二:继承Thread类。Thread类继承了Runnable接口。

    public class Thread implements Runnable {
    }

    异同点

    对于实现多线程的方式,有两点不同。

    1.在java中只能继承一个类,但是可以实现多个接口,所以接口实现的Runnable的方式有很好的扩展性。

    2.还有另外一点有争议的是,Runnable和Thread能否实现资源的共享,也就是是否共享一个对象实例(下面通过代码探讨)。

    其实真正创建线程的方式只有一种,新建一个Thread对象(也就是new Thread())。而对于多线程实现方式一,通过实现Runnable接口的类,就是产生一个任务或者工作类,交给一个或者多个线程去完成这个任务(new Thread(任务).start())。上面说到Thread类是继承了Runnable接口的,而Thread就是线程类。所以通过继承Thread接口,重写run()方法,就是任务和线程绑定到了一起。

    建议采用是实现Runnable接口的方式来实现多线程,这种方式只是产生了一个任务类,然后交给Thread线程去执行任务。将线程和任务进行了分离,达到了松耦合的目的。

    代码实现

    先看实现Runnable接口的方式。

    public class ThreadTask implements Runnable {
    
      private int ticket = 10;
      public void run() {
        for (int i = 0; i < 10; i++) {
          if (this.ticket > 0) {
            try {
              Thread.sleep(100);
              System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }
      }
      public static void main(String[] args) {
        ThreadTask thread = new ThreadTask();
        new Thread(thread, "线程1").start();
        new Thread(thread, "线程2").start();
      }
    }

    执行结果:

    线程2---卖票:ticket:10
    线程1---卖票:ticket:9
    线程1---卖票:ticket:8
    线程2---卖票:ticket:7
    线程2---卖票:ticket:6
    线程1---卖票:ticket:5
    线程1---卖票:ticket:4
    线程2---卖票:ticket:3
    线程1---卖票:ticket:2
    线程2---卖票:ticket:1
    线程1---卖票:ticket:0

    上面说过实现Runnable接口的类,只是产生一个任务的类,在main()方法中我们先创建了一个任务类ThreadTask,

    然后分别启动了两个线程去执行这个任务,所以两个线程合力完成了这个任务,也就是说资源被共享。

    再看继承Thread类的方式

     

    public class MyThread extends Thread {
    
      private int ticket = 10;
      @Override
      public void run() {
        for(int i = 0; i < 10; i++) {
          if(this.ticket>0) {
            try {
              Thread.sleep(100);
              System.out.println(Thread.currentThread().getName()+"---卖票:ticket:"+(ticket--));
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }
      }
      public static void main(String[] args) {
        MyThread thread = new MyThread();
        MyThread thread2 = new MyThread();
        thread.start();
        thread2.start();
      }
    }

    执行结果

    Thread-1---卖票:ticket:10
    Thread-0---卖票:ticket:10
    Thread-1---卖票:ticket:9
    Thread-0---卖票:ticket:9
    Thread-1---卖票:ticket:8
    Thread-0---卖票:ticket:8
    Thread-1---卖票:ticket:7
    Thread-0---卖票:ticket:7
    Thread-1---卖票:ticket:6
    Thread-0---卖票:ticket:6
    Thread-1---卖票:ticket:5
    Thread-0---卖票:ticket:5
    Thread-0---卖票:ticket:4
    Thread-1---卖票:ticket:4
    Thread-1---卖票:ticket:3
    Thread-0---卖票:ticket:3
    Thread-1---卖票:ticket:2
    Thread-0---卖票:ticket:2
    Thread-1---卖票:ticket:1
    Thread-0---卖票:ticket:1

    Thread就是个线程类,而且实现了Runnable接口。因为MyThread类继承Thread类,并重写run()(里面是任务),所以线程上绑定了任务。

    MyThread类继承了Thread线程类,所以MyThread本身就是个线程类,可以通过start()方法直接启动线程。

    下面这种不能说是错误的方式,但是以一种不是很合理的方式演示了继承Thread来实现多线程的方式。因为Thread实现了Runnable接口,而ThreadTask又继承Thread了,所以也可以说ThreadTask类是任务类,通过new Thread(任务).start()来执行任务也说的通,但是有一点,如果说将继承Thread类,比如上面的MyThread类,只是让这个类变成一个任务类,为什么不直接通过实现Runnable()的方式得到一个任务类。

    所以按上面分析,实现Runnable接口的方式可以实现资源共享,因为多个线程合力完成一个任务,这个任务的所有资源可以被所有的线程共享到。而继承Thread类的方式不能实现资源共享,因为继承Thread的类本身就是一个线程类,并且线程上绑定了任务,一个线程类绑定一个任务类,本线程里面的资源只能被本线程使用,别的线程不能共享到该线程的任务上的资源。

    public class ThreadTask extends Thread {
      private int ticket = 10;
    
      public void run() {
        for (int i = 0; i < 10; i++) {
          if (this.ticket > 0) {
            try {
              Thread.sleep(100);
              System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }
      }
      public static void main(String[] args) {
        ThreadTask task = new ThreadTask();
        new Thread(task,"线程1").start();
        new Thread(task,"线程2").start();
      }
    }

    执行结果:

    线程1---卖票:ticket:10
    线程2---卖票:ticket:9
    线程1---卖票:ticket:8
    线程2---卖票:ticket:7
    线程2---卖票:ticket:6
    线程1---卖票:ticket:5
    线程1---卖票:ticket:4
    线程2---卖票:ticket:3
    线程1---卖票:ticket:2
    线程2---卖票:ticket:1

    当然,这个演示的例子有很多要改进。就拿实现Runnable接口的方法来看,存在多个线程并发抢夺资源的情况。

    线程1如果将数据判断if(ticket>0)为真,此时线程2控制了资源。此时如果线程2将ticket数值修改为0,线程1依旧会进行下面的操作,造成多卖票的情况。改进如下:

    
     
    public class ThreadTask implements Runnable {
    
    	private int ticket = 10;
    	public void run() {
    		for (int i = 0; i < 10; i++) {
    			synchronized (this) {
    				if (this.ticket > 0) {
    					try {
    						Thread.sleep(100);
    						System.out.println(Thread.currentThread().getName() + "卖票:ticket:" + (ticket--));
    					} catch (InterruptedException e) {
    						e.printStackTrace();
    					}
    				}
    			}
    		}
    	}
    	public static void main(String[] args) {
    		ThreadTask thread = new ThreadTask();
    		new Thread(thread, "线程1").start();
    		new Thread(thread, "线程2").start();
    	}
    }

     

     

     

     

     

    展开全文
  • 在很多博客中用这样一个... Runnable更容易实现资源共享,能多个线程同时处理一个资源。 看代码: public static void main(String[] args) { new MyThread().start(); new MyThread().start(); ...
  • JAVA中Runnable和Thread

    2018-05-05 11:52:57
    JAVA多线程之Runnable和Thread比较 转自:https://blog.csdn.net/zengmingen/article/details/53217229 在我们开发的过程中常常会碰到多线程的问题,对于多线程的实现方式主要有两种:实现Runnable接口、继承...
  • 概述 Runnable 是接口。...public Thread(Runnable target) 构造方法接受Runnable的子类实例,也就是说可以通过Thread类来启动Runnable实现的多线程。 Runnable的好处 在程序开发中只要是多线程肯定永...
  • Thread和Runnable的联系 Thread类的定义: 1 public class Thread extends Object implements Runnable 联系:从Thread类的定义可以看到,Thread类实现了Runnable接口,即可以说ThreadRunnable的子类。 ...
  • 一使用Thread实现多线程模拟铁路售票系统 1代码 public class ThreadDemo { public static void main( String[] args ) { TestThread newTh = new TestThread( ); // 一个线程对象只能启动一次 newTh.start...
  • 我们都知道java实现线程有两种...继承Thread类 二。实现Runnable接口 看到很多说法他们之间有一个区别是:实现Runnable接口才能实现资源共享。继承thread的方式不行 并且附有类似以下的实例: //Implement
  • Thread类与Runnable接口的比较: 实现一个自定义的线程类,可以有继承Thread类或者实现Runnable接口这两种方式,它们之间有什么优劣呢? 由于Java“单继承,多实现”的特性,Runnable接口使用起来比Thread更灵活。 ...
  •  Runnable和Callable的区别是,  (1)Callable规定的方法是call(),Runnable规定的方法是run()。  (2)Callable的任务执行后可返回值,而Runnable的任务是不能返回值得  (3)call方法可

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 777
精华内容 310
关键字:

runnable和thread比较