java 多线程
术语辨析
任务和线程是不同的,Java 中 Thread 类本身不执行任何操作, 它只驱动赋予它的任务, 而 Runnable 才是定义任务的地方.
创建任务的方式有两种
实现 Runnable 接口中的 run 方法.
查看 Runnable 源码, 可以看到只有一个 run()方法
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object’s
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
使用 Runnable, 直接继承即可, 这样就创建了一个任务.
public class MyRunnable implements Runnable{
@Override
public void run() {
int i=0;
while(true)
System.out.println(i++);
}
// 暗示调度器该线程可以让出资源了
Thread.yield();
}
Thread 中实现 run 方法;
Thread 部分源码
/*
* @see Runnable
* @see Runtime#exit(int)
* @see #run()
* @see #stop()
* @since JDK1.0
*/
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
可以看到 Thread 继承了 Runnable 接口, 所以 Thread 创建任务也是用的 run 方法
代码可以这样写
class MyThread extends Thread{
@Override
public void run() {
int count=10;
while(count–!=0)
System.out.print(count+” “);
}
}
运行任务
不要误会, 下面的方法并未启动一个线程, 而是单纯的调用了实现 Runnable 接口的 MyRunnable 的 run()方法, 所以该任务的运行依旧被分派到该 main 线程中, 不会独立运行在一个新线程里.
public class Main{
public static void main(String[] args){
Runnable runnable=new Runnable();
runnable.run();
}
}
调用 Thread 的 start(),Thread 中记录了很多线程信息, 包括线程名称和优先级等, 以 MyRunnable 的实例作参传入 Thread, 然后 start 即可运行.
public static void main(String[] args){
Thread thread=new Thread(new MyRunnable);
thread.start()
}
在调用 start()后, 子线程不会因为主线程代码执行结束而停止.
3. 使用执行器 (Executor) 管理 Thread.
public static void main(String[] args){
// 常见执行器对象
ExecutorService executorService= Executors.newCachedThreadPool();
// 向执行器中添加任务
executorService.execute(new MyRunnable());
// 关闭向执行器中添加任务;
executorService.shutdown();
创建执行器又三种类型可选, 分别是 newCachedThreadPool,newFixedThreadPool,newSingleThreadPool, 区别如下
newCachedThreadPool: 系统自动调配线程池中的线程数量, 并主动复用已完成的线程资源.
newFixedThreadPool: 可以自定义线程池中线程数量;
newSingleThreadPool: 单例, 线程池中只有一个线程, 加载的任务会被排队, 任务只能一个个依次完成.
线程的返回值
有些任务执行完后需要返回值, 那么创建任务时可以通过实现 Callale 接口而实现该目的,Callable 是一种具有类型参数的泛型, 因为返回值是需要定义类型的.
class Task implements Callable<String>{
@Override
public String call() throws Exception {
Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
TimeUnit.MILLISECONDS.sleep(100);
return “ 线程执行啦 ”;
}
}
然后在 main()方法中接受返回值信息, 对线程信息的管理可以用 Future<String>
public static void main(String[] args){
ExecutorService executorService=Executors.newCachedThreadPool();
Future<String> future=executorService.submit(new Callable<String>(){
@Override
public String call() throws Exception {
return “ 执行啦 ”;
}
});
// 判断是否完成
System.out.println(future.isDone());
// 任务完成后, 才会打印词条语句, 否则会阻塞.
System.out.println(future.get());
// 判断是否完成
System.out.println(future.isDone());
}
———- 输出 ———-
false
执行啦
true
程序运行到 get()方法时会阻塞, 当运行完后, 主程序才会继续向后执行.
程序的优先级
优先级的设置在 run 方法中
public void run(){
// 例如将优先级设置为 10
Thread.currentThread().setPriority(10)
}
为了方便移植, 建议将优先级设置为 Thread 中的三个常量.
后台线程
设置后台线程语法 thread.setDaemon(true); 要在 start()之前.
后台线程创建的线程也为后台线程.