实现Callable接口对比实现Runnable接口的优点:
Future接口:
说明:
说明:
3. 可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
4. FutrueTask是Futrue接口的唯一的实现类
5. FutureTask 同时实现了Runnable, Future接口。它既可以作为 Runnable被线程执行,又可以作为Future得到Callable的返回值
FutureTask类
说明:
可以取消的异步计算
一些方法:
代码示例,求10,8,5的阶乘:
class CalFaci implements Callable<Integer>{
int num;
public CalFaci(int num) {
super();
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 1;
for(int i=1;i <= num;i++){
sum *= i;
}
return sum;
}
}
public class CallableDemo1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
FutureTask<Integer> ft1 = new FutureTask<Integer>(new CalFaci(10));
FutureTask<Integer> ft2 = new FutureTask<Integer>(new CalFaci(8));
FutureTask<Integer> ft3 = new FutureTask<Integer>(new CalFaci(5));
new Thread(ft1).start();
new Thread(ft2).start();
new Thread(ft3).start();
System.out.println(ft1.get());
System.out.println(ft2.get());
System.out.println(ft3.get());
}
}
求从键盘输入的三个数中的最大值:
class MaxNumber implements Callable<Integer>{
int num1;
int num2;
int num3;
public MaxNumber(int num1, int num2, int num3) {
super();
this.num1 = num1;
this.num2 = num2;
this.num3 = num3;
}
@Override
public Integer call() throws Exception {
return (num1 > num2 ? (num1 > num3 ? num1 : num3) : (num2 > num3 ? num2 : num3));
}
}
public class CallableDemo2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Scanner sc = new Scanner(System.in);
System.out.println("请输入三个数字,以回车键分隔");
FutureTask<Integer> ft = new FutureTask<Integer>(new MaxNumber(sc.nextInt(),sc.nextInt(),sc.nextInt()));
new Thread(ft).start();
System.out.println(ft.get());
}
}
说明:
提前创建好多个线程,放入线程池中,使用时直接获取,使用完 放回池中。可以避免频繁创建销毁、实现重复利用。类似生活中的公共交 通工具。
优点:
- 提高响应速度(减少了创建新线程的时间)
- 降低资源消耗(重复利用线程池中线程,不需要每次都创建)
- 便于线程管理
- corePoolSize:核心池的大小
- maximumPoolSize:最大线程数
- keepAliveTime:线程没任务时最多保持多长时间后会终止
ExecutorService接口:
ExecutorService service = Executors.newFixedThreadPool(int nThreads)
submit(Runnable task) 提交一个可运行的任务执行,并返回一个表示该任务的Future
isShutdown() 如果这个执行者已被关闭,则返回 true
isTerminated() 如果所有任务在关闭后完成,则返回 true
shutdown() 启动有序关闭,其中先前提交的任务将被执行,但不会接受任何新任务
shutdownNow() 尝试停止所有主动执行的任务,停止等待任务的处理,并返回正在等待执行的任务列表
代码示例:
计算1-100的整数之和,计算1-200的整数之和:
class Add implements Runnable {
int num;
public Add(int num) {
super();
this.num = num;
}
@Override
public void run() {
int sum = 0;
while (true) {
if (num > 0) {
sum += num--;
} else {
System.out.println(sum);
break;
}
}
}
}
public class PoolDemo1 {
public static void main(String[] args) {
//初始化线程池,线程容量为2
ExecutorService service = Executors.newFixedThreadPool(2);
Add a1 = new Add(100);
Add a2 = new Add(200);
service.submit(a1);
service.submit(a2);
}
}
这个类提供线程局部变量。 这些变量与其正常的对应方式不同,因为访问一个的每个线程(通过其get或set方法)都有自己的独立初始化的变量副本
方法:
代码示例:
public class ThreadLocalDemo {
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<String>();
new Thread() {
@Override
public void run() {
//设置值,该值只在当前线程有用
local.set("你这瓜保熟吗?");
try {
//稍微睡一下,不急
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在当前线程访问local的值
String str = local.get();
//当前线程可以访问哇
System.out.println(str);
}
}.start();
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
String str;
//判断在main函数是否访问得到
if (local.get() == null) {
//Main访问不到哇
str = "我还能卖你生瓜蛋子?";
}else {
str = "你故意找茬是吧?";
}
System.out.println(Thread.currentThread().getName() + "访问:" + str);
}
}
class Singleton{
//延时加载,懒汉式,线程不安全
private static Singleton singleton = null;
private Singleton(){
}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}
//线程类
class T extends Thread{
static Map m = new HashMap();
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
//线程内部获取实例化对象
Singleton s = Singleton.getInstance();
m.put(s, "a:" + i);
}
System.out.println(m);
}
}
public class SingletonNoSafe {
public static void main(String[] args) {
for(int i=0;i < 100;i++){
//不断启动线程去获取单例对象
T t = new T();
t.start();
}
}
}
这种写法是有可能获取两个不相同的对象的,就不符合单例模式的原则了
class Singleton2 {
// 延迟加载,懒汉模式.线程安全
private static Singleton2 singleton = null;
private Singleton2() {
}
//二重判断确保对象是单例的
public static Singleton2 getInstance() {
if (singleton == null) {
//如果对象为空,则加锁
synchronized (Singleton2.class) {
//获得锁的线程再次判断对象是否为空
if (singleton == null) {
singleton = new Singleton2();
}
}
}
return singleton;
}
}
class T2 extends Thread {
static Map m = new HashMap();
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
//线程内部获取实例化对象
Singleton2 s = Singleton2.getInstance();
m.put(s, "a:" + i);
}
System.out.println(m);
}
}
public class SingletonSafe {
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
T2 t = new T2();
t.start();
}
}
}
class Singleton3{
//即时加载,饿汉式,保证单例
private static Singleton3 singleton = new Singleton3();
private Singleton3() {
}
public static Singleton3 getInstance() {
return singleton;
}
}
class T3 extends Thread{
static Map m = new HashMap();
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 100; i++) {
//线程内部获取实例化对象
Singleton3 s = Singleton3.getInstance();
m.put(s, "a:" + i);
}
System.out.println(m);
}
}
public class SingletonHungry {
public static void main(String[] args) {
for(int i=0;i < 100;i++){
T3 t = new T3();
t.start();
}
}
}
运行结果也只会返回相同的对象
多线程实现的方式有很多种,这里也没有记录完全,特定的场景会用到不同的线程类,还需到多多钻研