本文目录一览:
java 多线程
所要执行的指令,也包括了执行指令所需的系统资源,不同进程所占用的系统资源相对独立。所以进程是重量级的任务,它们之间的通信和转换都需要操作系统付出较大的开销。 线程是进程中的一个实体,是被系统独立调度和分派的基本单位。线程自己基本上不拥有系统资源,但它可以与同属一个进程的其他线程共享进程所拥有的全部资源。所以线程是轻量级的任务,它们之间的通信和转换只需要较小的系统开销。 Java支持多线程编程,因此用Java编写的应用程序可以同时执行多个任务。Java的多线程机制使用起来非常方便,用户只需关注程序细节的实现,而不用担心后台的多任务系统。 Java语言里,线程表现为线程类。Thread线程类封装了所有需要的线程操作控制。在设计程序时,必须很清晰地区分开线程对象和运行线程,可以将线程对象看作是运行线程的控制面板。在线程对象里有很多方法来控制一个线程是否运行、睡眠、挂起或停止。线程类是控制线程行为的唯一的手段。一旦一个Java程序启动后,就已经有一个线程在运行。可通过调用Thread.currentThread方法来查看当前运行的是哪一个线程。
class ThreadTest{
public static void main(String args[]){
Thread t = Thread.currentThread();
t.setName("单线程"); //对线程取名为"单线程"
t.setPriority(8);
//设置线程优先级为8,最高为10,最低为1,默认为5
System.out.println("The running thread: " + t);
// 显示线程信息
try{
for(int i=0;i<3;i++){
System.out.println("Sleep time " + i);
Thread.sleep(100); // 睡眠100毫秒
}
}catch(InterruptedException e){// 捕获异常
System.out.println("thread has wrong");
}
}
}
多线程的实现方法
继承Thread类
可通过继承Thread类并重写其中的run()方法来定义线程体以实现线程的具体行为,然后创建该子类的对象以创建线程。 在继承Thread类的子类ThreadSubclassName中重写run()方法来定义线程体的一般格式为:
public class ThreadSubclassName extends Thread{
public ThreadSubclassName(){
// 编写子类的构造方法,可缺省
}
public void run(){
// 编写自己的线程代码
}
}
用定义的线程子类ThreadSubclassName创建线程对象的一般格式为:
ThreadSubclassName ThreadObject = new ThreadSubclassName();
然后,就可启动该线程对象表示的线程:
ThreadObject.start(); //启动线程
应用继承类Thread的方法实现多线程的程序。本程序创建了三个单独的线程,它们分别打印自己的“Hello World!”。
class ThreadDemo extends Thread{
private String whoami;
private int delay;
public ThreadDemo(String s,int d){
whoami=s;
delay=d;
}
public void run(){
try{
sleep(delay);
}catch(InterruptedException e){
System.out.println("Hello World!" + whoami + " " + delay);
}
}
}
public class MultiThread{
public static void main(String args[]){
ThreadDemo t1,t2,t3;
t1 = new ThreadDemo("Thread1", (int)(Math.random()*2000));
t2 = new ThreadDemo("Thread2", (int)(Math.random()*2000));
t3 = new ThreadDemo("Thread3", (int)(Math.random()*2000));
t1.start();
t2.start();
t3.start();
}
}
实现Runnable接口
编写多线程程序的另一种的方法是实现Runnable接口。在一个类中实现Runnable接口(以后称实现Runnable接口的类为Runnable类),并在该类中定义run()方法,然后用带有Runnable参数的Thread类构造方法创建线程。 创建线程对象可用下面的两个步骤来完成:
- 生成Runnable类ClassName的对象
ClassName RunnableObject = new ClassName();
- 用带有Runnable参数的Thread类构造方法创建线程对象。新创建的线程的指针将指向Runnable类的实例。用该Runnable类的实例为线程提供 run()方法---线程体。
Thread ThreadObject = new Thread(RunnableObject);
然后,就可启动线程对象ThreadObject表示的线程:
ThreadObject.start();
在Thread类中带有Runnable接口的构造方法有:
public Thread(Runnable target);
public Thread(Runnable target, String name);
public Thread(String name);
public Thread(ThreadGroup group, Runnable target);
public Thread(ThreadGroup group, Runnable target, String name);
其中,参数Runnable target表示该线程执行时运行target的run()方法,String name以指定名字构造线程,ThreadGroup group表示创建线程组。 用Runnable接口实现的多线程。
class TwoThread implements Runnable{
TwoThread(){
Thread t1 = Thread.currentThread();
t1.setName("第一主线程");
System.out.println("正在运行的线程: " + t1);
Thread t2 = new Thread(this,"第二线程");
System.out.println("创建第二线程");
t2.start();
try{
System.out.println("第一线程休眠");
Thread.sleep(3000);
}catch(InterruptedException e){
System.out.println("第一线程有错");
}
System.out.println("第一线程退出");
}
public void run(){
try{
for(int i = 0; i < 5; i++){
System.out.println("第二线程的休眠时间:" + i);
Thread.sleep(1000);
}
}catch(InterruptedException e){
System.out.println("线程有错");
}
System.out.println("第二线程退出");
}
public static void main(String args[]){
new TwoThread();
}
}
程序运行结果如下:
正在运行的线程: Thread[第一主线程,5,main]
创建第二线程
第一线程休眠
第二线程的休眠时间:0
第二线程的休眠时间:1
第二线程的休眠时间:2
第一线程退出
第二线程的休眠时间:3
第二线程的休眠时间:4
第二线程退出
如何使用Java编写多线程程序(1)
一、简介
- 什么是线程 要说线程,就必须先说说进程,进程就是程序的运行时的一个实例。线程呢可以看作单独地占有CPU时间来执行相应的代码的。对早期的计算机(如DOS)而言,线程既是进程,进程既是进程,因为她是单线程的。当然一个程序可以是多线程的,多线程的各个线程看上去像是并行地独自完成各自的工作,就像一台一台计算机上运行着多个处理机一样。在多处理机计算机上实现多线程时,它们确实可以并行工作,而且采用适当的分时策略可以大大提高程序运行的效率。但是二者还是有较大的不同的,线程是共享地址空间的,也就是说多线程可以同时读取相同的地址空间,并且利用这个空间进行交换数据。
- 为什么要使用线程 学过《计算机体系结构》的人都知道。将顺序执行程序和采用多线程并行执行程序相比,效率是可以大大地提高的。比如,有五个线程thread1, thread2, thread3, thread4, thread5,所耗的CPU时间分别为4,5,1,2,7。(假设CPU轮换周期为4个CPU时间,而且线程之间是彼此独立的)顺序执行需要花费1Array个CPU时间,而并行需要的时间肯定少于1Array个CPU时间,至于具体多少时间要看那些线程是可以同时执行的。这是在非常小规模的情况下,要是面对大规模的进程之间的交互的话,效率可以表现得更高。
- java中是如何实现多线程的 与其他语言不一样的是,线程的观念在java是语言中是重要的,根深蒂固的,因为在java语言中的线程系统是java语言自建的, java中有专门的支持多线程的API库,所以你可以以最快的速度写一个支持线程的程序。在使用java创建线程的时候,你可以生成一个Thread类或者他的子类对象,并给这个对象发送start()消息(程序可以向任何一个派生自 Runnable 接口的类对象发送 start() 消息的),这样一来程序会一直执行,直到run返回为止,此时该线程就死掉了。在java语言中,线程有如下特点:
- 在一个程序中而言,主线程的执行位置就是main。而其他线程执行的位置,程序员是可以自定义的。值得注意的是对Applet也是一样。
- 每个线程执行其代码的方式都是一次顺序执行的。
- 一个线程执行其代码是与其他线程独立开来的。如果诸线程之间又相互协作的话,就必须采用一定的交互机制。
- 前面已经说过,线程是共享地址空间的,如果控制不当,这里很有可能出现死锁。 各线程之间是相互独立的,那么本地变量对一个线程而言就是完全独立,私有的。所以呢,线程执行时,每个线程都有各自的本地变量拷贝。对象变量(instance variable)在线程之间是可以共享的,这也就是为什么在java中共享数据对象是如此的好用,但是java线程不能够武断地访问对象变量:他们是需要访问数据对象的权限的。
二、准备知识
在分析这个例子之前,然我们先看看关于线程的几个概念,上锁,信号量,和java所提供的API。
上锁
对于大多数的程序而言,他们都需要线程之间相互的通讯来完成整个线程的生命周期,二实现线程之间同步的最简单的办法就是上锁。为了防止相互关联的两个线程之间错误地访问共享资源,线程需要在访问资源的时候上锁和解锁,对于锁而言,有读锁,写锁和读写锁等不同的同步策略。在java中,所有的对象都有锁;线程只需要使用synchronized关键字就可以获得锁。在任一时刻对于给定的类的实例,方法或同步的代码块只能被一个线程执行。这是因为代码在执行之前要求获得对象的锁。
信号量
通常情况下,多个线程所访问为数不多的资源,那怎么控制呢?一个比较非常经典而起非常简单的办法就是采用信号量机制。信号量机制的含义就是定义一个信号量,也就是说能够提供的连接数;当有一个线程占用了一个连接时,信号量就减一;当一个线程是放了连接时,信号量就加一。
java 如何实现多线程
java中多线程的实现方式有两种,一种是继承java.lang.Thread类,另一种是实现java.lang.Runnable接口。下面是两种方式的简单代码。
继承Thread类方式:
import java.lang.Thread;
//用集成Thread类方式实现多线程。
public class Test{
public static void main(String arg[]){
T t1=new T();
T t2=new T();
//更改新线程名称
t1.setName("t1");
t2.setName("t2");
//启动线程
t1.start();
t2.start();
}
}
class T extends Thread{
//重写run()方法
public void run(){
System.out.println(this.getName());
}
}
输出结果为:
t1
t2
实现Runnable接口方式:
在使用Runnable接口时需要建立一个Thread实例。因此,无论是通过Thread类还是Runnable接口建立线程,都必须建立Thread类或它的子类的实例。
import java.lang.*;
//用实现Runnable接口的方式实现多线程。
public class Test{
public static void main(String arg[]){
T t1=new T();
T t2=new T();
//一定要实例化Thread对象,将实现Runnable接口的对象作为参数传入。
Thread th1=new Thread(t1,"t1");
Thread th2=new Thread(t2,"t2");
//启动线程
th1.start();
th2.start();
}
}
class T implements Runnable{
//重写run()方法
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
输出结果为:
t1
t2
public void run()
方法是JAVA中线程的执行体方法,所有线程的操作都是从run方法开始,有点类似于main()方法,即主线程。
java多核多线程
java可以实现多线程. 但是不能判断某个线程再哪个CPU上执行. java实现多线程有两种方法。
一种是继承Thread类
class MyThread extends Thread{
@Override
public void run(){
//要执行的代码
}
}
调用时,使用new MyThread().start()
就可以了
另一种方法是实现Runnable接口
class MyThread implements Runnable{
@Override
public void run(){
//要执行的代码
}
}
调用方法:
new Thread(new MyThread()).start()
多线程回顾笔记总结
三种创建方式:
练习Thread,实现多线程同步下载图片
public class TestThread1 extends Thread {
private String name; // 保存文件名
// 创建一个构造器
public TestThread1(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) {
TestThread1 testThread1 = new TestThread1("url1","1.jpg");
TestThread1 testThread2 = new TestThread1("url2","2.jpg");
TestThread1 testThread3 = new TestThread1("url3","3.jpg");
testThread1.start();
testThread2.start();
testThread3.start();
/* 结果,说明线程不是按代码先后顺序执行的,是同时执行的
下载了文件,名为:3.jpg
下载了文件,名为:1.jpg
下载了文件,名为:2.jpg
*/
}
// 下载图片的线程执行体
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载了文件,名为:"+name);
}
}
// 下载器
class WebDownLoader {
// 下载方法
public void downLoader(String url,String name) {
try {
// copyURLToFile(),这个方法就是把网上的一个url变成一个文件
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出错");
}
}
}
创建线程方式2:实现Runnable接口,重写run()方法,执行线程需要丢入Runnable接口实现类,调用start()方法
public class TestThread2 implements Runnable {
public static void main(String[] args) {
// 创建Runnable 接口实现类对象
TestThread2 testThread2 = new TestThread2();
// 创建线程对象,通过线程对象来开启我们的线程,代理
Thread thread = new Thread(testThread2);
thread.start();
//new Thread(testThread2).start(); // 简写方法
for (int i = 0; i < 2000; i++) {
System.out.println("我们在学习多线程---"+i);
}
}
@Override
public void run() {
for (int i = 0; i < 2000; i++) {
System.out.println("我们在玩 游戏 啦---"+i);
}
}
}
发现问题:多个线程操作同一个资源情况下,线程不安全,数据紊乱,出现重复数据
模拟龟兔赛跑
public class Race implements Runnable {
// 胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
// 模拟比赛暂停
if(Thread.currentThread().getName().equals("兔子") && i == 90){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 判断比赛是否结束
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName() + "跑了" + i + "步");
}
}
// 判断是否完成比赛
private boolean gameOver(int steps){
if(winner != null){
// 已经有胜利者了
return true;
}
if(steps == 100){
winner = Thread.currentThread().getName();
System.out.println("winner is " + winner);
return true;
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
}
}
关闭服务:
executorService.shutdownNow();
线程创建方式三:实现Callable接口
import java.util.concurrent.*;
public class TestCallable implements Callable<Boolean> {
private String name; // 保存文件名
// 创建一个构造器
public TestCallable(String url, String name) {
this.url = url;
this.name = name;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable testThread1 = new TestCallable("url1","1.jpg");
TestCallable testThread2 = new TestCallable("url2","2.jpg");
TestCallable testThread3 = new TestCallable("url3","3.jpg");
// 创建执行服务
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> submit1 = executorService.submit(testThread1);
Future<Boolean> submit2 = executorService.submit(testThread2);
Future<Boolean> submit3 = executorService.submit(testThread3);
// 获取结果
Boolean b1 = submit1.get();
Boolean b2 = submit2.get();
Boolean b3 = submit3.get();
// 关闭服务
executorService.shutdownNow();
System.out.println(b1);
System.out.println(b2);
System.out.println(b3);
}
// 下载图片的线程执行体
@Override
public Boolean call() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载了文件,名为:"+name);
return true;
}
}
// 下载器
class WebDownLoader {
// 下载方法
public void downLoader(String url,String name) {
try {
// copyURLToFile(),这个方法就是把网上的一个url变成一个文件
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出错");
}
}
}
静态代理模式总结:
- 真实对象和代理对象都要实现同一个接口
- 代理对象要代理真实角色
代理模式的好处:
- 代理对象可以做很多真实对象做不了的事情
- 真实对象专注做自己的事情就可以了,其他的事情交给代理公司来做
五大状态:
测试Stop
- 建议线程正常停止,利用次数,不建议死循环
- 建议使用标志位,设置一个标志位
- 不要使用stop(),destroy()方法,已过时
public class StopTest implements Runnable {
// 1 设置一个标志位
private boolean flag = true;
public static void main(String[] args) {
StopTest stopTest = new StopTest();
// 开启线程
new Thread(stopTest).start();
for (int i = 0; i < 1000; i++) {
System.out.println("main Thread is running..."+i);
if (i == 900) {
// 调用stop方法切换标志位停止线程
stopTest.stop();
System.out.println("Thread is stopped...");
}
}
}
// 设置一个公开的方法停止线程,转换标志位
public void stop() {
this.flag = false;
}
@Override
public void run() {
int i = 0;
while (flag) {
System.out.println("Thread is running..." + i++);
}
}
}
yield
jion
daemon
化妆的方法,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) { // 获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror) { // 一秒后想获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
}
} else {
synchronized (mirror) { // 获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick) { // 一秒后想获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
这样synchronized
块包裹着,就会导致程序卡死,只要不包裹着,就可以正常运行,如下:
死锁,多个线程互相抱着对方需要的资源,然后形成僵持
public class DeadLock {
public static void main(String[] args) {
Makeup bestGirl = new Makeup(0, "婕儿");
Makeup betterGirl = new Makeup(1, "珂儿");
bestGirl.start();
betterGirl.start();
}
}
// 口红
class Lipstick {
}
// 镜子
class Mirror {
}
// 化妆
class Makeup extends Thread {
// 需要的资源只有一份,用static来保证只有一份
static Mirror mirror = new Mirror();
static Lipstick lipstick = new Lipstick();
int choice; // 选择
String girlName; // 使用化妆品的人
public Makeup(int choice, String girlName) {
this.choice = choice;
this.girlName = girlName;
}
@Override
public void run() {
// 化妆
try {
makeup();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 化妆的方法,互相持有对方的锁,就是需要拿到对方的资源
private void makeup() throws InterruptedException {
if (choice == 0) {
synchronized (lipstick) { // 获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
}
synchronized (mirror) { // 一秒后想获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
}
} else {
synchronized (mirror) { // 获得镜子的锁
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
}
synchronized (lipstick) { // 一秒后想获得口红的锁
System.out.println(this.girlName+"获得口红的锁");
}
}
}
}
上面列出了死锁的四个条件,我们只要想办法破其中任意一个,或多个条件就可以避免死锁发生
- 这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件.
- Java提供了几个方法解决线程之间的通信问题
- 解决方式1 并发协作模型“生产者/消费者模式” —管程法 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
- 解决方式2
测试线程池
public class PoolTest {
public static void main(String[] args) {
// 创建服务,创建池子
// newFixedThreadPool,参数为:线程池池子大小
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
service.execute(new MyThread());
// 2.关闭连接
service.shutdown();
}
}
class MyThread implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
在Java 中多线程的实现方法有哪些,如何使用
Java多线程的创建及启动
Java中线程的创建常见有如三种基本形式
- 继承Thread类,重写该类的run()方法。
class MyThread extends Thread {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread myThread1 = new MyThread(); // 创建一个新的线程 myThread1 此线程进入新建状态
Thread myThread2 = new MyThread(); // 创建一个新的线程 myThread2 此线程进入新建状态
myThread1.start(); // 调用start()方法使得线程进入就绪状态
myThread2.start(); // 调用start()方法使得线程进入就绪状态
}
}
}
}
如上所示,继承Thread类,通过重写run()方法定义了一个新的线程类MyThread,其中run()方法的方法体代表了线程需要完成的任务,称之为线程执行体。当创建此线程类对象时一个新的线程得以创建,并进入到线程新建状态。通过调用线程对象引用的start()方法,使得该线程进入到就绪状态,此时此线程并不一定会马上得以执行,这取决于CPU调度时机。 2. 实现Runnable接口,并重写该接口的run()方法,该run()方法同样是线程执行体,创建Runnable实现类的实例,并以此实例作为Thread类的target来创建Thread对象,该Thread对象才是真正的线程对象。
class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Runnable myRunnable = new MyRunnable(); // 创建一个Runnable实现类的对象
Thread thread1 = new Thread(myRunnable); // 将myRunnable作为Thread target创建新的线程
Thread thread2 = new Thread(myRunnable);
thread1.start(); // 调用start()方法使得线程进入就绪状态
thread2.start();
}
}
}
}
相信以上两种创建新线程的方式大家都很熟悉了,那么Thread和Runnable之间到底是什么关系呢?我们首先来看一下下面这个例子。
public class ThreadTest {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Runnable myRunnable = new MyRunnable();
Thread thread = new MyThread(myRunnable);
thread.start();
}
}
}
}
class MyRunnable implements Runnable {
private int i = 0;
@Override
public void run() {
System.out.println("in MyRunnable run");
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
class MyThread extends Thread {
private int i = 0;
public MyThread(Runnable runnable){
super(runnable);
}
@Override
public void run() {
System.out.println("in MyThread run");
for (i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
同样的,与实现Runnable接口创建线程方式相似,不同的地方在于
Thread thread = new MyThread(myRunnable);
那么这种方式可以顺利创建出一个新的线程么?答案是肯定的。至于此时的线程执行体到底是MyRunnable接口中的run()方法还是MyThread类中的run()方法呢?通过输出我们知道线程执行体是MyThread类中的run()方法。其实原因很简单,因为Thread类本身也是实现了Runnable接口,而run()方法最先是在Runnable接口中定义的方法。
public interface Runnable {
public abstract void run();
}
我们看一下Thread类中对Runnable接口中run()方法的实现:
@Override
public void run() {
if (target != null) {
target.run();
}
}
也就是说,当执行到Thread类中的run()方法时,会首先判断target是否存在,存在则执行target中的run()方法,也就是实现了Runnable接口并重写了run()方法的类中的run()方法。但是上述给到的列子中,由于多态的存在,根本就没有执行到Thread类中的run()方法,而是直接先执行了运行时类型即MyThread类中的run()方法。 3. 使用Callable和Future接口创建线程。具体是创建Callable接口的实现类,并实现call()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。 看着好像有点复杂,直接来看一个例子就清晰了。
public class ThreadTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable(); // 创建MyCallable对象
FutureTask<Integer> ft = new FutureTask<Integer>(myCallable); // 使用FutureTask来包装MyCallable对象
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
if (i == 30) {
Thread thread = new Thread(ft); // FutureTask对象作为Thread对象的target创建新的线程
thread.start(); // 线程进入到就绪状态
}
}
System.out.println("主线程for循环执行完毕..");
try {
int sum = ft.get(); // 取得新创建的新线程中的call()方法返回的结果
System.out.println("sum = " + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
private int i = 0;
// 与run()方法不同的是,call()方法具有返回值
@Override
public Integer call() {
int sum = 0;
for (; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
首先,我们发现,在实现Callable接口中,此时不再是run()方法了,而是call()方法,此call()方法作为线程执行体,同时还具有返回值!在创建新的线程时,是通过FutureTask来包装MyCallable对象,同时作为了Thread对象的target。那么看下FutureTask类的定义:
public class FutureTask<V> implements RunnableFuture<V> {
//....
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
于是,我们发现FutureTask类实际上是同时实现了Runnable和Future接口,由此才使得其具有Future和Runnable双重特性。通过Runnable特性,可以作为Thread对象的target,而Future特性,使得其可以取得新创建线程中的call()方法的返回值。 执行下此程序,我们发现sum = 4950永远都是最后输出的。而“主线程for循环执行完毕..”则很可能是在子线程循环中间输出。由CPU的线程调度机制,我们知道,“主线程for循环执行完毕..”的输出时机是没有任何问题的,那么为什么sum =4950会永远最后输出呢? 原因在于通过ft.get()方法获取子线程call()方法的返回值时,当子线程此方法还未执行完毕,ft.get()方法会一直阻塞,直到call()方法执行完毕才能取到返回值。 上述主要讲解了三种常见的线程创建方式,对于线程的启动而言,都是调用线程对象的start()方法,需要特别注意的是:不能对同一线程对象两次调用start()方法。 你好,本题已解答,如果满意请点右下角“采纳答案”。