(十四)探讨Runnable可以共享数据的原因

前言:最近看博客看到Thread和Runnable的区别,区别主要是两个:1.Thread实现了Runnable,但使用Thread需要继承丧失了继承其他类的能力,Runnable没有这个问题,因为使用中用实现代替了继承。2.Runnable可以共享数据。那为啥呢?


之前阅读博客:

点击打开链接


demo:

1.普通的例子

2.结合Runnable例子


1. 举个类似的例子

场景:一个班有50名同学,然后班主任介绍了一个插班生进来,人数变为51;接着副班主任也介绍了一个插班生进来,人数变成52。

对应demo:

package com.example.demo_13_runnable;

public class RunnableTest {
    public static void main(String args[]){
        Banji banji = new Banji(50);
        BanZhuRen banZhuRen = new BanZhuRen(banji);
        System.out.println("the number of banji is :" + banZhuRen.addChabanSheng().toString());

        BanZhuRen fubanZhuRen = new BanZhuRen(banji);
        System.out.println("the number of banji is :" + fubanZhuRen.addChabanSheng().toString());

    }
}

class BanZhuRen{
    private Banji banji;
    public BanZhuRen(Banji banji){
        this.banji = banji;
    }

    public Banji addChabanSheng(){
        return banji.addChabanSheng();
    }
}

class Banji{
    private int numOfStudent  = 50;
    Banji(int numOfStudent){
        this.numOfStudent = numOfStudent;
    }

    public void setNumOfStudent(int numOfStudent) {
        this.numOfStudent = numOfStudent;
    }

    public int getNumOfStudent() {
        return numOfStudent;
    }

    public Banji addChabanSheng(){
        this.numOfStudent++;
        return this;
    }

    @Override
    public String toString() {
        return numOfStudent + "";
    }
}

运行结果:

the number of banji is :51
the number of banji is :52

Process finished with exit code 0


解释:

班主任和副班主任能共享numOfStudent的原因就在于他们管的班是同一个班(同一个班级类),当然学生人数即numOfStudent也是共享的。


注意区别:

public class RunnableTest {
    public static void main(String args[]){
        Banji banji = new Banji(50);
        BanZhuRen banZhuRen = new BanZhuRen(new Banji(50));
        System.out.println("the number of banji is :" + banZhuRen.addChabanSheng().toString());

        
    BanZhuRen fubanZhuRen = new BanZhuRen(new Banji(50));
    System.out.println("the number of banji is : + fubanZhuRen.addChabanSheng().toString());
    }
} 


2. 用Runnable举个类似上面的例子

这时候再写一个和上面类似的例子还是:

场景:一个班有50名同学,然后班主任介绍了一个插班生进来,人数变为51;接着副班主任也介绍了一个插班生进来,人数变成52。

对应demo(只是用Thread代替了自定义的班主任类):

package com.example.demo_13_runnable_levelup;

public class RunnableTest {
    public static void main(String args[]) throws InterruptedException {
        Banji banji = new Banji();
        Thread banZhuRen = new Thread(banji);
        banZhuRen.start();
        Thread.sleep(200);
        System.out.println("the number of banji is :" + banji.toString());

        Thread fubanZhuRen = new Thread(banji);
        fubanZhuRen.start();
        Thread.sleep(200);
        System.out.println("the number of banji is :" + banji.toString());
    }
}



class Banji implements Runnable{
    private int numOfStudent  = 50;

    @Override
    public void run() {
        addChabanSheng();
    }

    public synchronized Banji addChabanSheng(){
        this.numOfStudent++;
        return this;
    }

    @Override
    public String toString() {
        return numOfStudent + "";
    }
}

运行结果:

the number of banji is :51
the number of banji is :52

Process finished with exit code 0

解释:

班主任(Thread)和副班主任(Thread)能共享Runnable的参数numOfStudent的原因就在于他们管的班是同一个班(同一个Runnable类),当然学生人数即numOfStudent也是共享的。


PS:解释下为什么要睡200ms,由于Thread.start只要将线程状态变为可执行,并没有立刻跑起来,这取决于jvm。我尴尬地发现不加sleep会出现50 和 51的情况。


3.结合源码解释

对应源码如下:

/**
     * Allocates a new {@code Thread} object. This constructor has the same
     * effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
     * {@code (null, target, gname)}, where {@code gname} is a newly generated
     * name. Automatically generated names are of the form
     * {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
     *
     * @param  target
     *         the object whose {@code run} method is invoked when this thread
     *         is started. If {@code null}, this classes {@code run} method does
     *         nothing.
     */
    public Thread(Runnable target) {
        init(null, target, "Thread-" + nextThreadNum(), 0);
    }
/**
     * Initializes a Thread.
     *
     * @param g the Thread group
     * @param target the object whose run() method gets called
     * @param name the name of the new Thread
     * @param stackSize the desired stack size for the new thread, or
     *        zero to indicate that this parameter is to be ignored.
     */
    private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
        Thread parent = currentThread();
        if (g == null) {
            g = parent.getThreadGroup();
        }

        g.addUnstarted();
        this.group = g;

        this.target = target;
        this.priority = parent.getPriority();
        this.daemon = parent.isDaemon();
        setName(name);

        init2(parent);

        /* Stash the specified stack size in case the VM cares */
        this.stackSize = stackSize;
        tid = nextThreadID();
    }
 /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }

解释:可以看到两个Thread的target参数其实是同一个Runnable对象,然后调用其run()方法就是用的同一个对象的run()方法,那自然共享Runnable里面的数据了。


4.总结

其实共享数据不是什么很稀奇的地方,同一个对象当然共享数据嘛。还是mark一下Thread 和 Runnable的区别吧:1.Thread实现了Runnable,但使用Thread需要继承丧失了继承其他类的能力,Runnable没有这个问题,因为使用中用实现代替了继承。2.Runnable可以共享数据。

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/79671589