JDK并发包

synchronized扩展:重入锁

使用java.util.concurrent.locks.ReentrantLock类来实现

重入锁必须手动加锁,释放锁,加几次锁,就要释放几次锁,多放,报异常,少放,还有锁

public class ReenterLock implements Runnable{

	public static ReentrantLock lock = new ReentrantLock();
	public static int i = 0;
	@Override
	public void run(){
		for(int j=0;j<10000000;j++){
			lock.lock();
			try {
				i++;
			} finally{
				lock.unlock();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		ReenterLock t = new ReenterLock();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();t2.start();
		t1.join();t2.join();
		System.out.println(i);
	}
}

中断响应

对于synchronized,如果一个线程在等待锁,只能获得锁继续执行,或者保持等待。重入锁可以线程被中断,就是等待过程中,程序可以根据需要取消锁的请求

lockInterruptibly()可以对中断进行响应的锁申请动作,在等待锁的过程中,可以响应中断

public class IntLock implements Runnable {
	public static ReentrantLock lock1 = new ReentrantLock();
	public static ReentrantLock lock2 = new ReentrantLock();
	int lock;
	public IntLock(int lock) {
		this.lock = lock;
	}
	@Override
	public void run() {
		try {
			if (lock == 1) {
				lock1.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				lock2.lockInterruptibly();
			} else {
				lock2.lockInterruptibly();
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				lock1.lockInterruptibly();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}finally {
			if(lock1.isHeldByCurrentThread())
				lock1.unlock();
			if(lock2.isHeldByCurrentThread()){
				lock2.unlock();
			}
			System.out.println(Thread.currentThread().getId()+":线程退出");
		}
	}
	public static void main(String[] args) throws InterruptedException {
		IntLock r1 = new IntLock(1);//先占用lock1,再占用lock2
		IntLock r2 = new IntLock(2);//先占用lock2,再占用lock1
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		t1.start();t2.start();
		//主程序休眠,俩线程死锁,
		Thread.sleep(1000);
		//中断其中一个线程,t2被中断,会放弃对lock1的申请,同时释放lock2
		t2.interrupt();
	}
}

上述代码,中断后,俩线程都退出,t1完成工作,t2放弃任务,释放资源

锁申请等待限时

用tryLock()进行一次限时的等待

public class TimeLock implements Runnable {

	public static ReentrantLock lock = new ReentrantLock();
	@Override
	public void run() {
		try {
			if(lock.tryLock(5, TimeUnit.SECONDS)){
				Thread.sleep(6000);
			}else{
				System.out.println("get lock failed");
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally {
			if(lock.isHeldByCurrentThread())
				lock.unlock();
		}
	}
	public static void main(String[] args) {
		TimeLock t = new TimeLock();
		Thread t1 = new Thread(t);
		Thread t2 = new Thread(t);
		t1.start();
		t2.start();
	}
}

tryLock()可以接收俩个参数,一个为等待时长,一个为计时单位

也可以不带参数执行,不会等待

public class TryLock implements Runnable {
	public static ReentrantLock lock1 = new ReentrantLock();
	public static ReentrantLock lock2 = new ReentrantLock();
	int lock;
	public TryLock(int lock) {
		this.lock = lock;
	}

	@Override
	public void run() {
		if(lock ==1){
			while(true){
				if(lock1.tryLock()){
					try {
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						if(lock2.tryLock()){
							try {
								System.out.println(Thread.currentThread().getId()+":my job done");
								return;
							} catch (Exception e) {
								lock2.unlock();
							}
						}
					} finally{
						lock1.unlock();
					}
				}
			}
		}else{
			while(true){
				if(lock2.tryLock()){
					try {
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
						if(lock1.tryLock()){
							try {
								System.out.println(Thread.currentThread().getId()+":my job done");
								return;
							} catch (Exception e) {
								lock1.unlock();
							}
						}
					} finally{
						lock2.unlock();
					}
				}
			}
		}
	}
	public static void main(String[] args) {
		TryLock r1 = new TryLock(1);
		TryLock r2 = new TryLock(2);
		Thread t1 = new Thread(r1);
		Thread t2 = new Thread(r2);
		t1.start();
		t2.start();
	}
}

只看到一个输出

公平锁

synchronized不公平锁,重入锁可以公平

有一个构造函数,fair为true,锁是公平的,实现公平锁要求系统维护一个有序队列,因此成本较高,性能低下

    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
public class FairLock implements Runnable {
	public static ReentrantLock fairLock = new ReentrantLock(true);
	@Override
	public void run() {
		while (true) {
			try {
				fairLock.lock();
				System.out.println(Thread.currentThread().getName() + " 获得锁");

			} finally {
				fairLock.unlock();
			}
		}
	}
	public static void main(String[] args) {
		FairLock r1 = new FairLock();
		new Thread(r1, "Thread_t1").start();
		new Thread(r1, "Thread_t2").start();
	}
}

公平锁t1,t2交替输出
对ReentrantLock重要方法整理:

lock()获得锁,锁被占用,则等待

lockInterruptibly()获得锁,但优先响应中断

tryLock()尝试获得锁,成功,返回true,失败返回false,不等待,立刻返回

tryLock(long time,TimeUnit unit)在给定时间尝试获得锁

unLock释放锁

重入锁的实现主要集中在java层面,主要包含三个元素,原子状态,等待队列,阻塞原语用来挂起和恢复线程

Condition条件

与wait和notify作用大致一样Condition与重入锁关联

通过lock接口的Condition newCondition()生成与当前重入锁绑定的Condition实例,利用Condition对象,可以让线程在合适的时间等待,或在某一时刻得到通知执行

Condition接口实例

void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();

await()方法使当前线程等待,并释放锁,当其他线程使用signal()或者signalAll(),线程重新获得锁并继续执行,或者当线程中断时,也能跳出等待。

awaitUninterruptibly()与await()差不多,但不会在等待过程中响应中断

singal()唤醒一个等待中的线程,singalAdd()唤醒所有在等待的线程

public class ReenterLockCondition implements Runnable{

	public static ReentrantLock lock = new ReentrantLock();
	//通过lock生成一个与之绑定的Condition对象
	public static Condition condition = lock.newCondition();
	@Override
	public void run(){
		try {
			lock.lock();
			//线程在Condition对象上等待
			condition.await();
			System.out.println("Thread is going on");
		} catch (Exception e) {
			
		}finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		ReenterLockCondition t1 = new ReenterLockCondition();
		new Thread(t1).start();
		Thread.sleep(2000);
		lock.lock();
		//主线程发出通知,等待在Condition上的线程可以继续执行
		condition.signal();
		lock.unlock();
	}
}

线程使用Condition.await(),要求线程持有相关的重入锁,在await调用后释放锁,signal也一样要先获得锁,在调用,随机唤醒Condition对象中的一个等待线程,然后释放锁

允许多个线程同时访问:信号量(Semaphore)

无论内部锁还是重入锁,一次只允许一个线程访问一个资源。信号量可以指定多个线程,同时访问某一个资源
主要有以下构造方法

public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

第二个参数指定是否公平
主要逻辑方法

public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()

acquire()方法尝试获取一个准入的许可,无法获得,线程就会等待,acquireUninterruptibly()和acquire()类似,但不影响中断,tryAcquire()尝试获得一个许可,成功返回true,不等待,release()用于线程访问资源结束后,释放一个许可,使其他等待许可线程可以进行资源访问

public class SemapDemo implements Runnable {
	//同时可以五个线程进入临界区管理代码
	final Semaphore semp = new Semaphore(5);
	@Override
	public void run() {
		try {
			semp.acquire();
			//模拟耗时操作
			System.out.println(Thread.currentThread().getId()+":done");
			semp.release();
		} catch (Exception e) {
		}
	}
	public static void main(String[] args) {
		ExecutorService exec = Executors.newFixedThreadPool(20);
		final SemapDemo demo = new SemapDemo();
		for(int i =0;i<20;i++){
			exec.submit(demo);
		}
	}
}


ReadWriteLock读写锁

系统中,读操作的次数远远大于写,读写锁可以提高性能

public class ReadWriteLockDemo {

	private static Lock lock = new ReentrantLock();
	private static ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	private static Lock readLock = readWriteLock.readLock();
	private static Lock writeLock = readWriteLock.writeLock();
	private int value;
	public Object handleRead(Lock lock) throws InterruptedException{
		try {
			lock.lock();//模拟读操作
			Thread.sleep(1000);//读操作耗时越多,读写锁的优势越明显
			return value;
		} finally {
			lock.unlock();
		}
	}
	public void HandleWrite(Lock lock,int index) throws InterruptedException{
		try {
			lock.lock();//模拟写操作
			Thread.sleep(1000);
			value = index;
		} finally {
			lock.unlock();
		}
	}
	public static void main(String[] args) {
		final ReadWriteLockDemo demo = new ReadWriteLockDemo();
		Runnable readRunnable = new Runnable() {
			@Override
			public void run() {
				try {
					demo.handleRead(readLock);
//					demo.handleRead(lock);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Runnable writeRunnable = new Runnable() {
			
			@Override
			public void run() {
				try {
					demo.HandleWrite(writeLock, new Random().nextInt());
//					demo.HandleWrite(lock, new Random().nextInt());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		for(int i=0;i<18;i++){
			new Thread(readRunnable).start();
		}
		for(int i=0;i<2;i++){
			new Thread(writeRunnable).start();
		}
	}
}

使用读写分离,因此,读线程完全并行,写会阻塞读,写线程实际是串行的,使用重入锁,所有的读和写线程都必须相互等待,耗时长
倒计时器:CountDownLatch

控制线程等待,让某个线程等待直到倒计时结束,再开始执行

构造器接收一个整数作为参数,即当前这个计数器的计数个数

    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
public class CountDownLatchDemo implements Runnable{
	//计数数量为10表示需要有10个线程完成任务,等待在CountDownLatch上的线程才能继续执行
	static final CountDownLatch end = new CountDownLatch(10);
	static final CountDownLatchDemo demo = new CountDownLatchDemo();
	@Override
	public void run(){
		//模拟检查任务
		try {
			Thread.sleep(new Random().nextInt(10)*1000);
			System.out.println("check complete");
			//一个线程执行完,到计数器减少1
			end.countDown();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) throws InterruptedException {
		ExecutorService exec  = Executors.newFixedThreadPool(10);
		for(int i=0;i<11;i++){
			exec.submit(demo);
		}
		//等待检查,主线程等待所有的检查任务全部完成,主线程继续执行
		end.await();
		System.out.println("Fire!");
		exec.shutdown();
	}
}

循环 栅栏:CyclicBarrier
也可以实现线程间的计数等待CyclicBarrier可以接收一个参数作为barrierAction,即当计数器一次计数完成后,系统会执行的动作

计数器设置为10,凑齐10个后,计数器归0,接着凑齐下一批10个线程

public class CyclicBarrierDemo {

	public static class Soldier implements Runnable{
		private String soldier;
		private final CyclicBarrier cyclic;
		Soldier(CyclicBarrier cyclic,String soldierName) {
			this.cyclic = cyclic;
			this.soldier = soldierName;
		}
		@Override
		public void run(){
			try {
				//等待所有士兵到齐
				cyclic.await();
				doWork();
				//等待所有士兵完成工作
				cyclic.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
		void doWork() {
			try {
				Thread.sleep(Math.abs(new Random().nextInt()%10000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(soldier+":任务完成");
		}
	}
	public static class BarrierRun implements Runnable{
		boolean flag;
		int N;
		public BarrierRun(boolean flag, int n) {
			this.flag = flag;
			N = n;
		}
		@Override
		public void run(){
			if(flag){
				System.out.println("司令:+[士兵"+N+"个,任务完成]");
			}else{
				System.out.println("司令:+[士兵"+N+"个,集合完毕]");
				flag =true;
			}
		}
	}
	public static void main(String[] args) {
		final int N = 10;
		Thread[] allSoldier = new Thread[N];
		boolean flag = false;
		//创建实例,计数器设置为10,计数器达到指标,执行BarrierRun的run方法,没管过士兵线程会执行Soldier的run方法
		CyclicBarrier cyclic = new CyclicBarrier(N, new BarrierRun(flag, N));
		//设置屏障点,主要是为了执行这个方法
		System.out.println("集合队伍!");
		for(int i=0;i<N;++i){
			System.out.println("士兵"+i+"报道!");
			allSoldier[i] = new Thread(new Soldier(cyclic, "士兵"+i));
			allSoldier[i].start();
		}
	}
}

集合队伍!
士兵0报道!
士兵1报道!
士兵2报道!
士兵3报道!
士兵4报道!
士兵5报道!
士兵6报道!
士兵7报道!
士兵8报道!
士兵9报道!
司令:+[士兵10个,集合完毕]
士兵9:任务完成
士兵7:任务完成
士兵2:任务完成
士兵8:任务完成
士兵5:任务完成
士兵0:任务完成
士兵1:任务完成
士兵4:任务完成
士兵6:任务完成
士兵3:任务完成
司令:+[士兵10个,任务完成]

线程阻塞工具类:LockSupport

可以在任何位置让线程阻塞,和Thread.suspend()比较,弥补由于resume()在前发生,导致线程无法继续执行的异常,不需要先获取某个对象的锁

静态方法park()可以阻塞当前线程

public class LockSupportDemo {

	public static Object u= new Object();
	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
	static ChangeObjectThread t2 = new ChangeObjectThread("t2");

	public static class ChangeObjectThread extends Thread{
		public ChangeObjectThread(String name){
			super.setName(name);
		}
		@Override
		public void run(){
			//对临界区的访问
			synchronized (u) {
				System.out.println("in "+getName());
				LockSupport.park();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(100);
		t2.start();
		LockSupport.unpark(t1);
		LockSupport.unpark(t2);
		t1.join();
		t2.join();
	}
}

上述代码正常执行

LockSupport类使用类似信号量的机制,为每个线程准备一个许可,许可可用,park()函数立即返回,将许可变为不可用,许可不可用,就会阻塞,unpark()使许可变为可用,许可只能有一个,即使unpark()发生在park()之前,可以使下一个park操作立即返回

支持中断影响

public class LockSupportIntDemo {
	public static Object u= new Object();
	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
	static ChangeObjectThread t2 = new ChangeObjectThread("t2");

	public static class ChangeObjectThread extends Thread{
		public ChangeObjectThread(String name){
			super.setName(name);
		}
		@Override
		public void run(){
			//对临界区的访问
			synchronized (u) {
				System.out.println("in "+getName());
				LockSupport.park();
				if(Thread.interrupted()){
					System.out.println(getName()+"被中断了");
				}
			}
			System.out.println(getName()+"执行结束");
		}
	}
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(100);
		t2.start();
		t1.interrupt();
		LockSupport.unpark(t2);
	}
}

in t1
t1被中断了
t1执行结束
in t2
t2执行结束

线程复用:线程池

Executor框架提供了各种类型的线程池

主要以下几种工厂方法

public static ExecutorService newFixedThreadPool(int nThreads)
public static ExecutorService newSingleThreadExecutor()
public static ExecutorService newCachedThreadPool()
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)

第一个返回固定数量的线程池,提交新任务,有空闲线程,立即执行,没有则暂存到一个队列中,有线程空闲,就处理队列中的

第二个返回只有一个线程的线程池,多余任务会保存在一个任务队列中,线程空闲,按先入先出的顺序执行

第三返回可根据实际情况调整线程数量的线程池,有空闲线程可以复用,优先使用可复用的线程,所有线程都工作,有新任务,就会创建新的线程处理任务,所有线程在当前任务执行完毕后,返回线程池复用

第四个线程池大小为1,有在指定时间执行某功能的能力

第五个可以指定线程数量

固定大小的线程池

public class ThreadPoolDemo {

	public static class MyTask implements Runnable{
		@Override
		public void run(){
			System.out.println(System.currentTimeMillis()+":Thread ID:"+Thread.currentThread().getId());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		MyTask task = new MyTask();
		ExecutorService es = Executors.newFixedThreadPool(5);
		for(int i =0;i<10;i++){
			es.submit(task);
		}
	}
}

计划任务
newScheduledThreadPool(),返回ScheduledExecutorService,可以根据时间对线程进行调度

public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                  long initialDelay,
                                                  long period,
                                                  TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                     long initialDelay,
                                                     long delay,
                                                     TimeUnit unit);

FixedRate,任务调度频率一定,以上个任务开始执行为起点,之后的周期时间,调度下一次任务;FixDelay在上个任务结束后,经过延期后进行任务调度

public class ScheduledExecutorServiceDemo {
	public static void main(String[] args) {
		ScheduledExecutorService ses = Executors.newScheduledThreadPool(10);
		ses.scheduleAtFixedRate(new Runnable() {
			
			@Override
			public void run() {
				try {
					Thread.sleep(8000);
					System.out.println(System.currentTimeMillis()/1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				
			}
		}, 0, 2, TimeUnit.SECONDS);
	}
}

休息时间太长,任务会在上个任务结束后立即执行,如果是Delay,会在休息时间基础上加上调度周期时间

任务出现异常,后续所有执行都会中断

核心线程池的内部实现
内部实际上都是ThreadPoolExecutor实现

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

ThreadPoolExecutor最重要的构造函数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize,指定核心线程数量;

maximumPoolSize,最大线程数量;

KeepAliveTime,线程池线程数量超过核心线程数量时,多余的空闲线程存活的时间;

unit,KeepAliveTime的单位;

workQueue,任务队列,被提交尚未执行的任务;

threadFactory,线程工厂,用于创建线程,默认即可;

handler,拒绝策略,任务太多来不及处理,如何拒绝任务

workQueue指被提交但未执行的任务队列,是BlockingQueue接口的对象,仅用来存放Runnable对象,有以下几种BlockingQueue

直接提交队列:由SynchronousQueue提供,没有容量,每一个插入操作都要等待一个相应的删除操作,每一个删除操作都要等待相应的插入操作,提交的任务不会真实的保存,总是将新任务提交给线程执行,没有空闲线程,尝试创建新线程,进程数量大于最大值,执行拒绝策略,通常设置最大的maximumPoolSize

有界的任务队列:使用ArrayBlockingQueue,构造函数必须带一个容量参数,表示该队列最大容量 public ArrayBlockingQueue(int capacity),有新任务执行,实际线程小于核心线程数,优先创建新线程,大于则将新任务加入等待队列,等待队列满了,在总线程数不大于最大线程数的情况下,创建新线程,大于,执行拒绝策略

无界任务队列:通过LinkedBlockingQueue实现,除非系统资源耗尽,不存在任务入队失败情况,此时最大线程数失效

优先任务队列:带有执行优先级的队列,通过PriorityBlockingQueue,控制任务的执行先后顺序,特殊的无界队列

public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

第五行workerCountOf()获取线程总数,小于核心线程数将任务通过addWorker()直接调度执行,否则第10行workQueue。offer()进入等待队列,失败执行第17行,将任务直接交给线程池,当前线程数达到最大线程数,执行18行拒绝策略

拒绝策略

四种拒绝策略

AbortPolicy策略,直接抛出异常,阻止系统正常工作

CallerRunsPolicy策略,线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务,任务提交线程的性能会急剧下降

DiscardOledestPolicy策略,丢弃最老的一个请求,也就是即将执行的任务,并尝试再次提交当前的任务

DiscardPolicy策略,丢弃无法处理的任务,不予处理

这些策略实现了RejectedExecutionHandler接口,也可以自己扩充此接口

public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
public class RejectThreadPoolDemo {

	public static class MyTask implements Runnable{
		@Override
		public void run(){
			System.out.println(System.currentTimeMillis()+":Thread ID:"+Thread.currentThread().getId());
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) throws InterruptedException {
		MyTask task = new MyTask();
		ExecutorService es = new ThreadPoolExecutor(5, 5, 
				0L, TimeUnit.MILLISECONDS, 
				new LinkedBlockingQueue<Runnable>(10), 
				Executors.defaultThreadFactory(), new RejectedExecutionHandler() {					
					@Override
					public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
						System.out.println(r.toString()+" is discard");
						
					}
				});
		for(int i =0;i<Integer.MAX_VALUE;i++){
			es.submit(task);
			Thread.sleep(10);
		}
	}
}

1500819406087:Thread ID:11
1500819406103:Thread ID:12
1500819406119:Thread ID:13
1500819406134:Thread ID:14
1500819406150:Thread ID:15
java.util.concurrent.FutureTask@3d4eac69 is discard
java.util.concurrent.FutureTask@42a57993 is discard

有大量任务被丢弃
自定义线程创建,ThreadFactory
最开始的线程来自ThreadFactory,它是一个接口,只有一个创建线程的方法

Thread newThread(Runnable r);

自定义线程可以跟踪在何时创建多少线程,自定义线程名称,组及优先级等,可以设置线程为守护线程

	public static void main(String[] args) throws InterruptedException {
		MyTask task = new MyTask();
		ExecutorService es = new ThreadPoolExecutor(5, 5, 
				0L, TimeUnit.MILLISECONDS, 
				new SynchronousQueue<Runnable>(), 
				new ThreadFactory() {
					
					@Override
					public Thread newThread(Runnable r) {
						Thread t = new Thread(r);
						t.setDaemon(true);
						System.out.println("create "+t);
						return t;
					}
				});
		for(int i =0;i<5;i++){
			es.submit(task);
		}
		Thread.sleep(2000);
	}

扩展线程池

ThreadPoolExecutor是一个可以扩展的线程池,提供了beforeExecute(),afterExecute(),terminated()三个接口对线程池进行控制

public class ExtThreadPool {

	public static class MyTask implements Runnable{
		public String name;
		public MyTask(String name) {
			this.name = name;
		}
		@Override
		public void run(){
			System.out.println("正在执行"+":Thread Id:"+Thread.currentThread().getId()+",Task Name="+name);
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}	
	}
	public static void main(String[] args) throws InterruptedException {
		ExecutorService es = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<Runnable>()){
			@Override
			protected void beforeExecute(Thread t, Runnable r){
				System.out.println("准备执行:"+((MyTask)r).name);
			}
			@Override
			protected void afterExecute(Runnable r, Throwable t) { 
				System.out.println("执行完成:"+((MyTask)r).name);
			}
			@Override
			protected void terminated() { 
				System.out.println("线程池退出");
			}
		};
		for(int i=0;i<5;i++){
			MyTask task = new MyTask("hh"+i);
			es.execute(task);
			Thread.sleep(10);
		}
		//关闭线程池,会等待所有线程执行完后再关闭线程池,但不会等待所有线程执行完成后再返回,只是发送一个关闭信号,执行后,线程池不能在接收新任务
		es.shutdown();
	}
}

shutdown:关闭线程池,会等待所有线程执行完后再关闭线程池,但不会等待所有线程执行完成后再返回,只是发送一个关闭信号,执行后,线程池不能在接收新任务
在线程池中寻找堆栈

使用submit()不会打印异常堆栈,可以用execute()或者

Future re = pools.submit();
   re.get();,他们都可以得到部分堆栈信息

public class DivTask implements Runnable{
	int a,b;
	public DivTask(int a, int b) {
		this.a = a;
		this.b = b;
	}
	@Override
	public void run(){
		double re =a/b;
		System.out.println(re);
	}
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ThreadPoolExecutor pools = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
		for(int i=0;i<5;i++){
			//pools.execute(new DivTask(100, i));
			Future re = pools.submit(new DivTask(100, i));
			re.get();
		}
	}
}


对线程池扩展,在调度任务之前,先保存一下提交任务线程的堆栈信息

public class TraceThreadPoolExecutor extends ThreadPoolExecutor{
	public TraceThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
			BlockingQueue<Runnable> workQueue) {
		super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
	}
	@Override
	public void execute(Runnable task){
		super.execute(wrap(task,clientTrace(),Thread.currentThread().getName()));
	}
	@Override
	public Future<?> submit(Runnable task){
		return super.submit(wrap(task,clientTrace(),Thread.currentThread().getName()));
	}
	private Runnable wrap(final Runnable task,final Exception clientStatus, String name) {
		return new Runnable() {
			
			@Override
			public void run() {
				try {
					task.run();//运行任务
				} catch (Exception e) {
					clientStatus.printStackTrace();
					throw e;
				}
			}
		};
	}
	private Exception clientTrace() {
		return new Exception("client stack trace");
	}
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		ThreadPoolExecutor pools = new TraceThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
		for(int i=0;i<5;i++){
			pools.execute(new DivTask(100, i));
		}
	}
}

Fork/Join框架
Fork增加分支,join等待分支执行完毕

ForkJoinPool线程池,对于fork方法并不急着开启线程,而是提交给ForkJoinPool线程处理,节省资源;每个线程拥有一个队列,执行完任务的线程可以帮助繁忙的线程,但从任务队列底部开始,原线程从顶部开始

ForkJoinPool的重要接口

public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)

可以向ForkJoinPool线程池提交ForkJoinTask任务,此任务就是支持fork()分解和join()等待的任务,ForkJoinTask有俩个重要的子类,分别是RecursiveAction和RecursiveTask,分别表示没有返回值的任务和可以携带返回值的任务


 

//计算列的和需要返回值
public class CountTask extends RecursiveTask<Long>{
	private static final long serialVersionUID = 1L;
	private static final int THRESHOLD = 1000;
	private long start;
	private long end;
	public CountTask(long start, long end) {
		this.start = start;
		this.end = end;
	}
	@Override
	protected Long compute() {
		long sum = 0;
		//设置任务分解的模型
		boolean canCompute = (end-start)<THRESHOLD;
		if(canCompute){
			for(long i=start;i<=end;i++){
				sum+=i;
			}
		}else {
			//分成100个小任务
			long step = (start+end)/100;
			List<CountTask> subTasks = new ArrayList<>();
			long pos = start;
			for(int i = 0;i<100;i++){
				long lastOne = pos+step;
				if(lastOne>end){
					lastOne = end;
				}
				CountTask subTask = new CountTask(pos, lastOne);
				pos+=step+1;
				subTasks.add(subTask);
				//提交子任务
				subTask.fork();
			}
			for(CountTask t: subTasks){
				//等待子任务结束
				sum+=t.join();
			}
		}
		return sum;
	}
	public static void main(String[] args) {
		//建立线程池
		ForkJoinPool forkJoinPool = new ForkJoinPool();
		//构建任务
		CountTask task = new CountTask(0, 20000L);
		//把任务交给线程池
		ForkJoinTask<Long> result = forkJoinPool.submit(task);
		try {
			//获取结果
			long res = result.get();
			System.out.println("sum="+res);
		} catch (InterruptedException | ExecutionException e) {
			e.printStackTrace();
		}		
	}	
}

任务层次划分很深,会导致线程数量越积越多,性能下降,或者函数调用层次变得很深,最终导致溢出

ForkJoin线程池采用无锁的栈管理空闲线程,工作线程无任务,就会被挂起,压入由线程池维护的栈,有任务,则唤醒这些线程

JDK并发容器

并发集合
ConcurrentHashMap:高效的并发hashMap,线程安全的hashMap

CopyOnWriteArrayList:是List,读多写少的场合,性能很好,远远好于Vector

ConcurrentLinkedQueue:高效并发队列,使用链表实现,线程安全的LinkedList

BlockingQueue:接口,JDK内部通过链表,数组等方式实现这个接口,阻塞队列,适合数据共享的通道

ConcurrentSkipListMap:跳表的实现,是Map,使用跳表的数据结构进行快速查找

Collections工具类可以将任意集合包装成线程安全的

线程安全的HashMap

实现线程安全的HashMap

法一:

Map m = Collections.synchronizedMap(new HashMap<>());

使用委托,将Map相关的功能交给传入的HashMap实现,自己负责保证线程安全,性能不是很好

法二:ConcurrentHashMap

有关List的线程安全
 
ArrayList和Vector都使用数组作为其内部实现,Vector是线程安全的,ArrayList不是线程安全的,LinkedList使用链表的数据结构,但不是线程安全的

Collections.synchronizedList(new LinkedList<>());

高效读写的队列:深度剖析CollectionLinkedQueue

队列Queue也是数据结构,使用链表作为它的数据结构,

节点node核心

private static class Node<E> {
        volatile E item;
        volatile Node<E> next;

Item用来表示目标元素,next表示当前Node的下一个元素

        boolean casItem(E cmp, E val) {
            return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val);
        }

        void lazySetNext(Node<E> val) {
            UNSAFE.putOrderedObject(this, nextOffset, val);
        }

        boolean casNext(Node<E> cmp, Node<E> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }

第一第三个方法表示设置当前的Item,next值,第一个参数为期望值,第二个参数为设置目标值,当前值等于cmp时,将目标设为val
链表的头部和尾部

private transient volatile Node<E> head;
private transient volatile Node<E> tail;

head永远不为空,通过head和succ()后继方法一定能遍历整个链表

    final Node<E> succ(Node<E> p) {
        Node<E> next = p.next;
        return (p == next) ? head : next;
    }

tail更新会延迟,每次更新跳跃俩个元素

    public boolean offer(E e) {
        checkNotNull(e);
        final Node<E> newNode = new Node<E>(e);
        for (Node<E> t = tail, p = t;;) {
            Node<E> q = p.next;
            if (q == null) {
                // p是最后一个节点
                if (p.casNext(null, newNode)) {
                    // 每俩次更新下              
                    if (p != t) 
                        casTail(t, newNode);  
                    return true;
                }
                //CAS竞争失败,再次尝试
            }
              //遇到哨兵节点,从head开始遍历,如果tail被修改,使用tail
            else if (p == q)		p = (t != (t = tail)) ? t : head;	    
else
               //取下一个节点或最后一个节点
                p = (p != t && t != (t = tail)) ? t : q;
        }
    }


这个方法没有任何锁操作,for循环没有出口,第一次加入元素,队列为空,p.next为空,p的next节点赋值为newNode,,此时p==t成立,不会更新tail末尾,增加一个元素后,tail不会被更新,增加第二个元素时,t还在head位置,p.next指向第一个元素,q!=null,表示q不是最后的节点,往队列中增加元素需要最后一个节点的位置,循环开始查找最后一个节点位置,此时,p实际指向链表中的第一个元素,next为null,在第二个循环中q=null,p更新自己的next,指向新加入的节点,p!=t成功,更新t位置,t移到链表最后,p==q是由于哨兵节点,即next指向自己的节点,主要表示要删除的节点或空节点,可能直接返回head,执行过程中,tail被其他线程修改,新tail作为链表末尾

p = (t != (t = tail)) ? t : head;

执行!=时,先取得左边的t,在执行t=tail,取得新t值,并发时,右边的t被其他线程修改,用新的tail作为链表末尾,即等式右边的t

哨兵节点产生

		ConcurrentLinkedQueue<String> q = new ConcurrentLinkedQueue<>();
		q.add("1");
		q.poll();
    public E poll() {
        restartFromHead:
        for (;;) {
            for (Node<E> h = head, p = h, q;;) {
                E item = p.item;

                if (item != null && p.casItem(item, null)) {                  
                    if (p != h) 
                        updateHead(h, ((q = p.next) != null) ? q : p);
                    return item;
                }
                else if ((q = p.next) == null) {
                    updateHead(h, p);
                    return null;
                }
                else if (p == q)
                    continue restartFromHead;
                else
                    p = q;
            }
        }
    }

高效读取CopyOnWriteArrayList

写和写同步等待,写入操作时,进行自我复制,不修改原有内容,对原有数据进行复制,修改的内容写入副本,写完后将副本替换原有数据
读取操作

private transient volatile Object[] array;
    final Object[] getArray() {
        return array;
    }
    public E get(int index) {
        return get(getArray(), index);
    }
    private E get(Object[] a, int index) {
        return (E) a[index];
    }

写入操作

    public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

array是volatile,读取线程会立即知道这个修改
数据共享通道:BlockingQueue

它是一个接口

ArrayBlockingQueue基于数组实现,适合做有界队列,可容纳的最大元素需要在队列创建时指定,LinkedBlockingQueue适合做无界队列或边界值非常大的,因为内部元素可以动态增加,不会因为初始容量很大,耗掉大半内存

BlockingQueue适合作为数据共享的通道因为它会让服务任务在队列为空时等待,有新消息进入队列后,自动唤醒线程

ArrayBlockingQueue的内部元素都放置在一个对象数组中

final Object[] items;

可以用offer()和put()向队列中压入元素,offer在当前队列满时立即返回false,put会等待,直到有空闲位置

弹出元素可以用poll和take,都从队列头部获得一个元素,队列为null,poll返回空,take等待

final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;

执行take(),队列为空,当前线程等待在notEmpty上,新元素入队时,进行一次notEmpty的通知

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

 

    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
    public void clear() {
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            int k = count;
            if (k > 0) {
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
                    items[i] = null;
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
                takeIndex = putIndex;
                count = 0;
                if (itrs != null)
                    itrs.queueIsEmpty();
                for (; k > 0 && lock.hasWaiters(notFull); k--)
                    notFull.signal();
            }
        } finally {
            lock.unlock();
        }
    }
    void removeAt(final int removeIndex) {
        // assert lock.getHoldCount() == 1;
        // assert items[removeIndex] != null;
        // assert removeIndex >= 0 && removeIndex < items.length;
        final Object[] items = this.items;
        if (removeIndex == takeIndex) {
            // removing front item; just advance
            items[takeIndex] = null;
            if (++takeIndex == items.length)
                takeIndex = 0;
            count--;
            if (itrs != null)
                itrs.elementDequeued();
        } else {
            // an "interior" remove

            // slide over all others up through putIndex.
            final int putIndex = this.putIndex;
            for (int i = removeIndex;;) {
                int next = i + 1;
                if (next == items.length)
                    next = 0;
                if (next != putIndex) {
                    items[i] = items[next];
                    i = next;
                } else {
                    items[i] = null;
                    this.putIndex = i;
                    break;
                }
            }
            count--;
            if (itrs != null)
                itrs.removedAt(removeIndex);
        }
        notFull.signal();
    }


随机数据结构:跳表SkipList
跳表是一种可以用来快速查找的数据结构,类似平衡树,都可以对元素快速查找,但平衡树的插入和删除往往很可能导致平衡树进行一次全局的调整,对跳表的插入和删除只需要对整个数据结构的局部进行操作,跳表只需要部分锁,平衡树需要全局锁
跳表是随机算法,本质是同时维护多个链表,链表是分层的


最低层的链表维护跳表内所有的元素,每上面一层都是下面一层的子集,一个元素插入哪些层是随机的,所有链表的元素都是排序的,可以从顶级链表开始查找,发现被查找元素大于当前链表的取值,会转入下一层链表继续查找,搜索是跳跃的,


空间换时间的算法

使用跳表实现Map和使用hash实现Map,hash并不会保存元素的顺序,跳表有序

		Map<Integer,Integer> map = new ConcurrentSkipListMap<>();
		for(int i=0;i<30;i++){
			map.put(i, i);
		}
		for(Map.Entry<Integer,Integer> entry:map.entrySet()){
			System.out.println(entry.getKey());
		}
一个Node表示一个节点
    static final class Node<K,V> {
        final K key;
        volatile Object value;
        volatile Node<K,V> next;
对node的操作使用CAS方法

        boolean casValue(Object cmp, Object val) {
            return UNSAFE.compareAndSwapObject(this, valueOffset, cmp, val);
        }
        boolean casNext(Node<K,V> cmp, Node<K,V> val) {
            return UNSAFE.compareAndSwapObject(this, nextOffset, cmp, val);
        }
另一个数据结构是Index,内部包装了Node,增加向下和向右的引用

    static class Index<K,V> {
        final Node<K,V> node;
        final Index<K,V> down;
        volatile Index<K,V> right;
每一层的表头,需要记录当前处于哪一层,表示链表头部的第一个Index

    static final class HeadIndex<K,V> extends Index<K,V> {
        final int level;
        HeadIndex(Node<K,V> node, Index<K,V> down, Index<K,V> right, int level) {
            super(node, down, right);
            this.level = level;
        }
    }








评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值