一、Java多线程创建方式?
1、继承 Thread 类
2、实现 Runnable 接口
3、实现 Callable 接口
4、通过线程池获取线程对象,实现多线程
二、Java线程的基本状态?
新建状态:
使用 new 关键字和 Thread 类或其子类建立一个线程对象后,该线程对象就处于新建状态。它保持这个状态直到程序 start() 这个线程。
就绪状态:
当线程对象调用了 start()方法之后,该线程就进入就绪状态。就绪状态的线程处于就绪队列中,要等待 JVM 里线程调度器的调度。
运行状态:
如果就绪状态的线程获取 CPU 资源,就可以执行 run(),此时线程便处于运行状态。处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
阻塞状态:
如果一个线程执行了 sleep(睡眠)、suspend(挂起)等方法,失去所占用资源之后,该线程就从运行状态进入阻塞状态。在睡眠时间已到或获得设备资源后可以重新进入就绪状态。
1、等待阻塞 -- 通过调用线程的wait()方法,让线程等待某工作的完成。
2、同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用),它会进入同步阻塞状态。
3、 其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时,线程会进入到阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
死亡状态:
一个运行状态的线程完成任务或者其他终止条件发生时,该线程就切换到终止状态。
1、run()或call()方法执行完成,线程正常结束。
2、线程抛出一个未捕获的Exception或Error
3、直接调用该线程的stop方法结束该线程---该方法容易导致死锁,通常不推荐使用。
三、多线程中的常用方法及含义?
start():开启线程,使线程从新建进入就绪状态
sleep():线程睡眠,使当前线程休息, 需要指定睡眠时间,当执行sleep方法后进入阻塞状态,不释放cpu资源。
wait(): 当前线程进入等待状态,让出CPU给其他线程,自己进入等待队列,等待被唤醒,释放cpu资源。
notify():唤醒等待队列中的一个线程,唤醒后会重新进入就绪状态,准备抢夺cpu资源。
notifyAll():唤醒等待队列中的所有线程,抢夺cpu资源。
yield():让出CPU。当前线程让出CPU给其他的线程执行,但是自己也会进入就绪状态参与CPU的抢夺,因此调用yield方法后,仍然可能继续获得CPU。
join():加入线程,会将调用的线程加入当前线程。等待加入的线程执行完成后才会继续执行当前线程。
四、Java多线程线程池及核心参数?
ThreadPoolExecutor():是最原始的线程池创建,上面 1-3 创建方式都是对 ThreadPoolExecutor 的封装。
newSingleThreadExecutor():它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目;
newCachedThreadPool():它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列;
newFixedThreadPool(int nThreads):重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads;
newSingleThreadScheduledExecutor():创建单线程池,返回ScheduledExecutorService,可以进行定时或周期性的工作调度;
newScheduledThreadPool(int corePoolSize) :和newSingleThreadScheduledExecutor()类似 ,创 建的 是个ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程;
newWorkStealingPool(int parallelism):这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建 ForkJoinPool,利用 Work-Stealing 算法,并行地处理任务,不保证处理顺序;
核心参数:
- corePoolSize:线程池的核心线程数
- maximumPoolSize: 线程池最大线程数
- keepAliveTime:空闲线程的存活时间
- unit:空闲线程存活时间单位
- workQueue:线程池等待队列。
- threadFactory:线程工厂,生产线程的类。
- handler:线程拒绝策略。当线程池满的时候,该怎么拒绝后来的线程。
五、Java多线程中 Synchronized & volatile理解
Synchronized :java同步锁,能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
通过 synchronized 关键字来实现,所有加上synchronized 和 块语句,在多线程访问的时候,同一时刻只能有一个线程能够用synchronized 修饰的方法 或者 代码块。
synchronized锁 底层实现是,它指针指向的是一个monitor对象的起始地址每个对象实例都会有一个 monitor。其中monitor可以与对象一起创建、销毁,当多个线程同时访问一段同步代码时,会先存放到 _EntryList 集合中,接下来当线程获取到对象的monitor时,就会把_owner变量设置为当前线程。同时count变量+1,来保证在访问时只有一个引用,释放锁时同理count -1 ;
volatile :java提供的轻量级的同步机制,只能修时变量,修改后直接存储到主内存中。用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值。
六、线程池的拒绝策略/饱和策略
当请求任务不断的过来,而系统此时又处理不过来的时候,我们需要采取的策略是拒绝服务。 四种处理策略:
- AbortPolicy(抛出一个异常,默认的) 中止
- DiscardPolicy(新提交的任务直接被抛弃) 抛弃
- DiscardOldestPolicy(丢弃队列里最老的任务,将当前这个任务继续提交给线程池) 抛弃最旧
- CallerRunsPolicy(交给线程池调用所在的线程进行处理,即将某些任务回退到调用者) 调用者运行。