开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1 天,点击查看活动详情
回顾上文: Java并发基础(一) - 掘金 (juejin.cn),我们知道
- 通过实现
Runnable接口可以定义一个任务;- 在
Main方法中直接调用任务的run()方法是使用Main函数的主线程调用的任务- 通过
Thread类生成的对象传入任务,调用对象的start()方法会为该线程执行必需的初始化操作,并且调用Runnable的run()方法。- 然后又了解了
Executor可以帮助我们更好的去管理线程,因为它有线程池的概念。- 对了,我们还补充了不用理会线程组的概念,因为它是不被推荐的。
本文内容:
- 定义任务的第二种方式
Callable,它和Runnable的区别是,它可以在任务完成时返回一个值。- 了解影响任务执行的行为:休眠、让步、优先级
带返回值的任务
我们对照着Runnable来学,首先我们需要定义这个Callable任务。
static class CallableDemo implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("hi");
return "芜湖起飞";
}
}
Callable<String>通过泛型定义,表示它返回值是String类型。
在主线程中直接生成对象调用,使用call()方法执行,会执行return之前的内容,返回值可以通过变量接收。
public static void main(String[] args) throws Exception {
CallableDemo callableDemo = new CallableDemo();
String call = callableDemo.call();
System.out.println(call);
}
和之前一样,这里是使用Main方法的线程,如果说要另起一个新的线程呢?
Thread thread = new Thread(new CallableDemo());
这样尝试会报错,因为Thread只能传入实现Runnable接口的对象。我们会再想到使用Executors去处理。
submit()
sunmit()提交任务以供执行,并返回一个Future,表示该任务的结果。通过调用Future的get方法将在成功完成任务时返回任务的结果。
具体步骤描述:
使用Executors构建一个线程池对象,调用其submit()方法,它支持传入一个Callable对象。返回值是一个Future对象。
Future对象
Future表示异步计算的结果。提供了用于检查计算是否完成、等待计算完成以及检索计算结果的方法。
isDone():查询Future是否已经完成,如果完成可以调用get()获取结果。get():获取结果,如果没有结果,线程将会被阻塞,直到结果出来。
看看代码具体步骤:
ExecutorService exec = Executors.newCachedThreadPool(); // 声明一个线程池
Future<String> result = exec.submit(new CallableDemo()); // 调用submit方法,得到一个Futre对象
String s = result.get(); // 直接拿结果
System.out.println(s); // 输出结果
exec.shutdown(); //记得关闭线程池
影响任务行为
线程的执行是因为CPU在该时间片段下执行的是你这个任务,如果没有做线程优先级的话,分配规则是随机的,如果要打乱这种随机性,我们在定义任务本身时,就可以在编写业务逻辑时,对任务所做的事情做一些设置,例如是否要让其他线程去执行。
休眠
注意,该线程休眠后,下一个被执行的线程是随机的。(如果没有特殊设置,理论上是这样)
休眠是需要指定一个时间值,在该时间值中任务将会被终止,时间结束后会恢复执行。在JavaSE5/6中,推荐使用TimeUnit.xx.sleep()的方式去替换Thread.sleep(),因为会有更好的可读性。
使用
TimeUnit.xx.sleep()
在任务中,需要终止的地方加上该命令即可。因为需要设定时间,我们关注一下有哪些时间可以选择:
TimeUnit.SECONDS:设置秒级,几秒TimeUnit.MILLISECONDS:设置毫秒级,几毫秒;1秒=1000毫秒TimeUnit.DAYS:设置天数,几天TimeUnit.HOURS:设置小时,几小时TimeUnit.MINUTES:设置分钟,几分钟
让步
让步的意思是,你知道在一个定义的任务中,编写的业务代码执行的差不多了,这时候需要空出CPU的时间片让给其他线程去做事情了,可以使用yield()发出一个“建议”,表示其他具有相同优先级的线程可以运行。
使用
在任务中,添加:Thread.yield()即可。
区别休眠和让步,休眠很强制性,在该时间段内该任务就是不能执行!优先级较低的任务有可能会被执行。让步的话可能让出去了,但是下一秒还是线程本身,优先级较低的线程还是无法占用CPU。
优先级
在绝大多数时间里,所有线程都应该以默认的优先级运行。试图操纵线程优先级通常是一种错误。这是在Java编程思想中提及的。所以它用的少吧,简单了解一下。
在任务的业务中,通过调用Thread.currentThread.setPriority(优先级)的方式控制当前线程的优先级。每个线程都有一个优先级,由1到10之间的整数表示。
可以在任务的属性中添加优先级,在new方法调用时通过setter的方式注入该优先级属性。
Thread类提供3个常量属性:
Thread.MAX_PRIORITY:最高优先,值为10Thread.NORM_PRIORITY:普通优先,值为5Thread.MIN_PRIORITY:最低优先,值为1
这个只是改变概率和权重,并非是10就一定比5优先,只是概率上说大概率会比5优先。