线程池
作用:提高程序的执行效率;
1,如果程序中有大量短时间任务的线程任务,由于创建和销毁线程需要和底层操作系统交互,大量时间都耗费在创建和销毁线程上,因而比较浪费时间,系统效率很低;线程池里的每一个线程任务结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用,因而借助线程池可以提高程序的执行效率。
控制线程的数量,防止程序崩溃;
2,如果不加限制地创建和启动线程很容易造成程序崩溃,比如高并发1000W个线程,JVM就需要有保存1000W个线程的空间,这样极易出现内存溢出;线程池中线程数量是一定的,可以有效避免出现内存溢出。
分析如下代码:
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Text {
public static void main(String[] args) {
//线程池:实现。
ExecutorService service=Executors.newFixedThreadPool(3);//创建3个线程池。
service.submit(new Timenow("线程1"));//提交事务1
service.submit(new Timenow("线程2"));//提交事务2
service.submit(new Timenow("线程3"));//提交事务3,只有三个线程池,所以只会一次执行三个事务。
service.submit(new Timenow("线程4"));//前三个事务中某一个(因为是抢占式执行,所以不确定那个会先执行完成)执行完毕,才会提交事务4
service.shutdown();//关闭资源
}
}
class Timenow implements Runnable{
String name;
Timenow (String name){
this.name=name;
}
@Override
public void run() {
for(int i=0;i<3;i++) {
System.out.println(name+";"+new Date());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("......");
}
}
关于输出结果:
下边列出可能的一种输出结果,我们看到线程1,2,3抢占式交叉进行,其中的某一个线程结束后,线程4才开始执行。
线程1;Sun Jun 30 11:39:11 CST 2019
线程3;Sun Jun 30 11:39:11 CST 2019
线程2;Sun Jun 30 11:39:11 CST 2019
线程3;Sun Jun 30 11:39:12 CST 2019
线程2;Sun Jun 30 11:39:12 CST 2019
线程1;Sun Jun 30 11:39:12 CST 2019
线程1;Sun Jun 30 11:39:13 CST 2019
线程2;Sun Jun 30 11:39:13 CST 2019
线程3;Sun Jun 30 11:39:13 CST 2019
线程3......
线程2......
线程4;Sun Jun 30 11:39:14 CST 2019
线程1......
线程4;Sun Jun 30 11:39:15 CST 2019
线程4;Sun Jun 30 11:39:16 CST 2019
线程4......
线程常用方法
interrupt()
interrupt方法:结束线程在调用Object类的wait方法或该类的join方法、sleep方法过程中的阻塞状态,并在调用wait、join和sleep方法处产生InterruptedException异常。
看如下例子:
import java.util.Date;
public class T {
public static void main(String[] args) {//1,main方法为主线程,下边创建一个时间线程。
Thread thread=new Thread(new Timenew());//2,创建新的时间线程
thread.start();//3,时间线程准备就绪
try {
Thread.sleep(5000);//主线程阻塞5秒,下边代码先不执行。
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt();//5,改行代码执行,调用interrupt方法的线程中断阻塞,时间线程中断阻塞。
}
}
class Timenew implements Runnable{
@Override
public void run() {
System.out.println(new Date());
try {
Thread.sleep(30000);//4,时间线程阻塞30秒,此时主线程和时间线程都阻塞,主线程先中断阻塞。
} catch (InterruptedException e) {
e.printStackTrace();
}
System.err.println("......");//6,时间线程提前中断阻塞。
}
}
输出如下结果:
currentThread()
返回当前正在执行的线程对象。
public class T {
public static void main(String[] args) {
Thread thread=Thread.currentThread();//返回当前线程:此处只有主线程在执行,所以返回主线程。
System.out.println("主线程"+thread);
Thread thread1=new Thread(new Timenew());//创建新的时间线程
thread1.start();
System.out.println("时间线程"+thread1);//输出时间线程
}
}
class Timenew implements Runnable{
@Override
public void run() {
Thread t=Thread.currentThread();//时间线程开始执行
System.out.println("时间线程"+t);
}
}
输出结果如下:
isAlive()
判定该线程是否处于就绪、运行或阻塞状态,如果是则返回true,否则返回false。
public class T {
public static void main(String[] args) {
Thread thread =Thread.currentThread();//主线程
System.out.println("1..."+thread.isAlive());//判断主线程是否在执行,一定输出true
TimeThread1 timeThread=new TimeThread1(thread);//thread为主线程对象
timeThread.start();
System.out.println(111);
}
}
class TimeThread1 extends Thread{
Thread thread;
public TimeThread1(Thread thread) {
this.thread=thread;
}
@Override
public void run() {
System.out.println("2..."+thread.isAlive());//如果先执行111,此行代码很大可能输出false,因为主线程很大可能结束了。
try {
Thread.sleep(20);//在时间线程阻塞的时候,主线程一定会执行完毕,所以下边代码一定输出false
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("3..."+thread.isAlive());//此处一定输出false
}
}
输出结果如下:
setDaemon()
看如下例子:
public class T {
public static void main(String[] args) {
TimeThread1 timeThread=new TimeThread1();//时间线程
timeThread.setDaemon(true);//时间线程设为守护线程。守护线程会随着进程中最后一个非守护线程的结束而结束。
timeThread.start();//该行代码结束,主线程可能立马结束,不会输出111.如果主线程没有结束,会输出111.再结束主线程。
}
}
class TimeThread1 extends Thread{
@Override
public void run() {
while (true) {
System.out.println(111);
}
}
}
输出某个结果为:
再看如下代码:
public class T {
public static void main(String[] args) {
TimeThread1 timeThread=new TimeThread1();//时间线程
timeThread.setDaemon(true);//时间线程设为守护线程。守护线程会随着进程中最后一个非守护线程的结束而结束。
timeThread.start();//该行代码结束,主线程可能立马结束,不会输出111.如果主线程没有结束,会输出111.再结束主线程。
try {
Thread.sleep(3000);//加该行代码后,主线程阻塞,阻塞期间时间线程一定会执行,一定输出111,并且三秒之后结束线程,因为主线程为非守护线程,主线程结束后,守护线程也结束。
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class TimeThread1 extends Thread{
@Override
public void run() {
while (true) {
System.out.println(111);
}
}
}
在主线程阻塞期间,一定会输出111,最后主线程结束,守护线程也结束。
join()
执行该方法的线程进入阻塞状态,直到调用该方法的线程结束后再由阻塞转为就绪状态。
如下例子:
import java.util.Date;
public class T {
public static void main(String[] args) {
TimeThread1 timeThread=new TimeThread1();
timeThread.start();//就绪状态:开始执行线程中代码。
CountThread countThread=new CountThread(timeThread);//紧接着开启另一个线程。不会是一个线程结束再开启另一个线程。
countThread.start();//就绪状态:开始执行代码。
}
}
class CountThread extends Thread{
TimeThread1 timeThread;
public CountThread(TimeThread1 timeThread) {//构造方法
this.timeThread=timeThread;
}
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(i);
try {
timeThread.join();//一个线程执行另一个线程调用join方法的代码,则该计数器线程阻塞,一直到调用join方法的线程结束。
} catch (InterruptedException e1) {
e1.printStackTrace();
}
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TimeThread1 extends Thread{
@Override
public void run() {
for(int i=0;i<5;i++) {
System.out.println(new Date());
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
输出结果分析:计数器线程会在某一时刻阻塞,只要时间线程开始执行,就会有一直执行该时间线程。
此种情况下,计数器线程执行了一次后阻塞,时间线程只要开始加入,就会一直执行该时间线程。
其他方法
void start():使该线程开始启动,Java 虚拟机负责调用该线程的 run() 方法。多次启动一个线程是非法的。
void sleep(long millis):Thread类静态方法,线程进入阻塞状态,在指定时间(单位为毫秒)到达之后进入就绪状态(Runnable),而非立即进入执行状态。
void yield():静态方法,当前线程放弃占用CPU资源,回到就绪状态,使其他优先级不低于此线程的线程有机会被执行。
void setPriority(int newPriority):设置当前线程的优先级,线程优先级越高,线程获得执行的次数越多,Java线程的优先级用整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY 最高优先级值为10;
static int NORM_PRIORITY 默认优先级值为5;
static int MIN_PRIORITY 最低优先级值为1;
注意:同一个线程类创建的多个线程,线程优先级越高的线程获得的执行次数极有可能越多;但是不同线程类创建的线程,线程优先级越高,执行次数不一定越多,这个run方法的代码复杂度有关!
int getPriority():获得当前线程的优先级。