一、Java线程的生命周期
当新建一个线程的时候,状态为NEW,对这个线程执行start()方法后,状态为RUNNABLE,线程执行完毕后,状态为TERMINATED。还有一种情况也可以实现TERMINATED,就是线程执行过程中报错了。
两个线程执行同一个实例,当线程1先去启动并且拿到锁,执行sleep(1000)方法等待一秒,线程2再去拿就拿不到锁了,因为线程1有等待时间,此时线程1状态为TIMED_WAITING,线程2状态为BLOCKED。
两个线程执行同一个实例,当线程1先去启动并且拿到锁,执行sleep(1000)方法等待一秒后执行wait()方法,线程2执行sleep(1300)方法后再去拿就拿不到锁了,因为线程1等待后wait,没有自动唤醒时间了,会一直等待,此时线程1状态为WAITING,线程2状态为BLOCKED。
补充一点:即使是正在运行,也是RUNNABLE,而不是RUNNING。
二、synchronized
实现线程同步,让多个线程排队依次获取某个资源,保证数据不会出错。
问题:synchronized到底锁定的是什么元素?
答:
1.修饰方法
- 静态方法:锁定的是类
- 非静态方法:锁定的是方法的调用者
也就是说,如果synchronized加在类的静态方法中,创建两个类,对这两个类分别创建两个线程,这两个线程会排队等待;如果synchronized加在类的非静态方法中,创建两个类,对这两个类分别创建两个线程,这两个线程不会排队等待,而是各自执行各自的。
2.修饰代码块:锁定的是传入的对象
synchronized(锁定的对象){
}
这个传参传的啥,锁的就是啥。如果是this,锁的就是当前对象,如果是当前对象.class,锁的就是当前类
三、Volatile
1.保证可见性
每个线程有自己的工作内存,现在有一个main线程和线程1,还有一个共享内存i=1,在main线程中对这个共享内存中的i值进行改变,但是线程1不知道,但是加上volatile后,线程1就能知道了
public class VolatileTest { volatile int i = 1; void task1() { System.out.println("开始"); while (i == 1){ } System.out.println("结束"); } public static void main(String[] args) { VolatileTest v = new VolatileTest(); new Thread(v::task1).start(); try { TimeUnit.SECONDS.sleep( timeout: 2); } catch (InterruptedException e) { e.printStackTrace(); } v.i=2; }2.保证有序性,也叫禁止指令重排序
四、synchronized和Volatile区别
1、Synchronized关键字,用来加锁。Volatile只是保持变量的线程可见性。通常适用于一个线程写,多个线程读的场景。
2、Volatile不能保证线程安全。
五、ThreadPoolExecutor
核心线程池数、最大线程池数、非核心线程池数(最大线程池数-核心线程池数)、阻塞队列、异常处理策略(报错)
当核心线程池满了,任务进入到阻塞队列,当阻塞队列也满了,开始创建非核心线程池,当非核心线程池也满了,执行异常处理策略。有一点需要注意,因为执行速度是非常快的,所以可能下一个任务进去时,核心线程池中的任务已经执行完毕了,不会触发异常处理策略。