Java SE学习笔记 -- 多线程

多线程学习笔记

@拉勾训练营的程序🐒一枚 - Java就业7期

- 线程的生命周期

在这里插入图片描述

  • 新建状态
    当线程对象对创建后,即进入了新建状态,如:Thread myThread = new MyThread()
  • 就绪状态
    当调用线程对象的start()方法,线程进入就绪状态。处于就绪状态的线程,只是已经做好了准备,随时等待CPU调度分配,获取时间片,并不会立即执行。
  • 运行状态
    当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执行,即进入到运行状态。注:就 绪状态是进入到运行状态的唯一入口,也就是说,线程要想进入运行状态执行,首先必须处于就绪状态中
  • 阻塞状态
  1. 等待阻塞 - wait,使本线程进入到等待阻塞状态
  2. 同步阻塞 - synchronized,线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态
  3. 其他阻塞 - 过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
  • 死亡/结束状态
    线程执行完了或者因异常退出了run()方法,该线程结束生命周期
- 线程的创建三种方式:
  • extends Thread类,重写run方法

  • implements Runnable接口,实现run方法

  • implements Callable接口,实现call方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。FutureTask类实际上是同时实现了Runnable和Future接口。

  • Runnable接口有个问题,它的方法没有返回值。如果任务需要一个返回结果,那么只能保存到变量,还要提供额外的方法读取,非常不便。所以,Java标准库还提供了一个Callable接口,和Runnable接口比,它多了一个返回值。对线程池提交一个Callable任务,可以获得一个Future对象;可以用Future在将来某个时刻获取结果。

  • 一个Future接口表示一个未来可能会返回的结果,它定义的方法有:

  1. get():获取结果(可能会等待)
  2. get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
  3. cancel(boolean mayInterruptIfRunning):取消当前任务;
  4. isDone():判断任务是否已完成。
  • 线程的优先级
    可以对线程设定优先级,设定优先级的方法是:
    Thread.setPriority(int n) // 1~10, 默认值5
    N.B :不能对同一线程对象两次调用start()方法。
- 多线程中常见方法
  • join方法,让一个线程强制运行,线程强制运行期间,其他线程无法运行,必须等待此线程完成之后才可以继续执行。也就是指等待该线程结束,然后才继续往下执行自身线程。
  • notify方法,唤醒sleep的线程对象
  • sleep/wait方法,允许一个线程进行暂时的休眠/等待。它们的区别在于:sleep()释放CPU执行权,但不释放同步锁,wait()释放CPU执行权,也释放同步锁,使得其他线程可以使用同步控制块或者方法。sleep()是Thread类中的基本方法,而wait()是Object类中的基本方法。sleep可以在任意地方使用,但wait只能在同步代码方法或者同步代码块中使用。sleep需要指定时间,而wait无需。
  • interrupt方法,当一个线程运行时,另外一个线程可以直接通过interrupt()方法中断其运行状态
  • setDaemon方法,可以设置一个后台线程,这样即使 Java 线程结束了,此后台线程依然会继续执行
- 线程同步

当多个线程同时访问同一种共享资源时,可能会造成数据的覆盖等不一致性问题,此时就需要对线 程之间进行通信和协调,该机制就叫做线程的同步机制
引入同步机制,在一个线程使用一个资源时为其加锁,以防止其他的线程访问该资源,直至解锁。以防止资源的“过度消耗”或者“虚拟消耗”

  • synchronizded关键字的使用
  1. 代码块,synchronized同步代码块是细粒度的并发控制,只会将块中的代码同步,代码块之外的代码可以被其他线程同时访问。
class MyRunnable implements Runnable {
	public void run(){
		synchronized(this) {
		//同步方法块
		}
	}
}
  1. 代码方法
class MyRunnable implements Runnable {
	public synchronized void run(){
	
	}
}
  • 线程间共享变量需要使用volatile关键字标记,确保每个线程都能读取到更新后的变量值。
