文档章节

并发编程基础一--继承Thread,实现Runnable,实现Callable和Future.

Zhao-Qian
 Zhao-Qian
发布于 2013/01/22 17:18
字数 1589
阅读 426
收藏 3
点赞 0
评论 0

一.继承Thread的线程

1.定义Thread类的子类,并重写该类的run()方法,该run()方法就是代表了线程要完成的任务.因此run方法是线程执行体.
2.创建Thread子类的实例,就是创建了线程对象.
3.调用线程对象的start()方法来启动该线程.

package org.credo.thread;

/**
 * <p>Description:继承Thread类创建线程类. </p>
 * @author <a href="zhaoqianjava@qq.com">Credo</a>
 */
public class FirstThread extends Thread{
	private int i;
	
	/* 
	 * 重写run()方法,run方法的方法体就是线程的执行体.
	 */
	public void run(){
		for(;i<10;i++){
			//当线程类继承Thread类时,直接使用this即可获取当前线程
			//Thread对象的getName()返回当前线程的名字
			//因此可以直接调用getName()方法返回当前线程的名字
			System.out.println(getName()+" "+i);
		}
	}
	
	public static void main(String[] args) {
		for(int i=0;i<10;i++){
			//调用Thread的currentThread()方法获得当前线程
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==2){
				//创建并启动第一个线程,使用start方法启动线程.
				new FirstThread().start();
				//创建并启动第二个线程
				new FirstThread().start();
			}
		}
	}
}
上面的程序使用到了线程的两个方法:
  • Thread.currentThread():currentThread()是Thread类的静态方法,该方法总是返回当前正在执行的线程对象.
  • getName():该方法是Thread的实例方法,该方法返回调用该方法的线程名字.
程序也可以通过setName去为线程设定名字.
我们看下线程的输出情况.
main 0
main 1
main 2
Thread-0 0
Thread-0 1
Thread-0 2
Thread-0 3
Thread-0 4
Thread-1 0
main 3
Thread-1 1
Thread-0 5
Thread-1 2
main 4
Thread-1 3
main 5
Thread-0 6
main 6
Thread-1 4
main 7
Thread-0 7
main 8
Thread-1 5
main 9
Thread-0 8
Thread-0 9
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9
通过上面的输出可以看得出,3个线程输出的"i 变量"是不连续的.注意,i变量是FirstThread的实例属性,而不是局部变量.但因为程序每次创建线程对象时都需要创建一个FirstThread对象,所以他们不能共享该实例属性,i.

因此就可以有个结论:使用继承Thread类的方法来创建线程类时,多个线程之间无法共享线程类的实例变量.

二.实现Runnable接口创建线程类

实现Runnable接口来创建并启动多线程的步骤如下:
1.定义Runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体.
2.创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象.

示例代码:

package org.credo.thread;

public class FirstRunnable implements Runnable{

	private int i;
	
	public void run(){
		for(;i<10;i++){
			//当线程类实现Runnable接口时,
			//如果想获取当前线程,只能用Thread.currentThread()方法.
			System.out.println(Thread.currentThread().getName()+" "+i);
		}
	}
	
	public static void main(String[] args) {
		for(int i=0;i<10;i++){
			System.out.println(Thread.currentThread().getName()+" "+i);
			if(i==2){
				FirstRunnable fr=new FirstRunnable();
				//通过new Thread(target,name)方法创建新线程
				new Thread(fr,"newThread11").start();
				new Thread(fr, "newThread22").start();
			}
		}
	}
}
输出让我有点额...不太理解,就那两个0....按理说应该是1,2线程都是0~9顺序的.为毛啊?有高手解释没?

main 0
main 1
main 2
main 3
main 4
newThread11 0
newThread22 0
newThread22 2
main 5
main 6
main 7
main 8
main 9
newThread22 3
newThread22 4
newThread11 1
newThread11 6
newThread22 5
newThread11 7
newThread22 8
newThread11 9
按理来说,i应该是连续的,也就是采用runnable接口的方式创建的多个线程可以共享线程类的实例属性.这是因为在这种方式下,程序所创建的Runnable对象只是线程的target,而多个线程可以共享同一个target,所以多个线程可以共享同一个线程类(实际上应该是线程的target类)的实例属性.

三:使用Callable和Future创建线程

前面可以看到,通过实现Runnable接口创建多线程的时候,Thread类的作用就是把run()方法包装成线程执行体.
那么是否可以把任意方法都包装成线程执行体呢?
Java貌似不可以,但C#貌似可以.
因此从java5开始,java提供了Callable接口,该接口其实就是Runnable接口的增强版.Callable接口提供了一个call()方法可以作为线程执行体,但call方法比run()方法功能更强大.

  • call方法可以有返回值.
  • call方法可以声明抛出异常.

因此我们可以提供一个callable对象作为Thread的target.而该线程的线程执行体就是该Callable对象的call方法.但问题来了,callable是java5新增的一个并不是runnable接口的子接口,因此不能直接作为Thread的target,而且call方法还有一个返回值,call方法并不是直接调用.它是作为线程执行体被调用的.那么如果最后获取其值呢?

Java提供了Future接口来代表Callable接口里的call方法的返回值,并为future提供了一个FutureTask实现类.该类实现了Future接口,并且实现了Runnable接口--它可作为Thread类的target.

步骤基本如下:

1.创建Callable接口的实现类,并实现call()方法,该call方法将作为线程执行体.能抛异常,能有返回值.
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值.
3.使用FutureTask对象作为Thread对象的target创建并启动线程.
4.调用FutureTask对象的get()方法获取子线程的返回值.

