多线程并发基础外传之Thread的静态代理

299 阅读4分钟

1. 写在前面

我们或许都知道Thread类可以创建线程,也基本都学习过代理模式的相关知识,但Thread和代理模式有什么关系?或许有相当一部分人不太清楚。其实,Thread是用到了静态代理模式的。那是为什么呢?来,看完这篇文章你就知道了。

2. 先回顾下静态代理

我们都知道,静态代理,可以增强被代理对象的功能,完成被代理对象所做不了的事儿,而被代理对象只需要专心做它自己需要做的事情就好了。其他的交给代理对象来完成。哈哈,是不是被说迷糊了,ok,我们来看个例子,相信看完你就清楚静态代理是做了什么事儿了。

假如你要结婚,那一般都要找一家第三方机构比如婚庆公司来委托办理。在结婚之前帮你选场地布置场地等,在结婚之后帮你收拾场地。而你,就可以更加专心的打扮自己,只需要在结婚时闪亮登场。其他的事儿婚庆公司都帮你做了。在这里,婚庆公司就是一个代理,你就是被代理对象。这是之前学习的时候看过的例子,很形象。那么怎么用代码去实现呢?

public class ThreadDemo {
    public static void main(String[] args) {
        Man man = new Man();
        WeddingCompany weddingCompany = new WeddingCompany(man);
        weddingCompany.doMarry();
    }
}
// 新郎和婚庆公司都要实现的接口
interface Marry {
    public void doMarry();
}
// 新郎类
class Man implements Marry{
    @Override
    public void doMarry() {
        System.out.println("我是新郎,正在结婚");
    }
}
// 婚庆公司类
class WeddingCompany implements Marry {
    Marry target; // 存放被代理对象
    
    public WeddingCompany(Marry target) {
        this.target = target;
    }
    
    @Override
    public void doMarry() {
        System.out.println("我是婚庆公司,在结婚之前,我在布置场地。。。");
        target.doMarry();
        System.out.println("我是婚庆公司,在结婚之后,我在收拾场地。。。");
    }
}

输出结果:

image.png

解释一下:在这里我首先声明了一个接口Marry,然后分别创建实现了此接口的新郎类Man和婚庆公司类WeddingCompany。在婚庆公司类里面的接口实现方法doMarry中,调用了新郎类的接口实现方法doMarry,并且在它之前和之后都做了一些事情。最终,只需要委托婚庆公司,即可完成结婚的全部流程。

再抽象一点,就是被代理类和代理类都要实现同一个接口,并且代理类的接口实现方法里面,要调用被代理类的接口实现方法。这样,只需调用代理类的接口实现方法,即可完成需求

这,就是静态代理。

3. 静态代理,和Thread啥关系

我们先把上面的代码,和线程创建调用的代码,放到一起对比一下

WeddingCompany weddingCompany = new WeddingCompany(new Man()); // 创建代理对象
weddingCompany.doMarry(); // 开始结婚

Thread thread = new Thread(new ThreadSun()); // 创建线程对象
thread.start(); // 开始线程

-------------------------------------------------------------------------------------------
// ThreadSun是一个实现了Runnable接口的类
class ThreadSun implements Runnable {
    @Override
    public void run() {
        System.out.println("我是线程ThreadSon");
    }
}
-------------------------------------------------------------------------------------------

你会发现,这里调用格式就好像啊。接下来,我们只需要把静态代理的特点和Thread进行对比,就可以验证Thread是不是用到静态代理了。

首先,Thread作为代理类,它和被代理类,比如这里的ThreadSun,有没有实现同一个接口?答案:有,是Runnable接口。

ThreadSun实现了Runnable很明显,Thread呢?我们可以看下源码,

image.png 发现它其实也是实现了Runnable接口的

然后,代理类Thread和被代理类ThreadSun,有没有实现同一个方法,且在Thread的接口实现方法里面,有没有对ThreadSun的接口实现方法完成调用?答案:有,看run方法。

Runnable接口要求实现run方法,在ThreadSun里面显然已经实现了run方法,Thread方法呢?还是看源码

image.png 发现它也实现了run方法,并调用了targetrun方法。我们先不急继续验证,按照静态代理的思想,结合上面婚庆公司的例子,这里的target应该是Runnable的接口对象。来,验证一下:

image.png

果然!!!验证完毕。

4. 结论

当我们使用实现Runnable接口来创建线程的时候,其实用到了静态代理。Thread是代理类,实现Runnable接口的类是被代理类。两者都实现了Runnable接口,都实现了run方法,并且在Threadrun方法里面,有调用被代理类的run方法。