一些问题:说说wait(),sleep(),yield()这3个方法、notify(),notifyAll()各自可能出现的问题、说说copyOnWrite机制以及适用场景、说说AQS、阻塞队列的实现原理、线程池的4种实现,原理。
与线程相关的几个基本方法 :
wait():Object类的final native方法,抛中断异常。在一个线程中调用某个对象A的wait()方法,当前线程立即放弃对象锁,转到阻塞队列,当在另一个线程中调用A对象的notify()或notifyAll()时该线程被唤醒,准备从新获取锁。
sleep():Thread类的静态native方法,抛中断异常。休眠。在一个线程中调用sleep()方法,当前线程休眠指定时间后直接进入就绪状态,不会放弃对象锁。
yield():Thread类的静态native方法。屈服/让步。在一个线程中调用yield()方法,当前线程立即放弃CPU直接进入就绪状态,等待被CPU调度,不会放弃对象锁。
notify():Object类的final native方法。在一个线程中调用一个某个对象A的notify()方法,从等待A的阻塞队列中所有线程中随机唤醒一个,可能造成信号丢失问题;当前线程不会立即放弃对象锁;需要通过退出同步代码块(代码执行完),或者调用wait()显示放弃对象锁。
notifyAll():Object类的final native方法。在一个线程中调用一个某个对象A的notifyAll()方法,会唤醒所有等待A的线程(设N个),然后这N个线程去竞争同一个锁,最多只有一个线程可以得到锁,其它线程又回到阻塞状态(之前是等待A对象的通知,现在是竞争不到锁而阻塞,造成过早唤醒)。这意味着一次notifyAll()操作可能带来大量的上下文切换(N比较大),同时有大量竞争锁的请求。这对于频繁的唤醒操作而言性能上是一种灾难。可以通过ReentrantLock与Condition实现选择性通知。
sleep()会给其它线程运行的机会,不考虑线程的优先级;yield()只会给同优先级或更高优先级的线程运行的机会,当然也包括自己。sleep()会抛出异常,yield()不抛;sleep()比yield()具有更好的移植性,不能依靠yield()来提高程序的并发性能。
join():在一个线程里调用另一个线程对象的join()方法,会导致当前线程阻塞。不要混淆线程执行体和线程对象。看Thread类源码知,join()通过wait()实现,故join()会释放锁。
public final synchronized void join(long millis)
throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {//当线程对象仍是活着的时候
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
若在A线程中调用B线程的join()方法,只有当B线程执行完毕时,A线程才能继续执行(第一个分支)。
isAlive() :检测线程对象的状态,当前线程对象已经start()还没die就算活着
CopyOnWrite写时复制机制
写线程在副本上写,这样在写的同时读线程还可以读,使用于读多写少的场景。典型实现有copyOnWriteArraylist数组。
抽象队列同步器AQS
抽象队列同步器,它定义了一套同步框架,许多同步类都依赖于它,如常用的ReenterLock/ReenterReadWriteLock/Semaphore/CountDownLatch。
1.维护了一个volatile int state(代表同步状态)以及一个CLH队列(FIFO)和若干个Condition队列。AQS提供了三个核心的final方法实现对state变量的管理:getState()获取当前同步状态、setState()设置当前同步状态、compareAndSetState()通过CAS更新当前同步状态。
2.AQS采用了模板方法模式,有5个protected方法是延迟给子类实现的,子类可以选择只实现其中的部分方法。子类被推荐定义为自定义同步组件的静态内部类。
3.原理图。
4.Condition的实现原理。Condition依赖lock实现等待/通知机制,Condition对象是由lock对象创建出来的(lock.new Condition)。AQS的ConditionObject内部类是Condition接口的实现类。
阻塞队列
阻塞队列是一个支持两个附加操作的队列(FIFO)。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。试图向已满队列中添加元素会导致线程阻塞,试图向空队列中提取元素会导致线程阻塞。 JDK7提供了7个阻塞队列实现类。
顶层接口:public interface BlockingQueue<E> extends Queue<E> {...}
子接口: public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {...}
ThreadLocal类
为每个线程保存自己的一个私有属性。
线程池
Executors是线程池的工厂类,方便快捷的创建很多线程池。主要有4类线程池。
创建一个只含一个线程的线程池Executors.newSingelThreadExecutor();
创建一个固定大小的线程池Executors.newFixedThreadPool();
创建一个大小的伸缩的线程池Executors.newCachedThreadPool();
创建一个指定延迟时间或可周期性执行任务的线程池Executors.newScheduledThreadPool()。
//内部代码实现:
public ThreadPoolExecutor(int corePoolSize,//核心池大小
int maximumPoolSize,//线程池临时允许最大线程数
long keepAliveTime,//临时线程最多多久没有执行任务就会被销毁
TimeUnit unit,
BlockingQueue<Runnable> workQueue,/线程等待池,即待执行的任务序列
ThreadFactory threadFactory,//创建线程的工厂类
RejectedExecutionHandler handler) {//当任务序列满时,采用何种方式拒绝一个任务
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池任务拒绝策略RejectedExecutionHandler:
1.AbortPolicy:直接抛出异常。
2.CallerRunsPolicy:直接让调用者所在线程来运行任务。
3.DiscardOldestPolicy:丢掉队列里等待最久的一个任务,并尝试执行该任务。
4.DiscardPolicy:不处理,直接丢弃掉。
线程池的好处:
1.重复利用已经创建的线程,降低线程创建和线程销毁的开销。
2.提高响应速度,减少了等待线程创建的时间
3.提高了线程的可管理性。使用线程池可以对线程进行统一的 分配,调优和监控。
对于CPU密集型任务,配置尽量小的线程数Ncpu+1;对于IO密集型任务,需要cpu的时间比较少,大部分时间在IO,可以配置尽量多的线程数2*Ncpu。
Fork/Join框架
Future任务机制与FutureTask:
API:
public class FutureTask<V> implements RunnableFuture<V> {...}//FutureTask类实现了RunnableFuture接口
public interface RunnableFuture<V> extends Runnable, Future<V> {//RunnableFuture继承了Runnable接口和Future接口
void run();
}
Fork/Join将一个大任务Fork成若干个相互独立,不相依赖的子问题,知道所有子问题得到解决。
//使线程具有有序性
//顺序打印A B C D A B C D...
public class WaitNotifyDemo {
public static void main(String[] args) {
int totalNumber=4;
final Mylock sharedObj=new Mylock();
for(int i=0;i<totalNumber;i++){
new MyThreadV(sharedObj, (char)('A'+i), i, totalNumber).start();;
}
}
}
class MyThreadV extends Thread{
private Mylock sharedObj;
private char mark;
private int number;//该线程对象序号
private int total;//总序号数
public MyThreadV(Mylock obj,char mark,int number,int total) {
this.sharedObj=obj;
this.mark=mark;
this.number=number;
this.total=total;
}
@Override
public void run() {
while(true){
try {
sleep(200);
synchronized (sharedObj) {
if(sharedObj.count%total==number){
System.out.print(mark+" ");
if(sharedObj.count%total==total-1){
System.out.println();
}
sharedObj.count++;
sharedObj.notifyAll();//key1
}else{
sharedObj.wait();//key2没有轮到该线程,放弃处理机,释放对象锁,
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Mylock{
public int count;
public Mylock() {
this.count=0;
}
}
public class CountDownLaunchDemo {
public static void main(String[] args) throws InterruptedException {
int subTask=9;
CountDownLatch latch=new CountDownLatch(subTask);//key1
for(int i=0;i<subTask;i++){
new MyThread(latch, i).start();
}
latch.await();//key3
System.out.println("所有子任务结束,主任务开始继续执行");
}
}
class MyThread extends Thread{
CountDownLatch latch;
int i;
public MyThread(CountDownLatch latch,int i){
this.latch=latch;
this.i=i;
}
@Override
public void run() {
try {
System.out.println("子任务"+i+"开始");
Thread.sleep(1000);//模拟子任务工作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子任务"+i+"完毕");
latch.countDown();//key2
}
}
public class CyclicBarrierDemo {
public static void main(String[] args) {
int task=9;
CyclicBarrier cyclicBarrier=new CyclicBarrier(task);
for(int i=0;i<task;i++){
new MythreadII(cyclicBarrier, i).start();
}
}
}
class MythreadII extends Thread{
CyclicBarrier cyclicBarrier;
int index;//任务编号
public MythreadII(CyclicBarrier cyclicBarrier,int i) {
this.cyclicBarrier=cyclicBarrier;
this.index=i;
}
@Override
public void run() {
try {
Thread.sleep(1000);
System.out.println("子任务:"+index+"第一阶段完成...");
cyclicBarrier.await();//直到所有部分都已经对这个屏障执行了await()操作
System.out.println("子任务:"+index+"开始第二阶段");
} catch (Exception e) {
e.printStackTrace();
}
}
}
public class SemaphoreDemo {
public static void main(String[] args) {
//有10个人要求进行服务,服务器最多只能同时服务3个人
int maxCapcity=3;
int task=10;
Semaphore semaphore=new Semaphore(maxCapcity);//key1,指定许可量
for(int i=0;i<task;i++){
new MyThreadI(semaphore, i).start();
}
}
}
class MyThreadI extends Thread{
Semaphore semaphore;
int index;//编号
public MyThreadI(Semaphore semaphore,int i){
this.semaphore=semaphore;
this.index=i;
}
@Override
public void run() {
System.out.println("用户"+index+"连接上服务器:");
try {
semaphore.acquire();//key1 请求获取一个许可,获取前将一直阻塞
System.out.println("用户"+index+"开始访问后台程序");
Thread.sleep(1000);//模拟服务
System.out.println("用户"+index+"访问结束。");
semaphore.release();//归还一个许可
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
其它:
模板方法模式:在父类中定义算法骨架,将具体实现延迟到子类实现。
关于AQS与Condition,看AQS类源码。
参:
http://blog.csdn.net/qq_17438907/article/details/49049051
http://blog.csdn.net/yangdongchuan1995/article/details/78578337
http://ifeve.com/java-blocking-queue/
https://www.cnblogs.com/lnlvinso/p/4753554.html
http://ifeve.com/abstractqueuedsynchronizer-use/#more-18899
https://www.cnblogs.com/waterystone/p/4920797.html