1.线程生命周期
2.Java中实现多线程的方法
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口,并用FutureTask包装类创建线程
使用executorService,callable,future实现由返回结果的多线程
接口区别:
1)、实现Runnable无返回值,
2)、 实现Callable有返回值
方法区别:
1)、 execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
2)、 submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future 对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前 线程直到任务完成
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import cn.hutool.core.thread.ThreadUtil;
public class A {
//继承Thred
public class ThreadB extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
}
}
//实现Runnable
public class ThreadC implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
}
}
//通过FutureTask包装类创建线程
public class MyThreadClass{
//创建线程类
Callable<String> callable = new ThreadD();
//创建未来执行的线程任务
FutureTask<String> futureTask = new FutureTask<String>(callable);
//创建线程池
ThreadPoolExecutor executor = ThreadUtil.newExecutor(5, 20);
//提交线程
executor.submit(futureTask);
//获取返回值
futureTask.get(30,TimeUnit.MINUTES);
}
public class ThreadD implements Callable<String>{
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
String result= "返回值”
return result;
}
}
}
3.synchronized 和 ReentrantLock的区别
相似点:
都是加锁的方式同步,而且都是阻塞式的同步;当一个线程或缺对象锁,进入了同步代码块,其他访问该同步代码块的线程都在外面等待,进行线程阻塞和唤醒的代价是比较高的。
区别:
1)、实现方式区别:
Synchronized是java语言的关键字,是语法层面的锁,需要JVM实现,
ReenTrantLock是JDK1.5以后提供的API层面的锁,需要lock()和unlock()配合try/catch实现
2)、避免死锁现象
Synchronized通过两个字节码指令标记当前锁的状态,如何锁不被释放,就会造成多线程的阻塞
ReenTrantLock是线程可中断的锁,如果等待一定时间无响应,获取锁的线程会自动释放,可以避免出现死锁现象
3)、公平锁
Synchronized是所有的线程同时争抢cpu时间片,会有指令重排,是非公平锁
ReenTrantLock是多线程同时访问一个锁对象时,会按照访问的先后顺序进行执行;默认是非公平锁,可通过参数true设置为公平所
4)、一个ReenTrantLock可以同时绑定多个对象
4.线程池
1).创建线程池方法
注:《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽 的风险。
Executors 返回线程池对象的弊端如下:
FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。 CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
- 构造方法实现
- 通过Executor 框架的工具类Executors来实现
FixedThreadPool : 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。
当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在
一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
SingleThreadExecutor: 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线
程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
*CachedThreadPool:*该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复 用。
对应Executors工具类中的方法如图所示:
2).常用线程池
3. newSingleThreadExecutor:创建一个单线程的线程池,此线程池保证所有任务的执行顺序按照 任务的提交顺序执行。
4. newFixedThreadPool:创建固定大小的线程池,每次提交一个任务就创建一个线程,直到线程达 到线程池的大大小。
5. newCachedThreadPool:创建一个可缓存的线程池,此线程池不会对线程池大小做限制,线程池
大小完全依赖于操作系统(或者说JVM)能够创建的大线程大小。
6. newScheduledThreadPool:创建一个大小无限的线程池,此线程池支持定时以及周期性执行任 务的需求。
7. newSingleThreadExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务 的需求
3)线程池的优点
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系
统的稳定性,使用线程池可以进行统一的分配,调优和监控