Java并发编程-多线程实现Runnable和Thread比较

Java实现多线程有两种基础方式:继承Thread类和实现Runnable接口。虽然两者都能创建线程,但实现Runnable接口更具扩展性,能实现资源共享,适合多线程协作。Thread类已实现Runnable接口,因此推荐使用实现Runnable接口的方式,以达到任务与线程的松耦合。代码示例展示了两种方式的执行结果和资源共享的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

实现多线程基础的两种方式有:1.继承Thread类,2.实现Runnable接口。这两种方式一般都是适用于线程数量较少的情况,由于线程的创建和销毁会降低程序性能,所以当线程数量较多情况,建议使用线程池。

介绍

实现多线程基础方式。

方式一:实现Runnable接口。Runnable接口中只有一个方法Run()方法。

public interface Runnable {
	 public abstract void run();
}

方式二:继承Thread类。Thread类继承了Runnable接口。

public class Thread implements Runnable {
}

异同点

对于实现多线程的方式,有两点不同。

1.在java中只能继承一个类,但是可以实现多个接口,所以接口实现的Runnable的方式有很好的扩展性。

2.还有另外一点有争议的是,Runnable和Thread能否实现资源的共享,也就是是否共享一个对象实例(下面通过代码探讨)。

其实真正创建线程的方式只有一种,新建一个Thread对象(也就是new Thread())。而对于多线程实现方式一,通过实现Runnable接口的类,就是产生一个任务或者工作类,交给一个或者多个线程去完成这个任务(new Thread(任务).start())。上面说到Thread类是继承了Runnable接口的,而Thread就是线程类。所以通过继承Thread接口,重写run()方法,就是任务和线程绑定到了一起。

建议采用是实现Runnable接口的方式来实现多线程,这种方式只是产生了一个任务类,然后交给Thread线程去执行任务。将线程和任务进行了分离,达到了松耦合的目的。

代码实现

先看实现Runnable接口的方式。

public class ThreadTask implements Runnable {

  private int ticket = 10;
  public void run() {
    for (int i = 0; i < 10; i++) {
      if (this.ticket > 0) {
        try {
          Thread.sleep(100);
          System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
  public static void main(String[] args) {
    ThreadTask thread = new ThreadTask();
    new Thread(thread, "线程1").start();
    new Thread(thread, "线程2").start();
  }
}

执行结果:

线程2---卖票:ticket:10
线程1---卖票:ticket:9
线程1---卖票:ticket:8
线程2---卖票:ticket:7
线程2---卖票:ticket:6
线程1---卖票:ticket:5
线程1---卖票:ticket:4
线程2---卖票:ticket:3
线程1---卖票:ticket:2
线程2---卖票:ticket:1
线程1---卖票:ticket:0

上面说过实现Runnable接口的类,只是产生一个任务的类,在main()方法中我们先创建了一个任务类ThreadTask,

然后分别启动了两个线程去执行这个任务,所以两个线程合力完成了这个任务,也就是说资源被共享。

再看继承Thread类的方式

 

public class MyThread extends Thread {

  private int ticket = 10;
  @Override
  public void run() {
    for(int i = 0; i < 10; i++) {
      if(this.ticket>0) {
        try {
          Thread.sleep(100);
          System.out.println(Thread.currentThread().getName()+"---卖票:ticket:"+(ticket--));
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
  public static void main(String[] args) {
    MyThread thread = new MyThread();
    MyThread thread2 = new MyThread();
    thread.start();
    thread2.start();
  }
}

执行结果

Thread-1---卖票:ticket:10
Thread-0---卖票:ticket:10
Thread-1---卖票:ticket:9
Thread-0---卖票:ticket:9
Thread-1---卖票:ticket:8
Thread-0---卖票:ticket:8
Thread-1---卖票:ticket:7
Thread-0---卖票:ticket:7
Thread-1---卖票:ticket:6
Thread-0---卖票:ticket:6
Thread-1---卖票:ticket:5
Thread-0---卖票:ticket:5
Thread-0---卖票:ticket:4
Thread-1---卖票:ticket:4
Thread-1---卖票:ticket:3
Thread-0---卖票:ticket:3
Thread-1---卖票:ticket:2
Thread-0---卖票:ticket:2
Thread-1---卖票:ticket:1
Thread-0---卖票:ticket:1

Thread就是个线程类,而且实现了Runnable接口。因为MyThread类继承Thread类,并重写run()(里面是任务),所以线程上绑定了任务。

MyThread类继承了Thread线程类,所以MyThread本身就是个线程类,可以通过start()方法直接启动线程。

下面这种不能说是错误的方式,但是以一种不是很合理的方式演示了继承Thread来实现多线程的方式。因为Thread实现了Runnable接口,而ThreadTask又继承Thread了,所以也可以说ThreadTask类是任务类,通过new Thread(任务).start()来执行任务也说的通,但是有一点,如果说将继承Thread类,比如上面的MyThread类,只是让这个类变成一个任务类,为什么不直接通过实现Runnable()的方式得到一个任务类。

所以按上面分析,实现Runnable接口的方式可以实现资源共享,因为多个线程合力完成一个任务,这个任务的所有资源可以被所有的线程共享到。而继承Thread类的方式不能实现资源共享,因为继承Thread的类本身就是一个线程类,并且线程上绑定了任务,一个线程类绑定一个任务类,本线程里面的资源只能被本线程使用,别的线程不能共享到该线程的任务上的资源。

public class ThreadTask extends Thread {
  private int ticket = 10;

  public void run() {
    for (int i = 0; i < 10; i++) {
      if (this.ticket > 0) {
        try {
          Thread.sleep(100);
          System.out.println(Thread.currentThread().getName() + "---卖票:ticket:" + (ticket--));
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }
  public static void main(String[] args) {
    ThreadTask task = new ThreadTask();
    new Thread(task,"线程1").start();
    new Thread(task,"线程2").start();
  }
}

执行结果:

线程1---卖票:ticket:10
线程2---卖票:ticket:9
线程1---卖票:ticket:8
线程2---卖票:ticket:7
线程2---卖票:ticket:6
线程1---卖票:ticket:5
线程1---卖票:ticket:4
线程2---卖票:ticket:3
线程1---卖票:ticket:2
线程2---卖票:ticket:1

当然,这个演示的例子有很多要改进。就拿实现Runnable接口的方法来看,存在多个线程并发抢夺资源的情况。

线程1如果将数据判断if(ticket>0)为真,此时线程2控制了资源。此时如果线程2将ticket数值修改为0,线程1依旧会进行下面的操作,造成多卖票的情况。改进如下:


 
public class ThreadTask implements Runnable {

	private int ticket = 10;
	public void run() {
		for (int i = 0; i < 10; i++) {
			synchronized (this) {
				if (this.ticket > 0) {
					try {
						Thread.sleep(100);
						System.out.println(Thread.currentThread().getName() + "卖票:ticket:" + (ticket--));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
	public static void main(String[] args) {
		ThreadTask thread = new ThreadTask();
		new Thread(thread, "线程1").start();
		new Thread(thread, "线程2").start();
	}
}

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值