Java程序员应该常思考的问题

Java Serial

1. Java基础问答

2. Java Generic

3. Latch VS Barrier

4. AtomicInteger, BlockingQueue and Fork/Join

5. ConcurrentHashMap, Executor, ThreadPool

Java基础问答

1. 进程vs线程

2. JVM同步交互机制介绍

3. Synchornized死锁示例

4. 线程状态转换图

5. static

a. 为什么要有静态变量?为什么要有静态方法?普通的成员变量/方法和静态的成员变量/方法含义上有什么区别?

b. 静态变量存在在什么区域?静态方法存放于什么区域?

c. 私有静态方法有什么用?多线程对静态变量有什么影响?

d. 为什么不推荐把对静态变量的访问放于synchronized 块内?

e. 为什么thread的sleep方法是static的?

6. final

a. final关键字的作用是什么?final关键字对于多线程有什么帮助,会造成什么不良的后果?

b. 为什么在多线程中,对final变量的取值要取两次?

c. 利用final static组合关键字来代替一些枚举类型有什么好处?为什么在很多项目里面,String类型的常量或者模板被统一声明为final static?

7. volatile

a. 为什么 private volatile Integer i = new Integer(2); public int fun() {return i * i;} 是错的?

b. final, volatile一起使用可以吗?

c. happens-before法则是什么?它跟volatile有什么关系

d. volatile是用来修饰变量,而不是方法, 为什么?

e. 为什么volatile无法代替锁?

f. synchronized和volatile的区别是什么?在什么时候可以使用volatile变量?

g. 应用volatile的一般场景可以有哪些?

8. Threadsafe

a. 一句话概括出线程安全的含义

b. 为了保证线程安全,我们需要注意什么事情?

c. 我们可以利用哪些手段来保证线程安全?

d. 利用synchronized直接锁住所有访问,有些时候太重了,有什么简单的办法可以即使用synchronized(因为它简单),有可以大幅提高读写性能?

9. synchronized

a. synchronized锁加在什么地方?对哪些部分的访问会被锁住?JVM如何保证只有一个线程在访问?锁的信息存放在哪里?

b. synchronized是否可以用到变量上?

c. synchronized太重了,有什么其他的锁来替换?偏向锁

d. 什么是死锁, 什么是活锁,如何避免死锁?出现线程死锁如何补救?

e. 为什么构造函数上不能用synchronized?

10. Threadlocal

a. 为什么多线程中使用Threadlocal生命对象可以保证线程安全?

b. Threadlocal如何保证key是唯一的?

c. 线程run方法里面声明的变量为什么不需要使用Threadlocal来保证线程安全?

d. 什么时候需要使用Threadlocal以保证线程安全?

11. fail-fast和fail-safe

a. 为什么不能对java collection一边遍历一边删除?

b. 为什么能对juc可以一边遍历一边删除?

Java generic

1. 泛型是什么

2. 泛型的作用

3. 泛型实现原理:编译器擦除

a. 泛型是编译器中的概念,JVM没有泛型这样的概念,编译器用擦除法实现泛型。

b. 擦除演示

