昨晚在看死锁相关的东西,看到了synchronized
关键字,然后就想写一篇关于synchronized
关键字的博客,就去仔细看了看相关的东西。synchronized
关键字在使用多线程的时候使用,于是复习一下多线程的东西,写篇博客加深一下印象。 java中实现多线程的方式有两种,一是继承Thread
类,二是实现Runnable
接口,先看继承Thread
类。
Thread
类 Thread
是java.lang
包中的一个类,实现了Runnable
接口,Runnable
接口源码如下,简单粗暴。
ClassExtendsThread
类,继承了Thread
类,在run
方法中依次递减打印出10到1。
main
函数代码如下:
运行结果如下:
Thread-0--->10Thread-1--->10Thread-0--->9Thread-1--->9Thread-0--->8Thread-1--->8Thread-0--->7Thread-1--->7Thread-0--->6Thread-1--->6Thread-0--->5Thread-0--->4Thread-1--->5Thread-0--->3Thread-0--->2Thread-1--->4Thread-0--->1Thread-1--->3Thread-1--->2Thread-1--->1 从结果中可以看出,两个线程都启动起来,并且交叉运行,但是这两个线程可以认为他们是不关联的,因为线程1和线程2各自拥有了新的num
变量,即这两个线程不共享该变量。接下来看实现Runnable
接口的多线程Demo。
Runnable
接口上面已经看了Runnable接口的源码,直接上Demo。
public class ClassImplRunnable implements Runnable { private int num = 10; @Override public void run() { for (int i = 0; i < 10; i++) { if (num > 0) { // 因为Runnable中没有currentThread方法,所以要加上Thread System.out.println(Thread.currentThread().getName() + "--->" + num--); } } }} ClassImplRunnable
和ClassExtendsThread
代码几乎一样,不同的是实现了Runnable
接口。测试代码如下:
这里的ClassImplRunnable
只实现了Runnable
接口,但是没有start
方法,无法启动线程,但是Thread
类中有个构造方法:
传入一个实现Runnable
接口的类即可创建一个新的线程,然后调用start
方法即可启动线程。注意启动线程只能用start
方法,如果调用run
方法,那么就只是执行了一个方法,并没有启动线程。 运行结果如下:
两个线程交叉运行,因为没有使用同步锁。同时,这里两个线程使用了同一个对象构造了Thread
,所以其实两个线程运行的时候共享了num
变量。 仔细回顾一下两次运行,发现继承Thread
类的ClassExtendsThread
类运行时,是直接创建了两个ClassExtendsThread
对象,但是ClassImplRunnable
类是创建了一个对象,但是创建了两个Thread
对象并启动线程,那么就不能说继承Thread
的类实现多线程不能共享资源,实现了Runnable
接口的多线程才能共享资源。 接下里来模仿一下ClassImplRunnable
类启动线程的方式,代码如下:
运行结果如下:
Thread-2--->10Thread-1--->9Thread-1--->7Thread-1--->6Thread-1--->5Thread-1--->4Thread-1--->3Thread-1--->2Thread-1--->1Thread-2--->8 结果可以看出,其实是共享了资源(num)的。 这里线程名字变了,因为new了一个ClassExtendsThread
对象的时候,就已经创建了一个线程,而后面调用Thread
的构造函数又创建了两个线程,而且只启动了线程2和3,即Thread-1
、Thread-2
。 今天下午查了一下午资料,也没有一个讲的清楚的好文章,最后灵光一闪,用Google搜了一下,结果直接搜出来两篇讲的很好的文章,一个是GitHub的,一个是博客园的,按道理说百度和必应都能搜出来的,但是。。。
用Thread
和Runnable
都可以实现多线程,但是一般情况下还是用实现Runnable
接口的方法来实现多线程,原因有以下几点: 1. 因为Java的单继承特性,如果继承Thread
的话就不能再继承其他类,而接口是可以实现多个的,所以实现Runnable
接口更加灵活; 2. 关于很多文章提到的资源共享问题,继承了Thread
类的类的对象,已经是一个线程了,是可以直接调用start
方法来启动线程的,但是如果要实现类似于ClassImplRunnable
这样的所谓的“资源共享”,还需要把对象传入Thread
的构造函数重新实例化Thread
对象,比较麻烦,所以还是用实现Runnable
接口的方法便捷一点。
新闻热点
疑难解答