记一下学习过程的觉得需要注意的点吧
第二章、线程安全性
无状态对象一定是线程安全的
内置锁是可重入的,就是说获得锁的操作的粒度是一个『线程』,而不是一个『调用』。
synchronized 函数,是对类的实例加锁。
第三章、对象的共享
在程序中,可能编译过程中会进行重排序。就是对代码中的执行顺序可能进行改变,这样的优势是充分利用流水线的作用。 (这个有点可怕呀)
volatile
关键字可以保证变量的可见性,因为对volatile
的操作都在Main Memory中,而Main Memory是被所有线程所共享的,这里的代价就是牺牲了性能,无法利用寄存器或Cache。volatile
还有一个作用就是局部阻止重排序的发生
对于非volatile类型的64位数值变量double和long
jvm允许将64位的读操作和写操作分解成两个32位的操作,所以不考虑失效数据的问题,在多线程中共享
可变的long和double也是不安全的,除非用volatile来声明他们。(这个也要注意下,可能webApi中,会修改一个全局变量,这个时候long的修改可能会读到一个错误的值)
在构造函数中最好不要新建一个线程并启动,可以吧启动放在后续的函数中。因为可能在构造完成之前,线程就会访问到this的值。
线程封闭
- ad-hoc线程封闭
这个好像说完全自己在代码里注意 - 栈封闭
相当于用局部变量 - Threadlocal类
不同线程调用的一个Threadlocal变量不一样
第四章、对象的组合
没啥,主要注意加锁的时候,要加在一个锁上面,不要加错了
第五章、基础构建模块
Hashtable和Vector是同步容器
创建一个同步List Collections.synchronizedList(new ArrayList<Object>());
通过并发容器来代替同步容器,可以极大的提高伸缩性并降低风险
接下来介绍了一些并发容器:
- cuncurrentHashMap 使用的是分段锁,实现了额外的原子操作,类似相等则移除之类的,保障他的迭代器不会影响。
- copyOnWriteArrayList 每次修改的时候都会把底层数组复制一遍,这样保障他的迭代器不会影响。
- LinkBlockList ArrayBlockList.是FIFO队列
- PriorityBlockingQueue 是一个优先级的队列
- 双端队列 Deque ,可以实现,生产者首先读取自己的队列,空了再去别人的队列尾部开始工作,降低了队列的竞争程度
中断
首先,忘掉Thread.stop方法。虽然它确实停止了一个正在运行的线程,然而,这种方法是不安全也是不受提倡的,这意味着,在未来的JAVA版本中,它将不复存在。
中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。
因此,如果线程Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。Listing C这个示例描述了该技术。
http://www.cnblogs.com/simonshi/archive/2011/12/31/2308455.html 这个讲的还可以
同步工具类
- CountDownLatch是一种灵活的闭锁实现,await会一直阻塞到,值为0.countDown会把值减1(闭锁,门一开始是关闭的,等待所需要的资源都初始化之后,会打开,并且一直打开)
- FutureTask,Callable。一种有结果的Runnable.future.get() 可以获得
- 信号量Semaphore
- CyclicBarrier 感觉和countDownLatch差不多,只是可以重复使用
第六章、任务执行
Executor 任务执行框架
如果在程序中实现一个生产者消费者的设计,那么最简单的方式就是使用Executor
延时任务和周期性任务
Timer类和ScheduledThreadPoolExecutor。
Timer有一些缺陷,Timer在执行任务时只会创建一个线程,如果某个任务执行时间过长,将会影响其他的TimerTask的精确性。而且如果TimerTask抛出了一个未检查的异常,那么Timer将变现出糟糕的行为。
在java 5.0或更高的jdk中,将很少使用Timer。
如果要构建自己的调度系统,可以使用DelayQueue。(可以设置过期时间,然后take的时候,只能取出到了时间的)
可以用ExecutorService的submit(callable a)方法来得到一个Future,然后future.get()得到返回值。但是这里所有的都会在future.get()处等待
可以使用ExecutorCompletionService来进行处理,在提交多个task之后,起一个轮训,取completion.take(),这样可以获取完成了的线程。
为任务设置时限:
1.可以使用future.get(long timeout, TimeUnit unit),然后catch住TimeOutException
对于要对多个futureTask设定实现,可以使用invokeAll(List<Callable> **, long timeOut, TimeUtil unit)