java创建线程implement runnable 和 extends thread 比较

我们都知道java实现线程有两种方法

一。继承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类有如下好处:

  ->避免单点继承的局限,一个类可以继承多个接口。

  ->适合于资源的共享


  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值