java中定义线程类方法:
- 继承Thread类,实现run()方法;
- 定义类实现Runable接口;@Override 覆盖 run()方法;
- 定义类实现Callable接口,并与Future、线程池结合使用;
Java 线程:
- run() 方法是多线程程序的一个约定。所有的多线程执行代码都在run方法里面。Thread类实际上也是实现了Runnable接口的类。
- thread.start() 方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),(参考线程状态转换)什么时候运行是由操作系统决定的;
常用函数:
sleep(): 强迫一个线程睡眠N毫秒。
isAlive(): 判断一个线程是否存活。
join(): 等待线程终止。
activeCount(): 程序中活跃的线程数。
enumerate(): 枚举程序中的线程。
currentThread(): 得到当前线程。
isDaemon(): 一个线程是否为守护线程。
setDaemon(): 设置一个线程为守护线程。(用户线程和守护线程的区别在于,是否等待主线程依赖于主线程结束而结束)
setName(): 为线程设置一个名称。
wait(): 强迫一个线程等待。
notify(): 通知一个线程继续运行。
setPriority(): 设置一个线程的优先级。
java中定义线程池方法:import java.util.concurrent.Executors; 创建线程池,返回 ExecutorService 对象;
import java.util.concurrent.*;
@Test
public void testNewThreadPool(){
//5种创建线程池的方法
ExecutorService service = Executors.newCachedThreadPool(); //缓存型
ExecutorService service1 = Executors.newSingleThreadExecutor(); //单例型
ExecutorService service2 = Executors.newWorkStealingPool(10); //CPU数量型
ExecutorService service3 = Executors.newFixedThreadPool(10); //固定型
ExecutorService service4 = Executors.newScheduledThreadPool(10);//调度型
//生产线程的工厂类,可以定义线程名,优先级等。
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("demo-pool-%d").build();
//自定义线程池:大小、最大、延时、执行队列,指定名称,策略 //自定义型
ExecutorService service5 = new ThreadPoolExecutor(5, 200,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
service5.submit(new Runnable() {
@Override
public void run() {
while(true){
System.out.println("hello world !");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
System.out.println(" ===> main Thread execute here ! " );
}
//结果先执行main Thread,等待1秒后执行线程池,hello world
Java创建任务:任务分为两种:一种是有返回值的( callable ),一种是没有返回值的( runnable ),因此任务就是实现了callable或runnable的类实例,提交给线程池后,什么时间运行,执行顺序都由线程池来管理;
- 无返回值的任务就是一个实现了runnable接口的类.使用run方法.
- 有返回值的任务是一个实现了callable接口的类.使用call方法。Callable与Future同步使用。
import java.util.concurrent.*;
//有返回结果的任务创建,实现了callable接口的任务类
public static class MyCallable implements Callable{
private int flag = 0;
public MyCallable(int flag){
this.flag = flag;
}
public String call() throws Exception{
if (this.flag == 0){
return "Thread [" + Thread.currentThread().getName() + "flag = 0";
}
if (this.flag == 1){
try {
while (true) {
System.out.println("Thread [" + Thread.currentThread().getName() + "] looping.");
Thread.sleep(2000);
}
} catch (InterruptedException e) {
System.out.println("Thread [" + Thread.currentThread().getName() + "Interrupted");
}
return "Thread [" + Thread.currentThread().getName() + "false";
} else {
throw new Exception("Thread [" + Thread.currentThread().getName() + " Bad flag value!");
}
}
}
@Test
public void testCallable(){
// 定义3个Callable类型的任务
MyCallable task1 = new MyCallable(0);
MyCallable task2 = new MyCallable(1);
MyCallable task3 = new MyCallable(2);
// 创建一个执行任务的服务
ExecutorService es = Executors.newFixedThreadPool(3);
try {
// 提交并执行任务,任务启动时返回了一个Future对象,
// 如果想得到任务执行的结果或者是异常可对这个Future对象进行操作
Future future1 = es.submit(task1);
// 获得第一个任务的结果,如果调用get方法,当前线程会等待任务执行完毕后才往下执行
System.out.println("task1: " + future1.get());
Future future2 = es.submit(task2);
// 等待5秒后,再停止第二个任务。因为第二个任务进行的是无限循环
Thread.sleep(5000);
System.out.println("task2 cancel: " + future2.cancel(true));
// 获取第三个任务的输出,因为执行第三个任务会引起异常
// 所以下面的语句将引起异常的抛出
Future future3 = es.submit(task3);
System.out.println("task3: " + future3.get());
} catch (Exception e){
System.out.println(e.toString());
}
// 停止任务执行服务
es.shutdownNow();
}
- Callable定义的方法是call,而Runnable定义的方法是run。
- Callable的call方法可以有返回值,而Runnable的run方法不能有返回值。
- Callable的call方法可抛出异常,而Runnable的run方法不能抛出异常。
Future表示异步计算的结果,即callable的返回值,它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。Future的cancel方法可以取消任务的执行,它有一布尔参数,参数为 true 表示立即中断任务的执行,参数为 false 表示允许正在运行的任务运行完成。Future的 get 方法等待计算完成,获取计算结果。
Java任务执行:通过java.util.concurrent.ExecutorService接口对象来执行任务,该对象有两个方法可以执行任务execute和submit。execute这种方式提交没有返回值,也就不能判断是否执行成功。submit这种方式它会返回一个Future对象,通过future的get方法来获取返回值,get方法会阻塞住直到任务完成。
public static class MyRunnable implements Runnable{
static int flag = 0;
int index;
public MyRunnable(int index){
this.index = index;
}
@Override
public void run() {
flag +=1;
System.out.println("Thread [" + Thread.currentThread().getName() + "] "+ index+": flag = " + flag);
}
}
@Test
public void testRunnable(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
ExecutorService pool = new ThreadPoolExecutor(5, 200,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.CallerRunsPolicy());
for(int i=0;i<5000;i++) {
//实例化1000个任务,执行call函数
MyRunnable mr = new MyRunnable(i);
// pool.execute(mr); //无返回值的执行任务
pool.submit(mr); //有返回值的执行任务
}
try {
//不加这个,程序完不成1000个任务,就提前随主进程退出了
//每隔1秒,检查一次线程池任务是不是都结束了
pool.awaitTermination(1, TimeUnit.SECONDS);
System.out.println(MyRunnable.flag+"------------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
java关闭线程池: https://www.jianshu.com/p/edd7cb4eafa0
shutdown() //不再接收新的任务,如果线程池内有任务,那么把这些任务执行完毕后,关闭线程池
shutdownNow() //不再接受新的任务,并把任务队列中的任务直接移出掉,如果有正在执行的,尝试进行停止;
awaitTermination(1, TimeUnit.SECONDS); //每隔1秒,检查一次线程池任务是不是都结束了,等待所有任务都结束后运行至后续代码
线程池的使用大体步骤:
1、创建任务,实现runnable或callable的类,实例化后提交给线程池,由线程池来安排运行、停止;
2、ThreadPoolExecutor创建线程池,然后通过threadPool.execute(new Job());提交任务到线程池,
3、执行完线程池创建、提交任务到线程池后,就由操作系统来维护线程的调用,即execute的执行逻辑,与创建时的参数相关;
4、线程池中线程的数量:如果是io密集型任务,可以多配置线程;如果是cpu密集型任务,尽量少些线程;
5、线程池关闭:pool.awaitTermination(1, TimeUnit.SECONDS) 会每隔一秒钟检查一次是否执行完毕
6、execute与submit基本一致,区别是submit可以返回Future对象;
7、Future,ArrayBlockingQueue,ThreadFactory...
8、 策略
java线程池:
- 任务(Job,Task)就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。
- 一个任务通过 java.util.concurrent.ThreadPoolExecutor.execute(Runnable)方法被添加到线程池;
- 对比线程、线程池,任务其实就是线程,实例化一堆不同参数的含run()方法类,executer的submit追加到线程池,并由系统决定运行;返回一个Future对象;
- 任务应该就是一个概念,是线程池模式中的线程,由处理的场景、业务可以归纳为任务;
- 当系统需要处理非常多的执行时间很短的请求,如果每一个请求都开启一个新线程的话,系统就要不断的进行线程的创建和销毁,有时花在创建和销毁线程上的时间会比线程真正执行的时间还长。而且当线程数量太多时,系统不一定能受得了;
多线程 VS 线程池:
多线程弊端
- a. 每次new Thread新建对象性能差。
- b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
- c. 缺乏更多功能,如定时执行、定期执行、线程中断。
使用线程池目的:
- 线程是稀缺资源,不能频繁的创建。
- 解耦作用;线程的创建与执行完全分开,方便维护。
- 应当将其放入一个池子中,可以给其他任务进行复用。