Java并发编程(6):Runnable和Thread实现多线程的区别(含代码)

Java中实现多线程有两种方法:继承Thread类、实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势:

1、可以避免由于Java的单继承特性而带来的局限;

2、增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的;

3、适合多个相同程序代码的线程区处理同一资源的情况。

下面以典型的买票程序(基本都是以这个为例子)为例,来说明二者的区别。

首先通过继承Thread类实现,代码如下:

class MyThread extends Thread{
private int ticket = 5;
public void run(){
for (int i=0;i<10;i++)
{
if(ticket > 0){
System.out.println("ticket = " + ticket--);
}
}
}
}

public class ThreadDemo{
public static void main(String[] args){
new MyThread().start();
new MyThread().start();
new MyThread().start();
}
}
某次的执行结果如下:

从结果中可以看出,每个线程单独卖了5张票,即独立地完成了买票的任务,但实际应用中,比如火车站售票,需要多个线程去共同完成任务,在本例中,即多个线程共同买5张票。

下面是通过实现Runnable接口实现的多线程程序,代码如下:

class MyThread implements Runnable{
private int ticket = 5;
public void run(){
for (int i=0;i<10;i++)
{
if(ticket > 0){
System.out.println("ticket = " + ticket--);
}
}
}
}

public class RunnableDemo{
public static void main(String[] args){
MyThread my = new MyThread();
new Thread(my).start();
new Thread(my).start();
new Thread(my).start();
}
}
某次的执行结果如下:

从结果中可以看出,三个线程一共卖了5张票,即它们共同完成了买票的任务,实现了资源的共享。

针对以上代码补充三点:

1、在第二种方法(Runnable)中,ticket输出的顺序并不是54321,这是因为线程执行的时机难以预测,ticket–并不是原子操作。

2、在第一种方法中,我们new了3个Thread对象,即三个线程分别执行三个对象中的代码,因此便是三个线程去独立地完成卖票的任务;而在第二种方法中,我们同样也new了3个Thread对象,但只有一个Runnable对象,3个Thread对象共享这个Runnable对象中的代码,因此,便会出现3个线程共同完成卖票任务的结果。如果我们new出3个Runnable对象,作为参数分别传入3个Thread对象中,那么3个线程便会独立执行各自Runnable对象中的代码,即3个线程各自卖5张票。

3、在第二种方法中,由于3个Thread对象共同执行一个Runnable对象中的代码,因此可能会造成线程的不安全,比如可能ticket会输出-1(如果我们System.out….语句前加上线程休眠操作,该情况将很有可能出现),这种情况的出现是由于,一个线程在判断ticket为1>0后,还没有来得及减1,另一个线程已经将ticket减1,变为了0,那么接下来之前的线程再将ticket减1,便得到了-1。这就需要加入同步操作(即互斥锁),确保同一时刻只有一个线程在执行每次for循环中的操作。而在第一种方法中,并不需要加入同步操作,因为每个线程执行自己Thread对象中的代码,不存在多个线程共同执行同一个方法的情况。

原文地址:http://blog.51cto.com/13963248/2314483

时间: 2024-10-28 11:50:04

Java并发编程(6):Runnable和Thread实现多线程的区别(含代码)的相关文章

转: 【Java并发编程】之十三:生产者—消费者模型(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. 这里实现如下情况的生产--消费模型: 生产者不断交替地生产两组数据"姓名--1 --> 内容--1","姓名--2--> 内容--2",消费者不断交替地取得这两组数据,这里的"姓名--1&quo

Java并发编程】之十三:生产者—消费者模型(含代码)

生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. 这里实现如下情况的生产--消费模型: 生产者不断交替地生产两组数据"姓名--1 --> 内容--1","姓名--2--> 内容--2",消费者不断交替地取得这两组数据,这里的"姓名--1"和"姓名--2"模拟为数据的名称,"内容--1 "和"内容--2 &q

【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17161237 Java中实现多线程有两种方法:继承Thread类.实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势: 1.可以避免由于Java的单继承特性而带来的局限: 2.增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的: 3.适合多个相同程序代码的线程区处理同一资源的情

Runnable和Thread实现多线程的区别(含代码)

转载请注明出处:http://blog.csdn.net/ns_code/article/details/17161237 Java中实现多线程有两种方法:继承Thread类.实现Runnable接口,在程序开发中只要是多线程,肯定永远以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类有如下优势: 1.可以避免由于Java的单继承特性而带来的局限: 2.增强程序的健壮性,代码能够被多个线程共享,代码与数据是独立的: 3.适合多个相同程序代码的线程区处理同一资源的情

JAVA并发编程——守护线程(Daemon Thread)

在Java中有两类线程:用户线程 (User Thread).守护线程 (Daemon Thread). 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分.因 此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程.反过来说,只要任何非守护线程还在运行,程序就不会终止. 用户线程和守护线程两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下

Java并发编程-创建线程的两种方式及区别

转载请注明:http://blog.csdn.net/UniKylin/article/details/45016117 1.线程和进程的区别 并行:是多个任务在同一时间同时执行,例如多核计算机同时计算的任务可以理解为并行 并发:从微观上看是多个任务抢占一个CPU从而执行自己的任务,轮流执行任务,但是如果遇到资源冲突的时候并没有从根本提高执行效率.但是提高了CPU的使用效率. 前段时间在GitHub上的一幅图可以很好的阐述上面的概念非常形象 2.Java中创建线程的两种方式 1.第一种方式:直接

Java并发编程(二)为什么需要多线程

如果不考虑多线程的话,那么在程序只有一条执行路径,代码串行执行:顺序执行.选择或者循环.单线程就像你用你惯常的手去写字,多线程编程就要求你左手画圆,右手画方.一不留神就会手忙脚乱,圆不是圆,方也不像方.在继续学习多线程编程之前,先来小小的回顾一下操作系统的进化史. 手工操作:计算机并不包含操作系统,从头到尾只执行一个程序,这个程序可以访问程序中所有的资源. 批处理系统:加载在计算机上的一个系统软件,在它的控制下,计算机能够自动地.成批地处理一个或多个用户的作业(这作业包括程序.数据和命令). 多

Java并发编程(三)volatile域

相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Android多线程(一)线程池 Android多线程(二)AsyncTask源代码分析 前言 有时仅仅为了读写一个或者两个实例域就使用同步的话,显得开销过大,volatile关键字为实例域的同步訪问提供了免锁的机制.假设声明一个域为volatile,那么编译器和虚拟机就知道该域是可能被还有一个线程并发更新的. 再讲到volatile关键字之前我们须要了解一下内存模型的相关概念以及并发编程中的三个特性:原子性,可见

Java并发编程之线程创建和启动(Thread、Runnable、Callable和Future)

这一系列的文章暂不涉及Java多线程开发中的底层原理以及JMM.JVM部分的解析(将另文总结),主要关注实际编码中Java并发编程的核心知识点和应知应会部分. 说在前面,Java并发编程的实质,是线程对象调用start方法启动多线程,而线程对象则必须是Thread类或其子类实现.Runnable和Callable的作用类似于Comparable.Serializable,是用于被并发的类实现的接口,从而使得Thread类可以在初始化时传入这个被并发的类.此是大前提.本文从多线程实现和启动出发,对