package org.credo.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class CallableAndFuture implements Callable<Integer>{

	public static void main(String[] args) {
		CallableAndFuture caf=new CallableAndFuture();
		FutureTask<Integer> future=new FutureTask<Integer>(caf);
		for(int i=0;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"的I的值是:"+i);
			if(i==20){
				new Thread(future, "有返回值的线程").start();
			}
		}
		try {
			System.out.println("子线程的返回值:"+future.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public Integer call() throws Exception {
		int i=0;
		for(;i<100;i++){
			System.out.println(Thread.currentThread().getName()+"的I的值为:"+i);
		}
		return i;
	}

}

总结:掌握主线,支线的API查阅也就知道了.一般情况下不去使用继承Thread.太蛋疼了那样.就用Runnable和callable.


© 著作权归作者所有

共有 人打赏支持
Zhao-Qian
粉丝 309
博文 157
码字总数 237336
作品 0
朝阳
高级程序员
Java多线程、并发杂记

多线程涉及的类可以分为以下几类: 可执行对象:最基本的多线程 执行器:简化多线程编程 工具类 容器 并发控制 一、可执行对象: 1、Runnable: 执行单位:Thread 创建线程的两种方式(来自于...

rathan0
2016/02/17
60
0
Thread,Runnable,Callable. 多线程

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有三种方法,Thread,Runnable,Callable.   Runnable和Callable的区别是,   (1)Callable规定的方法是call(...

千惊万喜
2016/06/30
12
0
Callable,Runnable比较及用法以及创建线程的4种方法

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有三种方法,Thread,Runnable,Callable. Runnable和Callable的区别是, (1)Callable规定的方法是call(),Runnable...

geek_loser
2017/10/25
0
0
java多线程实现方法

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。一般有三种方法,Thread,Runnable,Callable. Runnable和Callable的区别是, (1)Callable规定的方法是call(),Runnable...

张欢19933
2016/02/23
31
0
Java多线程基础篇(02)-多线程的实现

1.概要 JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后...

kukudeku
2016/09/19
41
0
Callable与Future

接着上一篇继续并发包的学习,本篇说明的是Callable和Future,它俩很有意思的,一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果...

colorlesswind
2015/12/24
49
0
Callable,Runnable比较及用法

编写多线程程序是为了实现多任务的并发执行,从而能够更好地与用户交互。对目前的java来说,一般有三种方法,Thread,Runnable,Callable. Runnable和Callable的区别是, (1)Callable规定的方法...

again-Y
2013/07/29
0
2
Java-多线程三种实现方式

说明 Java多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程。其中前两种方式线程执行完后都没有返回值,只有最后一...

piz__
2016/11/25
10
0
java.util.concurrent包(4)——Callable和Future

Callable和Future,一个产生结果,一个拿到结果。 Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,...

woshixuye111
2014/06/22
0
0
JAVA多线程实现的三种方式和带返回值实现

1、JAVA多线程实现的四种方式 继承Thread类 实现Runnable接口 实现Callable接口通过FutureTask包装器来创建Thread线程 使用ExecutorService、Callable、Future实现有返回结果的多线程 2、继承...

职业搬砖20年
06/14
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对基于深度神经网络的Auto Encoder用于异常检测的一些思考

一、前言 现实中,大部分数据都是无标签的,人和动物多数情况下都是通过无监督学习获取概念,故而无监督学习拥有广阔的业务场景。举几个场景:网络流量是正常流量还是攻击流量、视频中的人的...

冷血狂魔
15分钟前
0
0
并发设计之A系统调用B系统

A-->B A在发送请求之前,用乐观锁,减少对B的重复调用,这样一定程度上是幂等性。 比如A系统支付功能,要调用B系统进行支付操作,但是前端对"支付"按钮不进行控制,即用户会不断多次点击支付...

汉斯-冯-拉特
35分钟前
0
0
HTTP协议通信原理

了解HTTP HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则。计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务器)请求信息和服务。 HTTP使用...

寰宇01
58分钟前
0
0
【Java动态性】之反射机制

一、Java反射机制简介

谢余峰
58分钟前
1
0
Centos 6.X 部署环境搭建

1.Linux学习笔记CentOS 6.5(一)--CentOS 6.5安装过程

IT追寻者
今天
0
0
博客即同步至腾讯云+社区声明

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=8vy9bsmadbko...

xiaoge2016
今天
1
0
大数据教程(3.1):Linux系统搭建网络YUM源服务器

博主在前面的2.5章节讲述了linux系统本地YUM服务器的搭建和httpd轻量级静态网站服务器的安装,本节博主将为大家分享内网环境中搭建自己的网络YUM服务器的全过程。如果大家对本地YUM服务器还不...

em_aaron
今天
1
0
蚂蚁技术专家:一篇文章带你学习分布式事务

小蚂蚁说: 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在这几年越来越火的微服务架构中,几乎可以说是无法避免,本文就围绕分布式事务...

Java大蜗牛
今天
1
0
新的Steam应用将拓展服务项目

导读 未来几周,Steam将推出两个免费的应用程序Steam Link和Steam Video。这两个应用程序都旨在拓展Steam平台的业务和便利性。 即将开放的Steam Link应用程序最先提供了Android测试版,它将允...

问题终结者
今天
0
0
golang 第三方包的使用总结

golang 第三方包的安装的方法: 1. go get 安装 $ go get github.com/gin-gonic/gin 注意:执行go get 命令需要先安装git命令,并配置git全局变量。 2. 源码包安装 由于国内网络问题,很多时...

科陆李明
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部