1、线程组:线程组持有一个线程的集合。线程组的价值可以用如下的这段话概括:最好把线程组看成是一次不成功的尝试,你只要忽略它就好了。
2、捕获异常:由于线程的本质特性,使得你不能捕获从线程逃逸的异常。一旦异常逃出任务的run方法,它就会向外传播至控制台,除非你采取特殊的步骤捕获这种异常。在SE5之前,你可以使用线程组来捕获这些异常,但是SE5之后,就可以使用Executor来解决这个问题,因此你就不需要线程组相关的知识了。
示例:
public class ExceptionThread implements Runnable{
public void run(){
throw new RuntimeException();
}
public static void main(String[] args){
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}
}
-->控制台将输出异常信息:java.lang.runtimeexception:.....
将main的主体放入到try catch块中是没有用的:
public static void main(String[] args){
try{
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new ExceptionThread());
}catch(Exception e){
System.out.println(“get a exception”);
}
}
这将产生与前面示例相同的结果:未捕获的异常。解决方式:Thread.UncaughtExceptionHandler;既可以给每个Thread都设置一个UncaughtExceptionHandler,也可以通过Thread的静态方法为Thread设置一个全局的UncaughtExceptionHandler。
3、Java中递增(++)不是原子性操作。
4、Java SE5的java.util.concurrent类库包含有定义在java.util.concurrent.locks中的显示的互斥机制 -- Lock。Lock对象必须被显示的创建、锁定、销毁,代码缺乏有优雅兴。但是,对于解决某些类型的问题来说,它更加的灵活。一般使用的时候遵循如下用法:
private Lock lock = new ReentrantLock();
public int methodName(){
lock.lock();
try{
......
return ...;
}finally{
lock.unlock();
}
}
当使用lock对象的时候,按照上述惯用法是很重要的。注意:return语句必须在try语句块中出现,以确保unlock不会过早发生,从而将数据暴露给第二个任务。
与synchronized关键字相比,lock对象的优点:如果在使用synchronized关键字,某些事物失败了,那么就会抛出一个异常。但是你没有机会去做任何清理工作了。有了显示的Lock对象,你就可以使用finally子句将系统维护在正确的状态。
使用时机:只有在解决特殊的问题的时候,才使用显示的Lock对象。例如,用synchronized关键字不能尝试着获取锁且最终获取锁会失败,或者尝试着获取锁一段时间后,放弃它,要实现这些,必须使用concurrent类库。
ReentrantLock允许你尝试着获取锁(tryLock(...))但最终未获取锁,这样如果其他人已经获取了这个锁,那你就可以决定离开去执行其他一些事情,而不是等待直至这个锁释放。
5、原子性和易变性:原子操作是不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他的线程)执行完毕。依赖于原子性是很棘手且很危险的。除非你是一个并发专家,否则你不应该使用原子性代替同步。
原子性可以应用于除了long和double之外的所有基本类型之上的“简单操作”。对于读取和写入除long和double之外的基本类型变量这样的操作,可以保证他们会被当做不可分的操作来操作内存。但是JVM可以将64位(long和double变量)的读取和写入当做两个分离的32位操作来执行,这就导致一个读取和写入操作之间发生上下文切换,从而导致任务看到不正确结果的可能性。当你定义long和double变量时,如果使用volatile关键字,就会获得原子性。
6 、volatile关键字确保了应用中的可见性。如果你将一个变量声明为volatile的,那么只要对这个变量产生了写操作,那么所有的读操作都可以看到这个修改。
7、如果有多个任务同时访问一个域,那么这个域就应该是volatile的,否则这个域应该只能经由同步来访问。同步也会导致向主存中刷新,因此如果一个域完全由synchronized方法或语句来防护,那么久不必再将其设置为volatile了。
8、再次提醒:synchronized是第一选择,这是最安全的方式,而尝试其他方式都是有风险的。
9、赋值和返回操作通常都是原子性的。
10、如果一个域可能会被多个任务访问或者这些任务至少一个是写入任务,那么你就应该将这个域设置为volatile。将一个域设置为volatile,那么它就会告诉编译器不要执行任何移除读取和写入操作的优化。但是volatile不能影响对递增操作不是原子性操作这一事实产生影响。