java创建线程implement runnable 和 extends thread 比较
2014-12-31 17:07
513 查看
我们都知道java实现线程有两种方法
一。继承Thread类
二。实现Runnable接口
看到很多说法他们之间有一个区别是:实现Runnable接口才能实现资源共享。继承thread的方式不行
并且附有类似以下的实例:
输出结果如下:
他们通过这个例子得出:实现Runnable接口才能实现资源共享。继承thread的方式不行 这个结论
其实我们仔细看一下这个例子,就会发现这个结论是错的:
1.首先我们看一下通过 Implements Runnable 方式创建线程时,发生了什么
截取上面例子中的两行代码:
我们进入Thread类的源码看一下(经过简化,要查看完整方法,参看java源码):
当new Thread的时候,会调用Thread的init()方法,初始化这个线程
我们看到这个函数的关键是用传递给他的参数初始化这个Thread,注释中明确说道调用Thread的start()方法时,最终会调用的是这个target参数的run方法,当这个Thread调用start()方法后,最终会调用到如下run()方法:
我们可以看到正如前面init()方法的注释所描述的一样,最终会调用的是线程init()时传递给这个Thread的target参数的run方法。
所以正如一开始的例子中的代码那样,用同一个Runnable target初始化多个线程,那么这多个线程都会调用同一个target的run()方
法。至始至终这个ImplementsRunnable对象就只有一个。
在这里要加一段话 虽然这样实现了资源的共享,但是并没有实现资源的同步,如下例子,我加入一个条件,如果counter小于10才能继续+1(比如多个窗口同时卖火车票的场景):
修改后的代码如下:
那我们在看看通过extend Thread 的方式。它并没有一个共同的Runnable对象来初始化3个Thread对象。每一个Thread都拥有自己一个独立的Runnable对象。
当然不能实现资源的共享,因为现在每个Thread都有一个自己counter对象了。
那么问题来了:难道用extends Thread的方式就不能实现共享吗,答案是肯定可以实现:
方法有很多。我自己试着写了一个:
运行结果如下:
可以看到 在这段代码中 我counter设置成了static,相应的用了class级别的锁。
不过相比implement runnable的方式还是麻烦了很多。
所以从上面两个比较我们可以得出
实现Runnable接口相比继承Thread类有如下好处:
->避免单点继承的局限,一个类可以继承多个接口。
->适合于资源的共享
一。继承Thread类
二。实现Runnable接口
看到很多说法他们之间有一个区别是:实现Runnable接口才能实现资源共享。继承thread的方式不行
并且附有类似以下的实例:
//Implement Runnable 的方式来实现线程 class ImplementsRunnable implements Runnable { private int counter = 0; public void run() { counter++; System.out.println("ImplementsRunnable : Counter : " + counter); } } //通过继承 Thread 类的方式 class ExtendsThread extends Thread { private int counter = 0; public void run() { counter++; System.out.println("ExtendsThread : Counter : " + counter); } } public class ThreadVsRunnable { public static void main(String args[]) throws Exception { // 多线程共享同一个变量(rc) ImplementsRunnable rc = new ImplementsRunnable(); Thread t1 = new Thread(rc); t1.start(); Thread.sleep(1000); //启动下一个线程前,等待一秒钟 Thread t2 = new Thread(rc); t2.start(); Thread.sleep(1000); //启动下一个线程前,等待一秒钟 Thread t3 = new Thread(rc); t3.start(); // 通过extend Thread只能为每一个线程创建新的对象 ExtendsThread tc1 = new ExtendsThread(); tc1.start(); Thread.sleep(1000); ExtendsThread tc2 = new ExtendsThread(); tc2.start(); Thread.sleep(1000); ExtendsThread tc3 = new ExtendsThread(); tc3.start(); } }
输出结果如下:
ImplementsRunnable : Counter : 1 ImplementsRunnable : Counter : 2 ImplementsRunnable : Counter : 3 ExtendsThread : Counter : 1 ExtendsThread : Counter : 1 ExtendsThread : Counter : 1
他们通过这个例子得出:实现Runnable接口才能实现资源共享。继承thread的方式不行 这个结论
其实我们仔细看一下这个例子,就会发现这个结论是错的:
1.首先我们看一下通过 Implements Runnable 方式创建线程时,发生了什么
截取上面例子中的两行代码:
ImplementsRunnable rc = new ImplementsRunnable(); Thread t1 = new Thread(rc);rc对象是一个Runnable类,把它作为参数通过new Thread(Runnable arg)初始化了一个Thread。
我们进入Thread类的源码看一下(经过简化,要查看完整方法,参看java源码):
当new Thread的时候,会调用Thread的init()方法,初始化这个线程
/** * 初始化一个Thread * ....... * @参数 target :这个target参数的run()方法将会被最后调用(the object whose run() method gets called) * ........ */ private void init(...,Runnable target,....) { ...... this.target = target; ...... }
我们看到这个函数的关键是用传递给他的参数初始化这个Thread,注释中明确说道调用Thread的start()方法时,最终会调用的是这个target参数的run方法,当这个Thread调用start()方法后,最终会调用到如下run()方法:
public void run() { if (target != null) { target.run(); } }
我们可以看到正如前面init()方法的注释所描述的一样,最终会调用的是线程init()时传递给这个Thread的target参数的run方法。
所以正如一开始的例子中的代码那样,用同一个Runnable target初始化多个线程,那么这多个线程都会调用同一个target的run()方
法。至始至终这个ImplementsRunnable对象就只有一个。
在这里要加一段话 虽然这样实现了资源的共享,但是并没有实现资源的同步,如下例子,我加入一个条件,如果counter小于10才能继续+1(比如多个窗口同时卖火车票的场景):
class ImplementsRunnable implements Runnable { private int counter = 0; public void run() { while(counter<10){ counter++; try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(Thread.currentThread().getName()+":"+ counter); } } }
public static void main(String args[]) throws Exception {运行结果如下,出现了同步错误:
// Multiple threads share the same object.
ImplementsRunnable rc = new ImplementsRunnable(); Thread t1 = new Thread(rc);
t1.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t2 = new Thread(rc);
t2.start();
Thread.sleep(1000); // Waiting for 1 second before starting next thread
Thread t3 = new Thread(rc);
t3.start();
}
Thread-5:2 Thread-6:3 Thread-5:4 Thread-5:6 Thread-6:7 Thread-7:8 Thread-5:9 Thread-6:10 Thread-7:9 Thread-5:10如果我们要实现同步的话,就需要加入synchronized方式了(当然还可以通过原子操作的方式通过CAS无锁实现)
修改后的代码如下:
class ImplementsRunnable implements Runnable { private int counter = 0; public void run() { while(counter<10){ synchronized(this){ if(counter<10){ counter++; System.out.println(Thread.currentThread().getName()+":"+ counter); } } } } }运行结果如下,达到了我们的目的:
Thread-5:1 Thread-6:2 Thread-7:3 Thread-7:4 Thread-6:5 Thread-6:6 Thread-6:7 Thread-6:8 Thread-6:9 Thread-5:10
那我们在看看通过extend Thread 的方式。它并没有一个共同的Runnable对象来初始化3个Thread对象。每一个Thread都拥有自己一个独立的Runnable对象。
当然不能实现资源的共享,因为现在每个Thread都有一个自己counter对象了。
那么问题来了:难道用extends Thread的方式就不能实现共享吗,答案是肯定可以实现:
方法有很多。我自己试着写了一个:
package test; public class TestThread { public static void main(String args[]) throws Exception { ExtendsThread tc1 = new ExtendsThread(); tc1.start(); ExtendsThread tc2 = new ExtendsThread(); tc2.start(); ExtendsThread tc3 = new ExtendsThread(); tc3.start(); } } class ExtendsThread extends Thread { public void run() { while(Resource.getCounter()<10){ Resource.count(); } } }多加入了一个类Resource.java
package test; public class Resource { private static int counter = 0; public static int getCounter(){ return counter; } public static synchronized void count(){ if(counter<10){ counter++; try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":"+counter); } } }
运行结果如下:
Thread-5:1 Thread-5:2 Thread-6:3 Thread-5:4 Thread-7:5 Thread-6:6 Thread-7:7 Thread-6:8 Thread-7:9 Thread-6:10
可以看到 在这段代码中 我counter设置成了static,相应的用了class级别的锁。
不过相比implement runnable的方式还是麻烦了很多。
所以从上面两个比较我们可以得出
实现Runnable接口相比继承Thread类有如下好处:
->避免单点继承的局限,一个类可以继承多个接口。
->适合于资源的共享
相关文章推荐
- java创建线程implement runnable 和 extends thread 比较
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
- Java【多线程知识总结(5)】比较继承Thread类创建线程和实现Runnable接口创建线程这两种方式
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
- java 创建线程的三种方法Callable,Runnable,Thread比较及用法
- Java线程总结(一):创建线程的两种方式Thread和Runnable
- java入门教程-7.4Java创建线程(Runnable接口和Thread类)
- Runnable和Thread两种方式创建线程的比较
- 2-5-Java多线程-创建线程的Runnable接口方法及Thread源码解析
- 线程创建:继承Thread、接口Runnable(静态代理模式)、接口CallableJAVA169-173
- Java创建线程(Runnable接口和Thread类)
- Java创建线程(Runnable接口和Thread类)
- extends Thread 与 implements Runnable 的区别 创建多线程的比较
- java多线程学习1-继承Thread类和实现Runnable接口来创建线程
- Java 多线程(二)——创建线程(Thread、Runnable、Callable)
- Java多线程-(4)线程的创建、启动、Runnable和Thread
- Java中继承Thread和实现Runnable这两种创建线程有何区别