《java编程思想》
http://blog.csdn.net/ns_code/article/details/17539599
http://www.runoob.com/java/thread-deadlock.html
线程基本机制
1定义线程任务的方法
extends Thread
implements Runnable
Executor框架:
public static ExecutorService newFixedThreadPool(int nThreads) 创建固定数目线程的线程池。 public static ExecutorService newCachedThreadPool() 创建一个可缓存的线程池,调用execute将重用以前构造的线程(如果线程可用)。如果现有线程没有可用的,则创建一个新线 程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。 public static ExecutorService newSingleThreadExecutor() 创建一个单线程化的Executor。 public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个支持定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。
通过Executors的以上四个静态工厂方法获得 ExecutorService实例,而后调用该实例的execute(Runnable command)方法即可.
2从任务中产生返回值
实现Callable接口。具有类型参数的泛型。
可以从call()方法中返回值,保存在Future类型的变量中,用future.get()获取。
执行:exector.submit(new TaskWithResult());
3后台线程
daemon线程,是指在程序运行的时候再后台提供一种通用服务的线程。(如垃圾回收)
只要有非后台线程还在运行,程序就不会终止。
thread.setDaemon(true) ->在start()前调用。
注:
在后台程序中产生的新线程也是后台线程。不要在后台程序中执行业务逻辑操作(数据的读写等)。
4加入一个线程
一个线程可以在其他线程之上调用join()方法,其效果是其他线程等待一段时间直到此线程结束(t.isAlive()为假)。 也可以在join()时带上一个超时参数,这样如果此线程在这段时间到期时还没有结束的话,join()直接返回。另外,调用join()的线程可以被中断。
共享受限资源
对于并发工作,我们需要某种方法来防止两个任务访问相同的资源。(非原子性操作)
1 synchronized
互斥性+原子性
将共享对象(如内存片段、文件、输入输出端口、打印机等)包装进一个对象,然后把所有要访问这个资源的方法标记为synchronized。如果某个任务处于一个对标记为synchronized的方法的调用中,那么在这个线程从该方法返回之前,其他所有要调用类中任何标记为synchronized方法的线程都会被阻塞。
还可以利用同步块的方式:syncronized(object){….}
2 volatile
互斥性
变量修饰符,如果将一个域声明为volatile的,那么只要对这个域产生了写操作,那么所有的读操作就都可以看到这个修改。
Volatile修饰的成员变量在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。
写操作happens before读操作。
当且仅当满足以下所有条件时,才应该使用volatile变量:
1、对变量的写入操作不依赖变量的当前值 //反例:count++,非原子性操作,反编译后可看到在getField()和putField()之间还有其他操作
2、该变量没有包含在具有其他变量的不变式中。 //反例:lower<upper
3 Lock
Lock lock = new ReentrantLock();//默认使用非公平锁,如果要使用公平锁,需要传入参数true
........
lock.lock();
try {
//更新对象的状态
//捕获异常,必要时恢复到原来的不变约束
//如果有return语句,放在这里
} finally {
lock.unlock(); //锁必须在finally块中释放
}
线程的终止
线程的中断
Thread.sleep()
t.interrupt()
t.isInterrupted() ->catch到InterruptedException后就重新置为false。
Thread.interrupted() 报告当前线程的中断状态,并隐式重置为false。
Thread.yield() 建议具有相同优先级的其他线程可以运行。(没有机制保证它会被采纳)
线程的挂起和恢复
设置标志位
线程之间的协作
1 wait()/notify()/notifyAll()
2 阻塞队列
阻塞队列的接口是java.util.concurrent.BlockingQueue,它有多个实现类:
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue等。
阻塞队列与普通队列的区别是:
当阻塞队列是空的时,从队列中获取元素的操作将会被阻塞,或者当队列是满时,往队列里添加元素的操作会被阻塞。试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列。
3 阻塞栈
阻塞栈的接口是java.util.concurrent.BlockingDueue,它有多个实现类:
ArrayBlockingDueue、LinkedBlockingDueue、PriorityBlockingDueue、SynchronousDueue等。
阻塞栈与普通栈的区别同阻塞队列与普通队列的区别。
blockQueue.put()/blockQueue.take() 阻塞式存取
blockDeque.putFirst()/blockDeque.takeFirst()
死锁
java 死锁产生的四个必要条件:
1、互斥使用,即当资源被一个线程使用(占有)时,别的线程不能使用
2、不可抢占,资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
3、请求和保持,即当资源请求者在请求其他的资源的同时保持对原有资源的占有。
4、循环等待,即存在一个等待队列:P1占有P2的资源,P2占有P3的资源,P3占有P1的资源。这样就形成了一个等待环路。
要破坏死锁的话,只需破坏其中一个条件即可。最容易破坏的是第4个条件。
新类库中的构件
1 CyclicBarrier
CyclicBarrier(又叫障碍器),它适用于这样一种情况:你希望创建一组任务,它们并发地执行工作,另外的一个任务在这一组任务并发执行结束前一直阻塞等待,直到该组任务全部执行结束,这个任务才得以执行.
2 Semaphore
信号量Semaphore实维护了一个许可集合,可以控制某个资源被同时访问的任务数,它通过acquire()获取一个许可,release()释放一个许可。如果被同时访问的任务数已满,则其他acquire的任务进入等待状态,直到有一个任务被release掉,它才能得到许可。
3 并发容器
策略:对容器的修改可以与读取操作同时发生。如在CopyOnArrayList/CopyOnArraySet中,写入将导致创建整个底层数组的副本,而源数组保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。当修改完成时,一个原子性的操作将把新的数组换入。
others
在Java中创建线程安全的Singleton:
http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
//双检锁
public static Singleton getSingleton() {
if (instance == null) { //Single Checked
synchronized (Singleton.class) {
if (instance == null) { //Double Checked
instance = new Singleton();
}
}
}
return instance ;
}