目录
多线程篇
1、线程和进程有什么区别?
2、线程实现的方式有几种?
3、高并发、任务执行时间短的业务怎么使用线程池?并发布高、任务执行时间长的业务怎么使用线程池?并发高业务执行时间长的业务怎么使用线程池?
4、如果你提交任务时,线程池队列已满,只是会发生什么?
5、锁的等级:方法锁、对象锁、类锁
6、如果同步块内的线程抛出异常会发生什么?
7、并发线程(concurrency)并发编程(parallellism)有什么区别?
8、如何保证多线程下i++结果正确?
9、一个线程如果出现了运行时异常会怎么样?
10、如何再两个线程之间共享数据?
多线程篇
1、线程和进程有什么区别?
1. 进程是系统进⾏资源分配的基本单位,有独⽴的内存地址空间2. 线程是 CPU 独⽴运⾏和独⽴调度的基本单位,没有单独地址空间,有独⽴的栈,局部变量,寄存器, 程序计数器等。3. 创建进程的开销⼤,包括创建虚拟地址空间等需要⼤量系统资源。4. 创建线程开销⼩,基本上只有⼀个内核对象和⼀个堆栈。5. ⼀个进程⽆法直接访问另⼀个进程的资源;同⼀进程内的多个线程共享进程的资源。6. 进程切换开销⼤,线程切换开销⼩;进程间通信开销⼤,线程间通信开销⼩。7. 线程属于进程,不能独⽴执⾏。每个进程⾄少要有⼀个线程,成为主线程。
2、线程实现的方式有几种?
1. 继承 Thread 类,重写 run ⽅法2. 实现 Runnable 接⼝,重写 run ⽅法,实现 Runnable 接⼝的实现类的实例对象作为 Thread 构造函数的 target3. 实现Callable接⼝通过FutureTask包装器来创建Thread线程4. 通过线程池创建线程public class ThreadDemo03 {public static void main(String[] args) {Callable<Object> oneCallable = new Tickets<Object>();FutureTask<Object> oneTask = new FutureTask<Object>(oneCallable);Thread t = new Thread(oneTask);System.out.println(Thread.currentThread().getName());t.start();}}class Tickets<Object> implements Callable<Object>{//重写call⽅法@Overridepublic Object call() throws Exception {// TODO Auto-generated method stubSystem.out.println(Thread.currentThread().getName()+"-->我是通过实现Callable接⼝通过FutureTask包装器来实现的线程"return null;}}
3、高并发、任务执行时间短的业务怎么使用线程池?并发布高、任务执行时间长的业务怎么使用线程池?并发高业务执行时间长的业务怎么使用线程池?
1. ⾼并发、任务执⾏时间短的业务:线程池线程数可以设置为 CPU 核数 +1 ,减少线程上下⽂的切换。2. 并发不⾼、任务执⾏时间⻓的业务要区分开看:a. 假如是业务时间⻓集中在 IO 操作上,也就是 IO 密集型的任务,因为 IO 操作并不占⽤ CPU ,所以不要让所有的 CPU闲下来,可以加⼤线程池中的线程数⽬,让CPU 处理更多的业务b. 假如是业务时间⻓集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)⼀样吧,线程池中的线程数设置得少⼀些,减少线程上下⽂的切换3. 并发⾼、业务执⾏时间⻓,解决这种类型任务的关键不在于线程池⽽在于整体架构的设计,看看这些业务⾥⾯某些数据是否能做缓存是第⼀步,增加服务器是第⼆步,⾄于线程池的设置,设置参考(2)。最后,业务执⾏时间⻓的问题,也可能需要分析⼀下,看看能不能使⽤中间件对任务进⾏拆分和解耦。
4、如果你提交任务时,线程池队列已满,只是会发生什么?
1、如果你使⽤的 LinkedBlockingQueue ,也就是⽆界队列的话,没关系,继续添加任务到阻塞队列中等待执⾏,因为LinkedBlockingQueue可以近乎认为是⼀个⽆穷⼤的队列,可以⽆限存放任务;2 、如果你使⽤的是有界队列⽐⽅说 ArrayBlockingQueue 的话,任务⾸先会被添加到 ArrayBlockingQueue 中,ArrayBlockingQueue满了,则会使⽤拒绝策略 RejectedExecutionHandler 处理满了的任务,默认是 AbortPolicy。
5、锁的等级:方法锁、对象锁、类锁
1. ⽅法锁( synchronized 修饰⽅法时)a. 通过在⽅法声明中加⼊ synchronized 关键字来声明 synchronized ⽅法。b. synchronized ⽅法控制对类成员变量的访问:c. 每个类实例对应⼀把锁,每个 synchronized ⽅法都必须获得调⽤该⽅法的类实例的锁⽅能执⾏,否则所属线程阻塞,⽅法⼀旦执⾏,就独占该锁 ,直到从该⽅法返回时才将锁释放,此后被阻塞的线程⽅能获得该锁,重新进⼊可 执⾏状态。这种机制确保了同⼀时刻对于每⼀个类实例,其所有声明为 synchronized 的成员函数中⾄多只有⼀个处于可执⾏状态,从⽽有效避免了类成员变量的访问冲突。2. 对象锁( synchronized 修饰⽅法或代码块)a. 当⼀个对象中有 synchronized method 或 synchronized block 的时候调⽤此对象的同步⽅法或进⼊其同步区域时,就必须先获得对象锁。如果此对象的对象锁已被其他调⽤者占⽤,则需要等待此锁被释放。(⽅法锁也是对象锁)b. java 的所有对象都含有 1 个互斥锁,这个锁由 JVM ⾃动获取和释放。线程进⼊ synchronized ⽅法的时候获取该对象的锁,当然如果已经有线程获取了这个对象的锁,那么当前线程会等待;synchronized ⽅法正常返回或者抛异常⽽终⽌,JVM 会⾃动释放对象锁 。这⾥也体现了⽤ synchronized 来加锁的 1 个好处,⽅法抛异常的时候,锁仍然可以由JVM来⾃动释放。3. 类锁( synchronized 修饰静态的⽅法或代码块)a. 由于⼀个 class 不论被实例化多少次,其中的静态⽅法和静态变量在内存中都只有⼀份。所以,⼀旦⼀个静态的⽅法被申明为synchronized 。此类所有的实例化对象在 调⽤此⽅法 ,共⽤同⼀把锁,我们称之为类锁。4. 对象锁是⽤来控制实例⽅法之间的同步,类锁是⽤来控制静态⽅法(或静态变量互斥体)之间的同步。
6、如果同步块内的线程抛出异常会发生什么?
synchronized⽅法正常返回或者抛异常⽽终⽌,JVM会⾃动释放对象锁
7、并发线程(concurrency)并发编程(parallellism)有什么区别?
1. 解释⼀:并⾏是指两个或者多个事件在同⼀时刻发⽣;⽽并发是指两个或多个事件在同⼀时间间隔发⽣。2. 解释⼆:并⾏是在不同实体上的多个事件,并发是在同⼀实体上的多个事件。3. 解释三:在⼀台处理器上“同时”处理多个任务,在多台处理器上同时处理多个任务。如 hadoop 分布式集群。所以并发编程的目标是充分的利用处理器的每一个核,以达到最高的处理性能。
8、如何保证多线程下i++结果正确?
1. volatile 只能保证你数据的可⻅性,获取到的是最新的数据,不能保证原⼦性;2. ⽤ AtomicInteger 保证原⼦性。3. synchronized 既能保证共享变量可⻅性,也可以保证锁内操作的原⼦性。
9、一个线程如果出现了运行时异常会怎么样?
1、如果这个异常没有被捕获的话,这个线程就停⽌执⾏了。2.、 另外重要的⼀点是:如果这个线程持有某个对象的监视器,那么这个对象监视器会被⽴即释放。
10、如何再两个线程之间共享数据?
通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进⾏唤起和等待,⽐⽅说阻塞队列BlockingQueue就是为线程之间共享数据⽽设计的。
1、 卖票系统:
public class Ticket implements Runnable {private int ticket = 10;public void run() {while (ticket > 0) {ticket--;System.out.println("当前票数为:" + ticket);}} }package 多线程共享数据;public class SellTicket {public static void main(String[] args) {Ticket t = new Ticket();new Thread(t).start();new Thread(t).start();} }
2、银行存取款
public class MyData {private int j = 0;public synchronized void add() {j++;System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);}public synchronized void dec() {j--;System.out.println("线程" + Thread.currentThread().getName() + "j为:" + j);}public int getData() {return j;} }public class AddRunnable implements Runnable {MyData data;public AddRunnable(MyData data) {this.data = data;}public void run() {data.add();} }public class DecRunnable implements Runnable {MyData data;public DecRunnable(MyData data) {this.data = data;}public void run() {data.dec();} }public class TestOne {public static void main(String[] args) {MyData data = new MyData();Runnable add = new AddRunnable(data);Runnable dec = new DecRunnable(data);for (int i = 0; i < 2; i++) {new Thread(add).start();new Thread(dec).start();}} }