1)java要完成多线程的创建有四种方式:
①继承Thread类,重写run方法
②实现Runnable接口,重写run方法
③通过线程池创建线程
④实现Callable接口,该接口被重写的方法带有返回值,异常信息等
这里我只讲继承Thread类和实现Runnable接口的区别
2)那么这两种创建多线程的方式到底有哪些不同呢,顾名思义,一个是继承一个是实现。
众所周知,java本质上其实是单继承的,举个例子。现在有两个类,Dog和Cat,按照面向对象的思想,我们可以将这两个类的共性抽取出来,抽取出来的类就是抽象类,那么Dog和Cat肯定要继承这个抽象类的,不然抽象类的存在就没有意义了。代码如下
看到这里,想必会有初学者会感到疑惑,既然父类方法体都不具体,还要子类自己去写,那为什么还要抽取父类再来继承它呢。那么这里就涉及到了多态,对多态的应用还不理解的朋友可以去多看看书。
3)那么现在给出一个场景,如果Dog和Cat不只一个呢。我们难道要去创建多个Dog和Cat类吗,很显然这里用到了多线程。那么创建多线程是继承Thread还剩实现Runable接口呢,显然,这里继承Thread是不合适的,因为Dog和Cat已经存在需要继承的父类了,不能再去继承其他类了。所以,这种情况就可以去实现Runnable接口来创建多线程。
4)说完了继承Thread的局限性,其实他们之间还有一个巨大的区别,那就是锁的问题。这里我写一个案例你们就明白了。代码如下:
下面是结果:
可以看到,加了synchronized 同步关键字之后,线程还是出现了安全问题。这是为什么呢?
显然,这肯定是锁的问题。那么被5同步方法修饰的锁对象到底是什么呢,答案是this。
测试一下:
结果如下:
可以看到,线程依旧存在安全问题,说明锁默认就是this,那么为什么this锁再这里不行呢。这里就考你面向对象学的怎么样了,如下:
因为你创建了两个线程类对象,所以当t1.start()和t2.start()后去找run方法时,this是不一样的,this分别代表t1和t2。你想想,锁都不一样了,何来同步。
解决方案:这里使用类锁,且类一样
结果如下:
可以看到,在当前线程没有执行完同步代码块时,另外的线程是进不来的。
5)接下来我们看实现Runnable接口来创建多线程有何不同
这里没有加任何同步,出现了安全问题,结果如下
解决方案:我们给run方法加上同步关键字
结果如下:
安全问题等到解决。
那么看到这里就会有人疑惑,难道这个同步锁默认是类锁吗。其实不是,就是this锁,为什么this锁可以呢?看下面:
因为我这里只创建了一个My1对象,t1和t2共用一个My1对象,my1只有一个,所有以this只能是my1。锁都一样了,还怕同步不了吗。
6)总结:继承Thread实现多线程需要造多个源对象,此时需要同步最好不好用this锁,当然具体情况具体分析。而且当前类不能再继承其他对象了。所以,以后在写代码完成某些需求要使用多线程时,尽量不要继承Thread类。最好是用实现Runable来创建多线程。