一、一个程序可能包含许多任务,这些任务是可以并行的执行的。而任务需要在线程中才能运行,线程提供了任务运行的控制机制,包括控制任务的开始,中断,优先级等等,但是线程只提供控制功能,任务的具体实现则需要一个任务类来指定。因此,要实现多线程,最基本的,就是任务类和线程类。
二、创建任务和线程
1、任务类
任务类,它的作用是指明一个任务是如何运行的,它需要实现Runable接口,Runable接口里有一个run方法,在这里,我们告诉线程将如何运行这个任务。
2、线程类
线程类,包括了创建线程的构造方法以及控制线程的方法。
常用方法如下:
+Thread(task:Runnable) ->为指定任务创建一个线程(参数为任务类,并且该类必须实现Runnable接口)
+start() : void ->启动线程,使方法run()被JVM调用
+isAlive():boolean -> 测试线程当前是否正在运行
+setPriority(p : int ):int ->设置线程的优先级,范围从1-10; MIN_PRIORITY = 1,NORM_PRIORITY = 5,MAX_PRIORITY = 10
+join():void ->等待线程结束
+sleep(millis:long):void ->使线程睡眠指定的数
+yield():void ->主动让出CPU,让其他线程执行
+interrupt():void ->中断线程
java虚拟机总是选择当前优先级最高的可运行线程。当所有可运行线程具有相同的优先级时,那将会用循环队列给他们分配等额的CPU份额,称为循环调度。
3、概括创建一个任务类、一个线程、以及开始一个线程的主要步骤
->任务类
/*
* 任务类,必须实现Runnable接口
* run()方法指明该任务类的运行过程
*/
public class TaskClass implements Runnable{
...
public void run() {
...
}
}
创建一个线程并且开始线程
public class TestThread {
...
public void someMethod(){
//创建一个任务类
TaskClass task = new TaskClass(。。);
//创建一条线程
Thread thread = new Thread(task);
//开启线程,执行指定的任务,即自动调用task中的run()方法
thread.start();
}
}
4、具体例子
/*
* 任务类,指明打印字符串任务的运行过程
*/
public class PrintChar implements Runnable{
private char charToPrint;
private int time;
public PrintChar(char charToPrint, int time) {
super();
this.charToPrint = charToPrint;
this.time = time;
}
public void run() {
for(int i = 0; i <time; i++){
System.out.print(charToPrint + " ");
}
}
}
/*
* 任务类,指明打印数字的具体实现过程
*/
public class PrintNum implements Runnable{
private int num;
public PrintNum(int num) {
super();
this.num = num;
}
public void run() {
for(int i = 0; i < num; i++){
System.out.println(i + " ");
}
}
}
/*
* 开启两条线程,同时执行打印操作
*/
public class Test {
public static void main(String[] args) {
Runnable printA = new PrintChar('a', 20);
Runnable printNum = new PrintNum(20);
Thread threadA = new Thread(printA);
Thread threadNum = new Thread(printNum);
threadA.start();
threadNum.start();
}
}
三、线程池
1、前面我们为每一个任务都创建一个线程,但是如果有很多个任务,每个任务都创建一个线程的话,则会显得很低效,因为开始一个线程的花销是比较大的。这时,我们可以通过线程池来管理并发执行任务的个数。
2、java提供Executor接口来执行线程池中的任务,提供ExecutorService接口来管理和控制任务。ExecutorService是Executor接口的子接口。
常用的方法如下:
Execute接口:
+execute(Runnable object):void 执行一个任务
+newFixedThreadPool(numberOfThreads:int):ExecutorService -> 创建一个线程池,该线程池可并发执行的线程数固定不变。在线程的当前任务结束后,它可以被重用以执行另一个任务。当所有线程都用完时,则线程处于等待状态。
+newCachedThreadPool():ExecutorService -> 创建一个线程池,它按需创建新线程。如果缓冲池中的线程在60秒内没有被使用,则终止它。
ExecutorService接口:
+shutdown():void ->关闭执行器,不允许新的线程加入线程池中。但允许在线程池的任务继续执行至完成。
+shutdownNow():List<Runnable> ->即使线程池中还有未完成的线程,还是会立即关闭执行器。返回未完成任务的清单
+isShutdown():boolean ->如果执行器已经被关闭,则返回true
+isTerminated():boolean ->如果线程池中所有任务都已经终止了,则返回true;