你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。
1、继承Runnable,把需要多线程运行的程序放到 public void run() 方法里,然后在main方法中 new 一个thread,并在实例化的时候调用start(); 例如如下代码:
在这里插
public class LiftOff implements Runnable {
protected int countDown = 10; // Default
private static int taskCount = 0;
private final int id = taskCount++; //区分任务的多个实例
public LiftOff() {}
public LiftOff(int countDown) {
this.countDown = countDown;
}
public String status() {
return "#" + id + "(" +
(countDown > 0 ? countDown : "Liftoff!") + "), ";
}
public void run() {
while(countDown-- > 0) {
System.out.print(status());
//是对线程调度器的一种建议,"我已经执行完生命周期中最重要的部分了,此刻正是切换给其他任务执行一段时间的大好时机。"
Thread.yield();
}
}
} ///:~
import com.chot.concurrency.LiftOff;
//当main()创建thread对象时,它并没有捕获任何对这些对象的引用。在使用普通对象时,这对与垃圾回收来说是一场公平的游戏,但是在使用thread时,情况就不同了。
//每个thread 都“注册”了它自己,因此确实有一个对它的引用,而且在它的任务退出其run() 并死亡之前,垃圾回收器无法清除它。
// (为何GC 无法将其清除呢? 答:因为有栈帧对其有引用,即有一个指针只想堆中那个由thread创建的object。故无法标记其可清除。)
public class MoreBasicThreads {
public static void main(String[] args) {
for(int i = 0; i < 5; i++)
new Thread(new LiftOff()).start();
System.out.println("Waiting for LiftOff");
}
}
入代码片
2、使用Executor
Executor在客户端和任务执行之间提供一个简介层,像是一个中介一样,任务就是在这些中介这里执行的,Executor允许你管理异步任务的执行,不需要显式的管理线程的生命周期。目前项目在使用的就是此种方式。
使用Executor有两种方式
ExecutorService exec = Executors.newCachedThreadPool();
ExecutorService exec = Executors.newFixedThreadPool(5);
两种方式孰优孰劣?
fixed 一次性预先执行代价高昂的线程分配。因而可以控制线程的数量。newCachedThreadPool在程序执行的过程中通常会创建与所需数量相同的线程,然后在它回收旧线程时停止创建新线程。
拿我接触到的项目来说,线程的创总和某些事情会关联起来,即会有时间来驱动来创建线程,所以使用的newCachedThreadPool。
下面的代码是用Executor来替换moreBasicThreads.java
1、cachedThreadPoll
package com.chot.concurrency.Chap21_2_3;
import com.chot.concurrency.LiftOff;
import java.util.concurrent.*;
// 将morebasicThreads 改造成使用cachedThreadPool
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService exec = Executors.newCachedThreadPool();
for(int i = 0; i < 5; i++)
exec.execute(new LiftOff());
exec.shutdown();
}
}
2、指定5个线程数
package com.chot.concurrency.Chap21_2_3;
import com.chot.concurrency.LiftOff;
import java.util.concurrent.*;
// 将morebasicThreads 改造成使用cachedThreadPool
public class CachedThreadPool {
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(5);
for(int i = 0; i < 5; i++)
exec.execute(new LiftOff());
exec.shutdown();
}
}
还有一种 SingleThreadExecutor。这就像是线程数量为1的FixedThreadPool。
ExecutorService exec = Executors.newSingleThreadExecutor();
个人认为在debug的时候使用此种方式将使你的focus不会很乱。如果向SingleThreadExecutor提交了多个任务,那么这些任务将排队,每一个任务都会在下一个任务开始之前运行结束。所有的任务将使用相同的线程。
#0(9), #0(8), #0(7), #0(6), #0(5), #0(4), #0(3), #0(2), #0(1),#0(Liftoff!)
#1(9), #1(8), #1(7), #1(6), #1(5), #1(4), #1(3), #1(2), #1(1), #1(Liftoff!)
#2(9), #2(8), #2(7), #2(6), #2(5), #2(4), #2(3), #2(2), #2(1), #2(Liftoff!)
#3(9), #3(8), #3(7), #3(6), #3(5), #3(4), #3(3), #3(2), #3(1), #3(Liftoff!)
#4(9), #4(8), #4(7), #4(6), #4(5), #4(4), #4(3), #4(2), #4(1), #4(Liftoff!)
Runnable是执行工作的独立任务,但是它不返回任何值。如果希望任务在完成时能够返回一个值,那么可以实现Callable接口而不是Runnable接口。
Callable是一个具有类型参数的泛型,他的类型参数表示的是从call()方法 <而不是run()> 方法中返回的值。并且必须使用ExecutorService.submit()**来调用。