Thread和Runnable,你该知道这一点

因为售票的例子博友们已经列举太多了,主要取决于是否采用new Thread(Runnable target, String name)构造创建实例,如果不想看例子的,直接看结果分析

1.Thread和Runuable的关系

public interface Runnable {
public abstract void run();
}
public class Thread implements Runnable {

}

从这两段代码我们可以知道Runnable是一个接口,而Thread实现了这个接口,即Thread是Runnable的实现类。

2.Thread和Runnable的比较

当要联网或处理耗时逻辑的时候,我们要开启子线程,那选用那种方式好呢?Runnable具有以下优势:
(1)java不支持多继承,实现Runnable还可以继承其它类,调用其功能,更具灵活性
(2)增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
(3)适合多个相同的程序代码的线程去处理同一个资源(有很多博友有这样的说法,个人存疑,因为Thread方式也可以达到同样目的)

还是用售票的例子吧,话不多说,上代码:

/**
 * Time: xxx
 * Author: xxx
 * Description: 继承Thread方式实现多线程并发售票
 */
public class TicketThread extends Thread {
    //4张票
    private int ticket = 4;

    @Override
    public void run() {
        for (int i = 0; i < 4; i++) {
            synchronized (this) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName()+"卖票--->"+(ticket--));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            }
        }
    }

    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        new Thread(ticketThread, "线程1").start();
        new Thread(ticketThread, "线程2").start();
        new Thread(ticketThread, "线程3").start();

    }
}

结果如下:
线程2卖票—>3
线程3卖票—>1
线程1卖票—>2

/**
 * Time: xxx
 * Author: xxx
 * Description: 实现Runnable的方式实现多线程卖票
 */
public class TicketRunnable implements Runnable {

    private int ticket = 3;
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            synchronized (this) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println(Thread.currentThread().getName()+"卖票--->"+(ticket--));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketRunnable ticketRunnable = new TicketRunnable();
        new Thread(ticketRunnable, "线程1").start();
        new Thread(ticketRunnable, "线程2").start();
        new Thread(ticketRunnable, "线程3").start();

    }
}

线程2卖票—>3
线程3卖票—>1
线程1卖票—>2

有个小细节要注意一下,一定要加上synchronized同步锁,否则可能会出现其中一个线程出现卖票—>0的情况,这很好理解,如果没加synchronized,也就是线程不安全,当其中一个线程卖最后一张票ticket=1执行ticket–的时候此时ticket已经为0了,但是另一个线程已经执行完了if(this.ticket>0),再执行ticket–的时候就会出现卖票—>0的情况的情况。这里就不演示了,有兴趣的博友可以自己试一下,如果一次没出现,就运行几次,总会中奖的。

至于博友们采用以下的写法:

public class TicketThread extends Thread {
    //3张票
    private int ticket = 3;

    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
                if (this.ticket>0) {
                    try{
                        Thread.sleep(100);
                        System.out.println("ticket= "+ticket--);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
        }
    }

    public static void main(String[] args) {
        TicketThread ticketThread = new TicketThread();
        new TicketThread().start();
        new TicketThread().start();
        new TicketThread().start();
    }
}

结果:
ticket= 3
ticket= 3
ticket= 3
ticket= 2
ticket= 2
ticket= 2
ticket= 1
ticket= 1
ticket= 1

3. 结果分析

并不是说Runnable支持资源的共享,而Thread不支持。
本质是:构造方法传参的问题,是要传参,持有Runnable的引用;还是不传参,new新的对象
以这种方式使用Thread(不传参)时,每一个New都会产生一个拥有自己的ticket的对象,即每个对象都有各自的ticket,各操作各的,三个ticket数值变化互不影响。就相当于new了三个对象,把方法调用了三遍。如果传参,同样可以达到“资源共享”的效果。在使用Runnable的整个过程中(传参了),只产生一个能操作ticket的对象,实现多个线程共同处理同一资源(ticket),即实现资源的共享;

原创文章 23 获赞 30 访问量 9578

猜你喜欢

转载自blog.csdn.net/my_csdnboke/article/details/86747583