20200718 by 1z
- 请说明sleep() 和 wait()有什么区别?
1、sleep() 是Thread的成员方法而Wait()是Object的成员方法
2、sleep()没有释放锁,因此在sleep结束后依旧是原线程执行,而wait()释放了锁,在notify(notifyAll)后其他线程可以使用同步控制块或者方法。
3、wait,notify,notifyAll只能在同步控制块方法 or 同步控制块中使用,而sleep可以在任何地方使用。
- 请你说明一下在监视器(Monitor)内部,是如何做到线程同步的?在程序又应该做哪种级别的同步呢?
监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和一个对象引用(object or class)相关联。线程在获取锁之前不允许执行同步代码。
java中提供了显示监视器(Lock) 和 隐式监视器(synchronized)两种锁方案
- 请分析一下同步方法 和 同步代码块的区别是什么?
1.什么是同步方法 和 同步代码块
* 同步方法是有synchronized修饰符修饰的方法。java的每个对象都有着一个内置锁,当使用此关键字修饰方法时,内置锁会保护整个方法,在调用给方法前,要获取内置锁,否则处于阻塞状态。
public synchronized method(){}
* 同步代码块 是有synchronized修饰的方法,用该关键词修饰方法时,内置锁会保护整个方法,在调用给方法前,要获取内置锁,否则处于阻塞状态.
synchronized(Object o){}
2.同步方法 和 同步代码块的区别
* 同步方法默认用this或者当前类class对象作为锁
* 同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,我们可以选择只同步会发生同步问题的部分代码而不是整个方法;
* 同步方法使用关键字 synchronized修饰方法,而同步代码块主要是修饰需要进行同步的方法,用synchronized(object){//需要同步的部分方法}进行修饰
- 请详细描述一下线程从创建到死亡的几种方式都有哪些?
- 创建线程有哪几种不同的方式,你喜欢哪一种?为什么?
(详见96题)
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法
我喜欢实现Runnable方法,因为可以使用lambda表达式方便快捷,而且可以多实现
new Thread(()->{
try {
//
} catch (Exception e) {
e.printStackTrace();
}
},"threadName").start();
- 请解释一下java多线程回调是什么意思?
调用者执行调用时将自己作为参数给被调用者,被调用者达到一定条件之后通过这个参数调用 调用者
多线程下回调多用于同时计算运算的结果回调给调用者
存在客户程序 client,调用服务程序server中的方法func1,然后server在某个时候client的方法func2
对于client而言,func2就是回调方法。
- 请列举一下启动线程有哪几种方式,之后再说明一下线程池的种类有哪些?
1.启动线程的方式(详见96题)
继承Thread类,重写run方法。
实现Runnable接口,重写run方法。
实现callable接口,重写call方法启动线程用start方法
参考链接 https://blog.csdn.net/qq_41555783/article/details/102001231
2.线程池的种类
newsingleThreadExecutor:单线程的线程池,处理完一个任务接着下一个,若异常则起一个新的线程
newFixedThreadPool:指定数目的线程池,如果多于这个数目则加入缓存队列
newcachedThreadPool:不限数目的线程池,完全依赖于JVM能创建的线程数,可能出现内存不足
自定义线程池(见90题)
- 请简要说明一下Java中cyclicbarrier 和 countdownlatch的区别分别是什么?
1. cyclicbarrier支持重用,而countDownLatch不支持重用
2. countdownlatch用于保持多个线程之间的执行顺序,cyclicbarrier用于一组线程互相等待对方执行一些操作后再一起继续执行。
3. CountDownLatch一般用于某个线程A等待若干个线程执行完任务之后才进行
而 CyclicBarrier一般用于一组线程互相等待至某个状态,然后再同时执行。
4. countdownlatch是计数器,线程完成一个就记一个,做递减操作,而CyclicBarrier更像一个水闸,线程执行就想水流动,但是会在水闸处停止,直至水满(线程都运行完毕)才开始泄流。
参考链接: https://blog.csdn.net/paul342/article/details/49661991
- 请说明一下线程池有什么优势?
创建一个对象要获取内存资源或其他更多资源。在Java中更加如此,虚拟机将视图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象的创建和销毁,这就是“池化资源”技术产生的原因。
线程池顾名思义就是事先创建若干个可执行的线程放入一个池(容器)中,需要的时候从池中获取线程不用自行创建,使用完毕不需要销毁线程,而是返回池中从而减少创建和销毁对象的开销。
- 请说明一下java中有几种线程池,并且详细描述一下线程池的实现过程?
1. java api中提供了三种线程池
newsingleThreadExecutor:单线程的线程池,处理完一个任务接着下一个,若异常则起一个新的线程
newFixedThreadPool:指定数目的线程池,如果多于这个数目则加入缓存队列
newcachedThreadPool:不限数目的线程池,完全依赖于JVM能创建的线程数,可能出现内存不足
使用方法:
public static void testSingleThread() {
ExecutorService singleService = Executors.newSingleThreadExecutor();
try {
for (int i = 0; i < 10; i++) {
singleService.execute(() -> {
System.out.println(Thread.currentThread().getName() + "在工作\t");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
singleService.shutdown();
}
}
-----------------------------------------------------------------------------
2. 线程池的详细实现过程和代码演示
package com.liz.juc.thread_pool.relearn_thread_pool;
import java.util.concurrent.*;
/**
* 自定义线程池的七大参数:
* 1.corePoolSize : 核心线程数,先来的线程可以直接为核心线程,当线程数 在corePoolSize和maximumPoolSize之间的时候,其他线程放入队列中
* 如果超过了maxiumPoolSize,采用拒绝策略
* <p>
* 2.maximumPoolSize : 最大线程数,在系统线程池中的CacheThreadPool方案,该值为Integer.MAXVALUE 很容易出现oom
* <p>
* 3.KeepAliveTime : 线程池中空闲线程等待工作的超时时间,单位是ns,一旦超时,直接进行销毁
* <p>
* 4.TimeUnit.SECONDS : 设置超时时间
* <p>
* 5.BlockingQueue<Runnable> workQueue : 默认的消息队列(存放闲置线程)
* <p>
* 6.ThreadFactory threadFactory : 线程工厂,设置为默认工厂即可 Executors.defaultThreadFactory()
* <p>
* 7.RejectedExecutionHandler handler : 拒绝策略
* new ThreadPoolExecutor.AbortPolicy() 直接抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() 调节机制,多出的线程回退给调用者线程 哪个线程调用了就给谁去处理
* new ThreadPoolExecutor.DiscardOldestPolicy() 丢弃最老的一个请求(最先入队列的那几个),并尝试再次提交任务
* new ThreadPoolExecutor.DiscardPolicy() 默默丢失不能处理的任务【不予任何处理】
*/
public class MyThreadPool {
public static void main(String[] args) throws InterruptedException {
testMyThreadPool();
}
public static void testMyThreadPool() throws InterruptedException {
ExecutorService myPool = new ThreadPoolExecutor(
2,
8,
2L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 0; i < 10; i++) {
myPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + "\t 在工作");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
myPool.shutdown();
}
ThreadSafe safe = new ThreadSafe();
safe.start();
System.out.println("running");
System.out.println();
Thread.sleep(2000);
safe.interrupt();
}
static class ThreadSafe extends Thread{
@Override
public void run() {
while(!isInterrupted()){
try {
Thread.sleep(1000);
System.out.println("I am alive");
} catch (InterruptedException e) {
e.printStackTrace();
break;//捕捉到中断异常 直接白给
}
}
}
}
}
- 请说明一下Java中都有哪些方式可以启动一个线程?
1.启动线程的方式(详见96题)
继承Thread类,重写run方法。
实现Runnable接口,重写run方法。
实现callable接口,重写call方法启动线程用start方法
参考链接 https://blog.csdn.net/qq_41555783/article/details/102001231
- 请列举一下创建线程的方法,并简要说明一下在这些方法中哪个方法更好,原因是什么?
* 实现Runnable方法,简单,但是传参不方便
* 继承Thread重写run: 传递参数方便,但是java不支持多继承
* 使用FutureTask接口: 可以拿到任务的返回值 通过 实例化对象的get方法
我喜欢实现Runnable方法,因为可以使用lambda表达式方便快捷,而且可以多实现
new Thread(()->{
try {
//
} catch (Exception e) {
e.printStackTrace();
}
},"threadName").start();