目录
什么是线程
线程是计算机最小的执行单元,是进程内的一个独立执行的流,可以让程序同时做多见事情。
作用及应用场景
可以实现并行和并发的需求,可以在多任务和多用户环境下提高程序的效率,或者拷贝迁移大文件使用提高效率。
多线程实现
并发和并行
并发:同一时刻,单个CPU交替执行多个线程
并行:同一时刻,多个CPU同时执行多个线程
6核12线程配置能同时执行12个线程。
三种方式创建线程
1.继承Thread类
2.实现Runnable接口
3.利用Callable接口和Future接口
class Work1 extends Thread{
@Override
public void run() {
System.out.println(getName()+"启动。。。。");
}
}
/**
* 线程的实现方式1
*/
@Test
public void test1(){
Thread work = new Work1();
work.setName("1");
work.start();
}
class Work2 implements Runnable{
@Override
public void run() {
Thread thread = Thread.currentThread();
System.out.println(thread.getName()+"启动。。。。");
}
}
/**
* 线程的实现方式2
*/
@Test
public void test2(){
/**
* 注意区别work2的方式,该写法只是在当前线程调用work2类中的run方法,不会创建新的线程执行
* Runnable接口作用是定义一个线程任务,要多线程的去执行还是需要new Thread。
*/
Work2 work2 = new Work2();
work2.run();
// 开启2个相同任务的线程,只需要创建一个线程任务对象
Thread thread = new Thread(work2);
Thread thread2 = new Thread(work2);
thread.start();
thread2.start();
}
class Work3 implements Callable<String>{
@Override
public String call() {
Thread thread = Thread.currentThread();
return thread.getName();
}
}
/**
* 创建线程方式3,该方式优点:可以返回线程执行的结果
*/
@Test
public void test3() throws ExecutionException, InterruptedException {
Work3 work3 = new Work3();
// FutureTask 管理多线程运行的结果
FutureTask futureTask = new FutureTask(work3);
Thread thread = new Thread(futureTask);
thread.setName("3");
thread.start();
System.out.println(futureTask.get());
}
线程常用方法
线程优先级
线程调度方法:Java中使用抢占式调度方式。线程优先级越高,CPU执行它的概率越高,但并不会100%被执行。
抢占式调度:CPU随机执行线程
非抢占式调度:CPU按顺序执行线程
守护线程
Java中的守护线程(Daemon Thread)是一种特殊类型的线程,其作用是在后台提供服务和支持,通常用于执行一些低优先级的任务,例如垃圾回收、定时任务、后台数据同步等,这些任务不需要用户干预。一旦所有的非守护线程结束,守护线程将立即终止。
当聊连和传输文件2个线程同时执行时,关闭聊天窗口就没必要继续传输文件,此时可以将传输文件的线程设置为守护线程。
礼让线程
线程主动让出本次CPU执行时间,以允许其他线程执行。但是该线程可能再次获取到CPU。
插入线程
将start的线程插入到当前执行线程前执行。
线程的生命周期
线程安全
产生的原因:当前线程代码在执行时,随时会失去执行权。
解决办法:给线程代码加锁,执行完代码后释放锁。
加锁方式
同步代码块
synchronized关键字加锁,自动锁
static class Shopping extends Thread{
static int food = 100;
@Override
public void run() {
for (int i = 0; i < 100; i++) {
// 锁的对象必须要是唯一的
synchronized (Shopping.class){
if(food > 0){
food--;
System.out.println(Thread.currentThread().getName()+"正在卖第"+(100-food)+"件物品");
}
}
}
}
}
/**
* 同步代码快,synchronized
*/
@Test
public void test4(){
Shopping shopping1 = new Shopping();
Shopping shopping2 = new Shopping();
Shopping shopping3 = new Shopping();
Thread thread1 = new Thread(shopping1);
Thread thread2 = new Thread(shopping2);
Thread thread3 = new Thread(shopping3);
thread1.setName("窗口1");
thread2.setName("窗口2");
thread3.setName("窗口3");
thread1.start();
thread2.start();
thread3.start();
}
同步方法
直接将synchronized修饰符加在方法上,会锁整个方法。
lock锁,手动锁
static class ShoppingLock implements Runnable{
// 同一资源
static int food = 100;
// 同一锁
static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
for (int i = 0; i < 100; i++) {
lock.lock();
try {
if(food > 0){
food--;
System.out.println(Thread.currentThread().getName()+":"+food);
}else {
break;
}
} finally {
// finally方式释放锁
lock.unlock();
}
}
}
}
死锁
一般锁的嵌套使用引起。
线程的等待唤醒
object.wait()和notify()与synchronized隐式锁一起使用。
如果想用手动(显示锁)锁lock,线程等待与唤醒要用new ReentrantLock().newCondition()的await()和signal()。
static class Scz extends Thread{
@SneakyThrows
@Override
public void run() {
while (true){
synchronized (Desk.lock){
// 线程结束标识
if(Desk.count == 0){
break;
}else {
if(Desk.space == 0){
Desk.space = 1;
System.out.println("生产了一个");
Desk.lock.notifyAll();
}else {
Desk.lock.wait();
}
}
}
}
}
}
static class Xfz extends Thread{
@SneakyThrows
@Override
public void run() {
while (true){
synchronized (Desk.lock){
// 线程结束标识
if(Desk.count == 0){
break;
}else {
if(Desk.space == 0){
Desk.lock.wait();
}else {
Desk.space = 0;
Desk.count--;
System.out.println("消费了一个,还要消费"+Desk.count+"个。");
Desk.lock.notifyAll();
}
}
}
}
}
}
static class Desk{
// 总数,线程停止的标识
private static int count = 10;
// 等待与唤醒的标识
private static int space = 0;
// 锁对象,用于将绑定的线程设为等待或唤醒
private static Object lock = new Object();
}
@Test
public void test5(){
Thread scz = new Scz();
Thread xfz = new Xfz();
scz.start();
xfz.start();
}
wait()让线程等待后,并不会再参与CPU的争夺并释放锁。直到被唤醒。
如果代码未加锁,就不会受保护,另一个线程可以等待获取锁的同时执行未加锁的代码。
通过队列实现(方式二)
class SczQue extends Thread{
ArrayBlockingQueue arrayBlockingQueue;
public SczQue(ArrayBlockingQueue arrayBlockingQueue){
this.arrayBlockingQueue = arrayBlockingQueue;
}
@SneakyThrows
@Override
public void run() {
while (true){
// put方法底层自己有锁和等待方法
arrayBlockingQueue.put("食物");
}
}
}
class XfzQue extends Thread{
ArrayBlockingQueue arrayBlockingQueue;
public XfzQue(ArrayBlockingQueue arrayBlockingQueue){
this.arrayBlockingQueue = arrayBlockingQueue;
}
@SneakyThrows
@Override
public void run() {
while (true){
Object take = arrayBlockingQueue.take();
System.out.println(take);
}
}
}
@Test
public void test6(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(1);
SczQue sczQue = new SczQue(arrayBlockingQueue);
XfzQue xfzQue = new XfzQue(arrayBlockingQueue);
sczQue.start();
xfzQue.start();
}
线程池
Executors:执行器工具类,可以用来创建线程池和添加任务。
自定义线程池:参考Executors创建线程池的源码。
Executor创建线程池源码
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
各个参数意义:
核心线程数,最大线程数
空闲线程最大存活时间,时间单位(第三个参数的单位)
线程队列,线程工厂
任务拒绝策略
拒绝策略默认是丢弃任务并抛出异常。