`

java多线程初探---创建线程方式及Executor框架

 
阅读更多

JAVA多线程实现方式主要有三种:继承Thread类、实现Runnable接口、使Executor框架(JDK 1.5引入)

 

		// 方式一:继承Thread类,重写run()方法
		Thread thread = new Thread() {
			@Override
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("开始");
			}
		};
		thread.start();
		System.out.println("111");

 

		// 方式二:实现Runnable接口
		Thread thread = new Thread(new Runnable() {
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println("开始");
			}
		});

		thread.start();
		System.out.println("111");

 

输出的结果都是:(先输出111,再输出开始

111
开始

 

传统线程两种实现方式的区别:

可以查看《Java中有两种实现多线程的方式以及两种方式之间的区别文章。

 

实现Runnable接口相对于继承Thread类来说,有如下显著的好处: 

(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。 

(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。 

(3)有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去,这个对象就是一个实现了Runnable接口的类的实例。 

 

 

 

 

下面重点讲解Executor框架

Executor框架是指java 5中引入的一系列并发库中与executor相关的一些功能类,其中包括线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable等。

Executor框架可以实现子线程与主线程的“通信”,能够传递数据。

Executors类,提供了一系列工厂方法用于创先线程池,返回的线程池都实现了ExecutorService接口。

 

直接看代码

		Executor executor = Executors.newFixedThreadPool(10);
		Runnable task = new Runnable() {
			public void run() {
				System.out.println("task executing");
			}
		};
		executor.execute(task);

		System.out.println("111");

 

输出结果:

111
task executing

 

子线程会一直处于运行状态。

 

更换Executor 接口为ExecutorService 接口,可以调用executor.shutdown(); 在子线程运行完毕之后,结束子线程的运行。

 

		ExecutorService executor = Executors.newFixedThreadPool(10);//创建10个线程池
		Runnable task = new Runnable() {   
		public void run() {   
		        System.out.println("task executing");   
		    }   
		};   
		executor.execute(task);   
		  
		System.out.println("111");   
		executor.shutdown();//运行结束,关闭线程池  

ExecutorService 扩充了Executor 接口的方法,比较常见的实现类是ThreadPoolExecutor,详细用法可参考API手册。

 

shutdown() 

启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 

 

 

public static ExecutorService newFixedThreadPool(int nThreads)

创建固定数目线程的线程池。

public static ExecutorService newCachedThreadPool()

创建一个可缓存的线程池,调用execute 将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。

public static ExecutorService newSingleThreadExecutor()

创建一个单线程化的Executor。

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

 

 

		Executor executor = Executors.newFixedThreadPool(10);
		Runnable task = new Runnable() {

			public void run() {
				System.out.println("task executing");
			}

		};
		executor.execute(task);

		ScheduledExecutorService scheduler = (ScheduledExecutorService) Executors
				.newScheduledThreadPool(10);
		scheduler.scheduleAtFixedRate(task, 5, 1, TimeUnit.SECONDS);

 

 输出结果是:

task executing

task executing

task executing

 

程序会先输出 task executing,过5秒之后,再次输出  task executing ,然后,每间隔1秒输出 task executing

 

关于多线程Callable和Future

       从java 5开始,Java提供了Callable接口,该接口是Runnable接口的增强版,Callable接口提供了一个call()方法,可以作为线程执行体,但call()方法比run()方法的功能更强大:有返回值,可以声明抛出异常

      因此我们完全可以提供一个Callable对象作为Thread的target,而该线程的线程执行体就是Callable对象的call()方法。

 

       在工具开发包中,java 5 提供了Future接口来代表Callable接口里的call()方法的返回值,并且为Future接口提供了一个FutureTask实现类,
       该实现类实现了Future接口,并且实现了Runnable接口——可以作为Thread类的target。
       实际上这里的FutureTask类实现了一个桥梁的作用,用于连接Callable对象和Thread对象。
      
       需要注意的是Callable接口有泛型限制,Callable接口里的泛型形参类型,与call()方法的返回值类型相同。


创建并启动有返回值的线程步骤与实现Runnable接口相似:
1.创建Callable接口的实现类,并实现call()方法
2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象
3.使用FutrueTask对象作为Thread对象的target创建并启动新线程。
4.调用FutrueTask对象的get()方法来获得子线程执行结束后的返回值。

 

 

public class ThirdThread implements Callable<Integer> {
	public Integer call() {
		int i = 0;
		for (; i < 30; i++) {
			System.out.println(Thread.currentThread().getName() + "循环变量i的值是 : "
					+ i);
		}
		return i;
	}

	public static void main(String[] args) {

		ThirdThread rt = new ThirdThread(); // 创建Callable对象
		FutureTask<Integer> task = new FutureTask<Integer>(rt);// 使用FutrueTask来包装Callable对象
		for (int i = 0; i < 30; i++) {
			System.out
					.println(Thread.currentThread().getName() + "-------" + i);
			if (i == 4) {
				new Thread(task, "有返回值的线程").start(); // 实际上还是以Callable对象创建并启动线程
			}
		}
		try {
			System.out.println("子线程的返回值 :" + task.get());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

分析:当 i=4 时,主线程与子线程争夺资源 ,子线程名称更改为“有返回值的线程”

子线程 从 0 到 30 开始循环输出,直到i=30退出,将值返回给主线程,

主线程执行 task.get() 方法时,等待子线程计算完成,然后获取其结果。

 

 

输出结果:

main-------0
main-------1
main-------2
main-------3
main-------4
main-------5
main-------6
main-------7
main-------8
main-------9
main-------10
main-------11
main-------12
main-------13
main-------14
main-------15
main-------16
main-------17
有返回值的线程循环变量i的值是 : 0
main-------18
main-------19
main-------20
main-------21
main-------22
有返回值的线程循环变量i的值是 : 1
main-------23
有返回值的线程循环变量i的值是 : 2
main-------24
有返回值的线程循环变量i的值是 : 3
main-------25
有返回值的线程循环变量i的值是 : 4
main-------26
有返回值的线程循环变量i的值是 : 5
main-------27
有返回值的线程循环变量i的值是 : 6
main-------28
有返回值的线程循环变量i的值是 : 7
main-------29
有返回值的线程循环变量i的值是 : 8
有返回值的线程循环变量i的值是 : 9
有返回值的线程循环变量i的值是 : 10
有返回值的线程循环变量i的值是 : 11
有返回值的线程循环变量i的值是 : 12
有返回值的线程循环变量i的值是 : 13
有返回值的线程循环变量i的值是 : 14
有返回值的线程循环变量i的值是 : 15
有返回值的线程循环变量i的值是 : 16
有返回值的线程循环变量i的值是 : 17
有返回值的线程循环变量i的值是 : 18
有返回值的线程循环变量i的值是 : 19
有返回值的线程循环变量i的值是 : 20
有返回值的线程循环变量i的值是 : 21
有返回值的线程循环变量i的值是 : 22
有返回值的线程循环变量i的值是 : 23
有返回值的线程循环变量i的值是 : 24
有返回值的线程循环变量i的值是 : 25
有返回值的线程循环变量i的值是 : 26
有返回值的线程循环变量i的值是 : 27
有返回值的线程循环变量i的值是 : 28
有返回值的线程循环变量i的值是 : 29
子线程的返回值 :30

 

 

class MyCallable implements Callable<Object> {
	private String taskNum;

	MyCallable(String taskNum) {
		this.taskNum = taskNum;
	}

	public Object call() throws Exception {
		System.out.println(">>>" + taskNum + "任务启动");
		Thread.sleep(1000);
		System.out.println(">>>" + taskNum + "任务终止");
		return taskNum + "任务返回运行结果";
	}
}

 

 

public class Test {
	public static void main(String[] args) throws ExecutionException,
			InterruptedException {
		System.out.println("----程序开始运行----");

		int taskSize = 5;
		// 创建一个线程池
		ExecutorService pool = Executors.newFixedThreadPool(taskSize);
		// 创建多个有返回值的任务
		List<Future> list = new ArrayList<Future>();
		for (int i = 0; i < taskSize; i++) {
			Callable c = new MyCallable(i + " ");
			// 执行任务并获取Future对象
			Future f = pool.submit(c);
			list.add(f);
		}
		// 关闭线程池
		pool.shutdown();

		// 获取所有并发任务的运行结果
		for (Future f : list) {
			// 从Future对象上获取任务的返回值,并输出到控制台
			System.out.println(">>>" + f.get().toString());
		}

		System.out.println("----程序结束运行----");
	}
}

 

ExecutoreService提供了submit()方法,传递一个Callable,或Runnable,返回Future。如果Executor后台线程池还没有完成Callable的计算,这调用返回Future对象的get()方法,会阻塞直到计算完成。

 

执行结果:

 ----程序开始运行----
>>>2 任务启动
>>>1 任务启动
>>>3 任务启动
>>>0 任务启动
>>>4 任务启动
>>>2 任务终止
>>>1 任务终止
>>>3 任务终止
>>>0 任务终止
>>>0 任务返回运行结果
>>>1 任务返回运行结果
>>>2 任务返回运行结果
>>>3 任务返回运行结果
>>>4 任务终止
>>>4 任务返回运行结果
----程序结束运行----

 

 

定时执行

class Task implements Callable<String> {

	public String call() throws Exception {
		System.out.println("task executing");
		return "success";
	}
}

 

客户端调用

public static void main(String[] args) throws InterruptedException,
			ExecutionException {

		ScheduledThreadPoolExecutor executor = (ScheduledThreadPoolExecutor) Executors
				.newScheduledThreadPool(10);
		ScheduledFuture<String> schedule = executor.schedule(new Task(), 1,
				TimeUnit.SECONDS);

		String retVal = schedule.get();
		System.out.println(retVal);
		executor.shutdown();
	}

 启动之后,1秒执行,输出结果:

task executing

success

 

在executor.shutdown()方法执行执行的时候,如果还有任务没有执行完成,可以执行

executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

“一盆凉水”将剩余的任务“浇灭”

 

再看下面的例子

class Task implements Callable<String> {
	private String taskNum;

	public Task(String taskNum) {
		this.taskNum = taskNum;
	}

	public Task() {
	}

	public String call() throws Exception {
		System.out.println("task " + taskNum + " executing");
		return "success";
	}
}

 

 返回第一个结果

   

// 创建一个线程池
  ExecutorService pool = Executors.newFixedThreadPool(5);

  List tasks = new ArrayList<Task>();

  tasks.add(new Task("1111"));
  tasks.add(new Task("2222"));

  String result = pool.invokeAny(tasks);
  System.out.println(result.toString());

  // 关闭线程池
  pool.shutdown();

 

 

返回所有结果

// 创建一个线程池
  ExecutorService pool = Executors.newFixedThreadPool(5);

  List tasks = new ArrayList<Task>();

  tasks.add(new Task("1111"));
  tasks.add(new Task("2222"));

  List<Future<MyCallable>> result = pool.invokeAll(tasks);
  
  for (Future<MyCallable> future : result) {
     System.out.println(future.get());
  }

  // 关闭线程池
  pool.shutdown();

 

 

分享到:
评论

相关推荐

    java多线程实现-tcp端口扫描

    Java提供了两种主要的线程创建方式:继承Thread类和实现Runnable接口。在本项目中,可能选择创建一个实现了Runnable接口的类,然后将其实例传递给Thread构造函数,创建新的线程实例。这样做可以避免单继承的限制,...

    Java多线程--等待所有子线程执行完的五种方法.docx

    在Java多线程编程中,有时我们需要确保所有子线程执行完毕后再进行后续操作,例如在并发测试、数据聚合或资源清理等场景。本篇文章将详细介绍五种在Java中等待所有子线程执行完的方法。 ### 方法一:使用`sleep`...

    Java多线程编程实战指南-核心篇

    Executor框架提供了ThreadPoolExecutor,它允许我们预先创建一定数量的线程,管理和控制线程的执行,有效避免了频繁创建和销毁线程的开销。通过调整线程池参数,可以优化系统资源的使用。 异常处理在多线程环境中也...

    java多线程例子-生产者消费者

    在Java编程中,多线程是一种重要的技术,它允许程序同时执行多个任务,从而提高效率和性能。在本示例中,“java多线程例子-生产者消费者”旨在展示如何利用多线程来实现生产者和消费者模式。这种模式是并发编程中的...

    Java多线程-JDK5.0新增线程创建方式

    ### Java多线程-JDK5.0新增线程创建方式 #### 一、新增方式1:实现Callable接口 ##### (1)介绍 自Java 5.0起,为提高线程管理的灵活性与效率,引入了`Callable`接口,这是一种全新的创建线程的方式。与传统的`...

    重新编译的Container-executor 目录文件路径/etc/container-executor.cfg

    hadoop自带的Container-executor在配置yarn-kerberos时存在问题,这边给出编译后的Container-executor,默认加载配置文件路径/etc/container-executor.cfg,大家不用再重新编译了

    java 多线程编程实战指南(核心 + 设计模式 完整版)

    - **线程的创建**:Java提供了两种创建线程的方式,一是通过实现`Runnable`接口,二是继承`Thread`类。每种方式都有其适用场景,需要根据实际需求选择。 - **生命周期**:线程有新建、就绪、运行、阻塞和终止五种...

    汪文君JAVA多线程编程实战(完整不加密)

    读者将学习如何通过实现Runnable接口或继承Thread类来创建线程,以及如何使用Executor框架来管理线程池。 此外,书中还深入探讨了线程安全问题,包括共享资源的并发访问、死锁、活锁、饥饿等问题,以及如何通过同步...

    Java多线程实战精讲-带你一次搞明白Java多线程高并发

    - **Executor框架**:Java提供的线程池服务,可减少线程创建和销毁的开销。 - **ThreadPoolExecutor**:最常见的线程池实现,参数包括核心线程数、最大线程数、队列容量等。 - **ScheduledExecutorService**:...

    java多线程

    由于提供的文件内容大部分与Java多线程编程核心技术并无直接关联,而是关于电子书资源的联系方式和说明,因此不能直接从这部分内容中生成关于Java多线程的知识点。但考虑到描述中提到了电子书的标题以及它涉及的主题...

    Java多线程编程核心技术_完整版_java_

    Java多线程编程是Java开发中的重要组成部分,它允许程序同时执行多个任务,极大地提高了程序的效率和响应性。在Java中,多线程主要通过继承Thread类或实现Runnable接口来实现。本教程《Java多线程编程核心技术》将...

    java多线程处理数据库数据

    在Java编程中,多线程处理是提升程序性能和效率的重要手段,特别是在处理大量数据库数据时。本主题将深入探讨如何使用Java的并发包(java.util.concurrent)来实现多线程对数据库数据的批量处理,包括增、删、改等...

    java多线程示例

    - 使用ExecutorService和Future:Java 5引入的Executor框架提供了一种更灵活的多线程管理方式。 二、线程控制 1. 同步机制:为了避免多线程间的竞态条件,Java提供了synchronized关键字。它可以用于方法或代码块,...

    xxl-job-executor-go-master

    XXL-JOB是一个分布式任务调度平台,而"xxl-job-executor-go-master"则表示这个项目是XXL-JOB的Go语言实现版本的源码仓库。这个仓库是针对XXL-JOB执行器的一个Go语言实现,它使得开发者可以利用Go语言来编写调度任务...

    Java多线程之Executor框架.docx

    Java的Executor框架是Java 1.5引入的用于管理和控制多线程的一种机制,它旨在解决直接使用`new Thread(…).start()`方法创建线程所带来的问题。在传统的多线程编程中,直接创建和销毁线程会导致大量开销,且无法有效...

    Java多线程详解及示例

    Java提供了ExecutorService和ThreadPoolExecutor类来管理线程,创建线程池可以有效控制并发线程的数量,减少线程的创建和销毁开销。线程池的基本使用如下: ```java ExecutorService executor = Executors....

    详细剖析JAVA多线程案例教学

    Java 5引入了`Executor`框架,它提供了一种更加灵活的方式来管理和控制线程池,从而有效地管理线程生命周期。 - **示例代码**: ```java ExecutorService executor = Executors.newFixedThreadPool(10); // 创建...

    Java多线程编程核心技术.zip

    - Executor框架:Java 5引入,用于管理和控制线程池,提高系统性能,避免频繁创建和销毁线程。 - ThreadPoolExecutor:线程池的核心实现,可以通过设置核心线程数、最大线程数、线程存活时间等参数定制线程池行为...

    Java的多线程(java基础)

    此外,Java还提供了Callable和Future接口,以及Executor框架,用于创建带返回值的线程和管理线程池,这在处理复杂并发场景时非常有用。 在实际编程中,多线程可能会遇到竞态条件、死锁等问题,需要通过适当的同步...

Global site tag (gtag.js) - Google Analytics