并发的本质是通过多线程提高cpu的使用效率。实现可响应的用户界面也是并发的一大亮点。并发技术,功能强大而又复杂难懂,充满魅力,是初级程序员的必修课。
java语言基本的线程机制
定义任务:
1.实现runnable接口,编写run方法,以实现了该接口的实例作为构造器参数。
2.实现callable接口,实现call方法。
启动线程
1.对于第1种任务定义方法,
a.可以用实现了该接口的实例为构造器参数,构造一个thread实例,然后调用thread.start,从而启动线程。
b.使用Executors新建一个线程池,线程池分为cachedthreadpool,singlethreadpool和fixthreadpool等,然后调用executorService.execute(实现了runnable接口的实例),从而启动线程,executorservice.shutdown()非常必要,否则线程退出后,jvm不会退出,仍有激活的线程存在。
2.对于第2种任务定义方法,新建线程池,然后threadpool.submit(实现了callable接口的实例),从而启动线程。submit方法,返回了future实例,通过该实例调用get方法,可以获取call方法的返回值。future.isdone方法判断是否完成,
若不判断,直接get,则该方法一直阻塞至任务完成。
休眠
TimeUnit.SECOND.sleep();休眠不释放锁,不占用cpu.
优先级
Thread.currentThread.setPriority();设置优先级,不通平台对优先级的处理不通,最好用max,normal和min
后台进程
thread.setDaemon(true),非后台进程全部退出时,后台进程也会被杀死。
加入一个线程
thread.join(),当thread退出或者thread被中断后,当前线程才继续进行
异常处理
线程抛出异常会直接传播到控制台,可以setuncaughtExceptionhandler来处理异常
处理共享资源
synchronized 或者lock.lock() lock.unlock的方式 lock方式支持公平锁,每次选择等待时间最久的进入临界区。Lock lock=new ReentrantLock(true);
volatitle修饰的成员变量,会直接从内存读取,但是不能保证复杂计算的原子性
atomiclong atomicdouble atomicrefence 提供了原子性操作
threadlocal在每个线程中都会有一个独立副本
终结任务
线程状态:新建、就绪、阻塞、死亡
进入阻塞的途径:1.wait 2.sleep 3.等待I/O 4在对象上调用同步方法
可中断的阻塞包括sleep和wait,等待I/O的阻塞可以通过关闭底层资源来实现.在对象上调用不通方法,可以用 lock.lockInterruptibly()来支持阻塞。
判断中断 Thread.isInterrputed(),常用例子
while(!Thread.isInterrupted()){ //todo }
线程池调用shutdownnow相当于对池中线程调用interrupt(),future的cancel也可以中断
线程之间的协作
wait() noitfy() notifyall() wait可以指定线程挂起的时间。三种方法都必须在获得对象锁的情况下调用。wait释放锁。
使用lock的加锁时,协作方法为:Condition condition=lock.newCondition(); condition.await();condition.signal()和signalall()
arrayblockqueue和linkedblockqueue同一个时间只允许一个线程存取。队列为空时,阻塞当前线程。
死锁
构成死锁的四个条件1.构成互斥(线程中使用了不可以共享的资源)2.持有一个资源等待另外一个资源 3.不可以强化资源 4.循环等待
同步构件
countdownlatch :新建时countdownlatch,指定一个size,直到调用了size个countdownlatch的coundown,所有的countdownlatch的await调用都会阻塞该线程。
cyclicbarrier:新建new CyclicBarrier(size,new Runnable),规则与countdownlatch类似,区别是await了size次之后,触发runnable实例的run方法。
delayqueue,可以持有实现了delayed接口的实例引用,delayed接口继承了 comparable接口,所以要实现getDelay()和compateTo()两个方法,只有getDelay返回值小于等于零时,才能从delayqueue中取出。
同时,可取元素的排序使用了compare方法的返回结果。
priorityblockingqueue 支持排序和阻塞,排序根据compare方法,队列为空时,取元素会阻塞当前线程
sheduledExecutorService可以定时启动线程,也可以周期性地启动线程。周期性启动线程时,如果周期时间耗尽后,前一个线程还没有结束,那么将不会启动下一个线程,只到上一个线程结束。
Semaphore: Semaphore semaphore=new Semaphore(size); semaphore.acquire(),semaphore.release()。acquire的次数减去release的次数小于等于size,否则acquire会阻塞当前线程。
Exchanger<T t>:顾名思义,从来在线程间交换T实例。
性能调忧
lcok相比synchronized更加高效,但代码可读性差。
性能:concurrentCollection>copyonwriteCollection>sychronized
免锁容器:copyOnWriteArrayList和copyOnWriteArraySet,concurrentHashMap,concurrentLinkedQueue等,修改时是对副本修改,修改是同步控制的,读取可以同时进行。修改完成后,与原数据进行替换,从而对读取者可见。
本质是减小加锁的粒度,从而介绍阻塞,提高效率。
乐观加锁:
原子类,AtomicInteger提供了原子性操作,decrementAndGet。也可以使用客观加锁,compareAndSet(),会将旧值和新值一起提交给该方法,如果提交的旧值与atomic的值不一致,那么该操作会失败。失败后如何操作,要由程序员自己决定。
ReentrantReadAndWritelock=new ReentrantAndWriteLock(); 加锁后,可以多线程读,但如果有写操作,则其他线程不能读也不能写。