c. 擦除引起的问题及解决方法(参考http://blog.csdn.net/lonelyroamer/article/details/7868820

i. 类型擦除所带来的多态麻烦

ii. 泛型类型变量不能是基本数据类型

iii. 异常中使用泛型的问题

iv. 不能声明参数化类型的数组

v. 泛型类型的实例化

vi. 类型擦除后的冲突(比如equals方法)

d. 其它常见问题

i. 泛型中 参数化类型不考虑继承关系

ii. 参数化类型与原始类型的兼容

4. 存取原则和PECS(Producer Extends, Consumer Super)法则

a. 泛型中的”?”通配符

b. 泛型中的”?”通配符的扩展

c. PECS法则

i. 如果你想从一个数据类型里获取数据,使用 ? extends 通配符

ii. 如果你想把对象写入一个数据结构里,使用 ? super 通配符

iii. 如果你既想存,又想取,那就别用通配符。

5. 为什么Java的泛型伪泛型

Latch and Barrier

1. Latch

a. latch就像一把大锁,所有调用它的await方法的线程都会被阻塞在那里,直到有足够的线程调用它的countDown方法,那些被阻塞的线程才会继续运行。

b. latch最主要的方法有两个,一个是await,另一个是countDown。

i. 当一个线程调用await方法时,该线程会被阻塞起来。

ii. 当一个线程调用countDown方法时,该线程不会被阻塞,会继续运行下去。

iii. latch在实例化的时候,需要属于一个参数用于指明在阻塞线程被唤起之前,需要调用countDown方法的次数。

iv. latch的await还提供了一种方法await(long timeout, TimeUnit unit)用来设定线程的最大阻塞时间。如果线程阻塞时间超过这个时间设置,但countDown方法的调用次数依然没有达到被唤醒的条件,该线程会放弃阻塞,被唤醒。

c. LatchSample.java是一个简单的使用CountDownLatch的例子。

d. latch的实例化对象不可以被反复使用,去掉上面例子中main方法中的倒数第二第三行的注销,执行代码,可以看到当第二次使用时,打印"All end."这一句的方法并没有在其它线程执行完了以后才被执行。

2. barrier

a. barrier类似一个同步所有线程的栅栏,它让所有执行到特定行的线程同时等待,直到等待的线程数目达到一定的数目,所有线程再又同时开始执行。

b. 和latch不同的是,barrier是可以被反复使用的。

c. barrier最主要的就是一个方法await。

i. 当一个线程调用await方法时,该线程会被阻塞起来。

ii. barrier实例化的时候,需要属于一个参数用于指明在阻塞线程被唤起之前,需要调用await方法的次数。当await方法被调用次数达到该次数时,所有被阻塞的线程被唤醒开始运行,此时barrier对await方法调用次数的计数将重新开始。

iii. barrier在实例化的时候,传入一个实现Runnable接口的对象作为唤醒所有线程前被执行的回调函数。这个回调函数是通过run()方法执行的,而不是start()方法,所以它不是一个和其他线程并行执行的线程,这个回调函数的执行会阻塞其他所有被await阻塞的线程,直到这个回调函数被执行完了之后,其他被阻塞线程才能开始执行。所以尽量不要在这个回调函数中去执行计算时间比较长的方法。

d. BarrierSample.java是一个简单的使用CyclicBarrier的例子。在这个例子中,CyclicBarrier的一个实例化对象被连续使用了三次。

e. BarrierSample2.java是一个模拟三个运动员参加跑步比赛的例子。其中包含三个运动员和一个裁判。运动员从热身开始起到最后发完奖品离开,中间有几个特定的时间点需要等到所有的人都达到特定的状态,例如,只有当裁判检查完所有运动员以后才开始跑步,所有人跑完以后再进行统计成绩,统计统计完毕统一发奖等。

3. Practise:有一个很大的整数list, 需要求这个list里面的所有整数的和,实现一段充分利用多线程的代码,要求如下:

a. 自行初始化list

b. 求和计算要利用JAVA多线程实现,要求根据一开始输入的线程个数不同可自行调控线程个数

c. 要能输出最后的计算结果,无论是作为返回值,或者直接输出在console上都可以

d. 该list无法一次性load到内存中

AtomicIntergrer, BlockingQueue and Fork/Join

1. for(;;) {} 和 while(true) {} 的相同点和不同点在哪里?

a. 相同点: 两者都是在做死循环

b. 不同点: 前者只对应一条循环语句,后则需要对一个局部变量进行判断,前者执行效率更高

2. Atomic Integer

a. 当没有原子整数的时候,用什么方式可以使得对同一个整型引用的多线程加减修改是线程安全的?--- 加锁

b. 该做法有什么不好的地方? ----- 容易造成死锁

c. Atomic Integer 比 加锁 要快吗? ----- 加锁会快很多,数值计算比较多的地方最好不要使用Atomic Integer,会影响执行效率

d. Atomic Integer适合用在类似分配唯一ID号的地方

3. BlockingQueue

a. Blocking的好处是什么?

可以让线程阻塞,不占用CPU时间,利用阻塞线程等待消息,减少代码复杂度,提高执行效率

b. array, linked的特点分别是什么?

i. array内部是用固定大小的数组实现的,初始化时必须给定数组固定大小并初始化话这么大一个数组,效率更高

ii. linked内部用链表实现,初始化时可不给定大小,默认大小为MAX_INT,内存使用上更灵活

c. ReentrantLock, Condition, Atomic Integer (capacity)

i. ReentrantLock 通过lock和unlock方法实现加锁解锁

ii. Condition 类似于Object的wait和notify

iii. Atomic Integer 用于记录队列中元素个数,capacity 队列最大容纳元素个数

d. BlockingQueue最主要的接口

i. Take

ii. put

e. BlockingQueue的应用场景可以有哪些?

f. 谨慎包装BlockingQueue

防止死锁,尽可能通过队列来实现信号的传递

g. queue, deque, priority queue

4. Fork & Join

a. RecursiveTask -> ForkJoinTask -> Future

RecursiveAction -> ForkJoinTask -> Future

ForkJoinPool -> AbstractExecutorService

b. RecursiveTask, RecursiveAction : computer, fork, join

ForkJoinPool : submit

c. 调用

forkJoinPool = new ForkJoinPool();

Future<Object> result = forkJoinPool.submit(new RecursiveTask(...)/RecursiveAction(...));

result.get();

d. ForkJoinPool如何实现自适应线程数?

通过调用 System.Runtime中的方法直接获取核数

ConcurrentHashmap, Executor, ThreadPool

1. ConcurrentHashMap

a. 场景

高并发读写,实时性要求很高,IO量大。

b. HashMap为什么不行

写入更新操作涉及到的步骤太多

c. 让步

可以允许读到稍微早一点的过期数据,但是不能读到太久以前的过期数据

还有什么问题不能解决的?

d. 两级结构

Segment, HashEntry

e. 基本操作

i. get, put, remove

ii. size

2. ThreadPool

a. Executor -> ExecutorService -> AbstractExecutorService -> ThreadPoolExecutor

void execute(Runnable command);

b. Executors?

c. ThreadPoolExecutor

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

corePoolSize: 线程池维护线程的最少数量

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime: 线程池维护线程所允许的空闲时间

unit: 线程池维护线程所允许的空闲时间的单位

workQueue: 线程池所使用的缓冲队列

handler: 线程池对拒绝任务的处理策略

threadFactory: ?

d. 停止线程池ThreadPoolExecutor

shutdown()

shutdownNow()

e. 执行

Worker

3. Future and FutureTask

V get() throws InterruptedException, ExecutionException;

V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;

运行超时设定取得结果

4. 该线程池使用哪些范围

5. 哪些地方使用该线程池不合适,使用什么样的方式比较合适

你可能感兴趣的:(java)