通过学习《Thinking in Java》,总结并发API如下:
12.1 Thread.yield():说明自己用的cpu时间差不多了,可以让别的线程使用cpu了,不一定会被采纳,就是说别的线程不一定就会马上获得cpu
12.2 线程池有固定大小,不固定大小的(newCachedThreadPool建议使用这个),以及单线程(newSingleThreadExecutor即只能一个线程结束后,第二个线程才执行)的。shutdown():不接受新任务
12.3 后台线程daemon:t.setDaemon(true) 需在t.start()之前设定,其创建的所有线程都为后台线程。当非后台线程都停止时,后台线程会立刻终止,finally子句不会执行;
12.4 Runnable任务:class可以implements Runnable()。之后其class可以用线程池调度:
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new class());
也可以用Thread t = new Thread(new classA());
12.5 join()方法:可以理解为等待其他线程结束再开始执行自己,如在t1线程中调用t2.join(),则t1要等待完t2结束才能往下执行
12.6 异常:Interupted异常能被自己捕获,但调用isInterrupted()方法时为false
run本身不能捕获运行时异常,需用UncaughtExceptionHandler捕获,例子如下:
package JavaBase2;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
public class ExceptionThread {
public static void main(String[] a){
//ExecutorService exe = Executors.newCachedThreadPool(new HandlerThreadFactory());
//ExecutorService exe = Executors.newCachedThreadPool();
//System.out.println(new ExceptionThread());
ExecutorService exe = MySys.getExe();
exe.execute(new ExceptionThread2());
}
}
class ExceptionThread2 implements Runnable{
public void run(){
Thread t = Thread.currentThread();
System.out.println("run by thread " + t.getName());
System.out.println("eh=" + t.getUncaughtExceptionHandler());
//throw new RuntimeException();
MySys.callExceptionMethod();
}
}
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
public void uncaughtException(Thread t,Throwable e){
System.out.println("caught " + e);
ExecutorService exe = MySys.getExe();
exe.execute(new NormalThread());
exe.execute(new NormalThread());
}
}
class HandlerThreadFactory implements ThreadFactory{
public Thread newThread(Runnable r){
System.out.println(this + " create new Thread");
Thread t = new Thread(r);
System.out.println("created " + t);
t.setUncaughtExceptionHandler(MySys.getEh());
System.out.println("eh=" + t.getUncaughtExceptionHandler());
return t;
}
}
class NormalThread implements Runnable{
public void run(){
Thread t = Thread.currentThread();
System.out.println("new a normal thread=" + t.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("sleep end....");
}
}
class MySys{
//private static ExecutorService exe = Executors.newSingleThreadExecutor(new HandlerThreadFactory());
private static ExecutorService exe = Executors.newCachedThreadPool(new HandlerThreadFactory());
private static MyUncaughtExceptionHandler eh = new MyUncaughtExceptionHandler();
public static ExecutorService getExe(){
return exe;
}
public static MyUncaughtExceptionHandler getEh(){
return eh;
}
public static void callExceptionMethod(){
System.out.println("Exception method run.....");
throw new RuntimeException();
}
}
12.7 volatile的使用:保证工作线程从jvm内存拿到的值是最新的;也就是确保有可视性,即主内存的值修改后,马上对全部工作线程可见,全部工作线程内存的值再次使用时会到主内存读取。java的64位操作可能有字撕裂。但不是线程安全的,详见:
12.8 synchronized:锁住共享资源,其一般表现为对象锁,即锁住对象形式存在的内存片段。一个对象一把锁,多个synchronized共用一把锁,一个对象的多个synchronized是互斥的。对于读和写的共享资源都要加锁
一个任务可以获得对象的多个锁,即调用多个synchronized片段,采用计数器计数,当为0时说明调用完成。
类也可以有synchronized,synchronized static
同步块比同步方法性能更优
同步块须给定一个对象上锁,一般是this,锁住当前对象,当前对象的其他临界资源也被锁住
如果在一个对象上有两个同步锁,其加锁的对象一个是当期对象,一个是其他对象,则这两个同步锁不会互斥
12.9 显式锁:进出的时候加锁解锁,解锁在return之后:
private int count = 0;
private Lock lock=new ReentrantLock();
public int next(){
try{
lock.lock(); ++count;
Thread.yield();
++count;
return count;
}finally{
lock.unlock();
}
12.10 原子性:应用于除long和double之外的所有基本类型之上的“简单操作”,也就是赋值和返回值。
12.11 中断:I/O和synchronized是不可中断的,除非中断其依赖的底层资源,sleep可以直接用ExectorServicce.submit().cancel(true)中断;
12.12 在构造器中锁了自己,其他任务通过自身的引用也会互斥:
12.13 ExectorService exec = Executors.newCachedThreadPool();
exec.shutdownNow();//调用所有由他控制的线程的interrupt();
12.14 wait():是Object的方法,释放锁,相当于挂起(而sleep不会释放锁),可以用notify()和notifyAll() 唤醒,也可以等时间到期唤醒。只能在同步块中使用,否则会报运行异常,因为使用者需获得锁。
12.15 notify():唤醒等待锁的某个任务,notifyAll()唤醒等待锁的全部任务,而且只是唤醒等待同一个对象锁的全部任务,等待不同对象锁的任务不会唤醒;
12.16 List<L> data = Collections.synchronizedList(new ArrayList<L>): data的所有方法都是线程安全的。
12.17 Semaphore:控制多个任务访问共享资源
12.18 ExecutorService exec = Executors.newCachedThreadPool();
线程的run方法的while用:
public void run(){
try{
while(!Thread.interrupted()){}
}catch{ }
当调用exec.shutdownNow()时,可以终止while循环
12.20 CoutDownLatch:一种用来等待一组任务完成,其他任务才能执行的锁
CountDownLatch latch = new CountDownLatch(100);
如果A调用了latch.await();
B1调用了latch.countDown();
B2调用了latch.countDown();
........................B100调用了latch.countDown();即减到了0;A任务才能往下走
12.21 CyclicBarrier:与CountDownLatch类似,不同的是其可以循环计数。
12.22 AtomicLong,Lock,synchronized比较:优先使用syn锁,因为可读性好;计数器用Atomic。
12.23 免锁容器CopyOnwriteArrayList:读写可同时进行,修改时有副本,当修改完成时一个原子性操作修改原来的容器,不会抛出ConcurrentModificationException。
12.24 读写锁ReadWriteLock,读要等写完才能读,多个读不互斥。当写锁被其他任务持有时,任何读取者都不能访问,直到写锁释放为止。
12.25 乐观锁,atomic.compareAndSet(oldvalue,newvalue),更新时如新值不等于旧值会失败。
12.26 延迟队列DelayQueue,延迟到期才能从队列取走。延迟最多最后执行,用CompareTo方法比较顺序。Comparable可以使用范型。
12.17 优先队列PriorityBlockingQueue,也是基于Comparable,排在前面的优先级最高先执行,与延迟相反。