昨晚在看死锁相关的东西,看到了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接口的方法便捷一点。
新闻热点
疑难解答