- 守护线程
  • 如果有一个线程没有退出,JVM进程就不会退出。所以,必须保证所有线程都能及时结束。但是有一种线程的目的就是无限循环,例如,一个定时触发任务的线程。如果这个线程不结束,JVM进程就无法结束。问题是,由谁负责结束这个线程?然而这类线程经常没有负责人来负责结束它们。但是,当其他线程结束时,JVM进程又必须要结束,怎么办?答案是使用守护线程(Daemon Thread)。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。因此,JVM退出时,不必关心守护线程是否已结束。
- 线程池
  • 为何引入线程池?
  1. 降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗。
  2. 提高系统响应速度,当有任务到达时,通过复用已存在的线程,无需等待新线程的创建便能立即执行。
  3. 方便线程并发数的管控。因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成CPU过度切换。
  4. 提供更强大的功能,延时定时线程池。
  • 线程池的创建及基本参数
    在这里插入图片描述

  • corePoolSize(线程池基本大小): 当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务,直到已创建的线程数大于或等于corePoolSize时, (除了利用提交新任务来创建和启动线程(按需构造), 也可以通过 prestartCoreThread() 或 prestartAllCoreThreads() 方法来提前启动线程池中的基本线程) 。

  • maximumPoolSize(线程池最大大小):线程池所允许的最大线程个数。当队列满了,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务。另外,对于无界队列,可忽略该参数

  • keepAliveTime(线程存活保持时间): 当线程池中线程数大于核心线程数时,线程的空闲时间如果超过线程存活时间,那么这个线程就会被销毁,直到线程池中的线程数小于等于核心线程数

  • workQueue(任务队列): 用于传输和保存等待执行任务的阻塞队列

  • threadFactory(线程工厂): 用于创建新线程。threadFactory创建的线程也是采用new Thread()方式,threadFactory创建的线程名都具有统一的风格:pool-m-thread-n(m为线程池的编号,n为线程池内的线程编号)

  • handler(线程饱和策略): 当线程池和队列都满了,再加入线程会执行此策略

使用线程池
  • 什么叫线程池?
    能接收大量小任务并进行分发处理的就是线程池。

  • 创建线程池
    在这里插入图片描述

  • Java标准库提供了ExecutorService接口表示线程池。线程池在程序结束的时候要关闭。使用shutdown()方法关闭线程池的时候,它会等待正在执行的任务先完成,然后再关闭。shutdownNow()会立刻停止正在执行的任务,awaitTermination()则会等待指定的时间让线程池关闭。

// 创建固定大小的线程池:
ExecutorService executor = Executors.newFixedThreadPool(3);
// 提交任务:
executor.submit(task1);
executor.submit(task2);
executor.submit(task3);
executor.close

executor.submit(task4);
executor.submit(task5);
  • 每个task都是一个Runnable接口的实现
  1. Java标准库提供的几个常用实现类有:
  2. FixedThreadPool:线程数固定的线程池;
  3. CachedThreadPool:线程数根据任务动态调整的线程池;
  4. SingleThreadExecutor:仅单线程执行的线程池。
  5. 动态线程池 Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
}
int min = 4;
int max = 10;
ExecutorService es = new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS,       new SynchronousQueue<Runnable>());
  • 需要定期反复执行任务使用ScheduledThreadPool,提交一次性任务,它会在指定延迟后只执行一次。
ScheduledExecutorService ses = Executors.newScheduledThreadPool(4);
//1秒以后执行的任务
ses.schedule(new Task("one-time"), 1, TimeUnit.SECONDS);
// 2秒后开始执行定时任务,每3秒执行:
ses.scheduleAtFixedRate(new Task("fixed-rate"), 2, 3, TimeUnit.SECONDS);
// 2秒后开始执行定时任务,以3秒为间隔执行:
ses.scheduleWithFixedDelay(new Task("fixed-delay"), 2, 3, TimeUnit.SECONDS);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值