同步和异步
异步操作是指多线程并发的操作,相当于同时执行,各干各的
同步操作是指有先后操作,相当于排队执行,你干我我在干
复制代码
66.同步访问共享的可变数据
1. 关键字 synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法.或者某一代码块,还可以保证看到的是由同一个锁保护之前的所有修改效果
a. 互斥(即同步可以阻止一个线程看到对象处于不一致的状态中)
b. 可见(保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前所有的修改效果)
2. Java语言规范保证读或者写一个变量是原子性的,除非这个变量的类型是long或者double(32位jvm)
3. 为了在线程之间进行可靠的通信,也为了互斥访问,同步是必须的.
4. volatile修饰符不执行互斥访问,但它可以保证任何一个线程在读取该域的时候都将看到最近被写入的值
5. 使用volatile需要格外谨慎,因为它并没有互斥作用,如果声明一个volatile int,然后对其进行++操作,那将会导致data corruption,因为++不是原子性的.
6. 要么不共享数据,要么共享不可变数据,将可变数据限制在单个线程中.
7.总结
a. 当多个线程共享可变数据的时候每个读或者写数据的线程都必须执行同步.
b. volatile是一种可接受的同步形式
复制代码
67.避免过度同步
1. 为避免死锁和数据破坏,不要在同步区域内部调用外来方法.
2. 要尽量限制同步区域内的工作量.
a. 获得锁->检查共享数据->根据需要转换数据->放掉锁
3. 当设计一个可变类时,应该考虑他们是否应该自己完成同步操作.
a. 如果在内部同步了类,就可以使用不同的方法实现高并发性,例如分拆锁、分离锁、非阻塞并发控制。
复制代码
68. executor和task优先于线程:
1. Executors提供了多个工厂方法,创建ExecutorService,还可以直接使用ThreadPoolExecutor,对线程池做更精细的控制.
2. 如果轻负载,使用Executors.newCachedThreadPool,如果重负载,用Executors.newFixedThreadPool
3. 任务和机制被分别抽象了,前者为Runnable和Callable,后者则是executor service;
4. java.util.Timer是单线程,且抛出未捕获异常会终止,可以使用ScheduledThreadPoolExecutor替代;
复制代码
69. 并发工具优先于wait和notify
1. Executor Framework(任务执行工具)
2. concurrent collections(并发集合)
3. synchronizers(同步器)
复制代码
70. 线程安全性的文档化
1. 一个方法的声明中加了synchronized并不能保证它是线程安全的,并且Javadoc也不会把这个关键字输出到文档中
file:///Users/wangjun/timwang/codeReview/src/main/java/javadoc/index.html
2. 一个类为了可被多个线程安全的使用,文档中必须清楚的说明它所支持的线程安全级别
3. 当一个类承诺了使用一个公有可访问的锁对象时,就意味着允许客户端以原子的方式执行一个方法的调用序列. 但是这种方法会发生拒绝服务攻击,只要客户端超时地持有公有可访问锁即可.
4. lock 域应被声明为final,防止不小心改变它的内容.
5. 私有锁只能用在无条件的线程安全类上.适用于专为继承而设计的类
复制代码
71 慎用延迟初始化
1. 如果域只有在类的实例部分被访问,并且初始化的开销很高,才值得进行延迟初始化。否则大多数的域都应该正常地初始化,而不是延迟初始化。假设真的有必要进行,也还应该注意线程同步的问题:
复制代码
72 不要依赖于线程调度器
1. 不要让应用程序的并发性依赖于线程调度器
2. 不要依赖Thread.yield和线程优先级
复制代码
73 避免使用线程组
1. 可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织。
a. 线程组ThreadGroup对象中比较有用的方法是stop、resume、suspend等方法,由于这几个方法会导致线程的安全问题
b. 线程组ThreadGroup不是线程安全的,这在使用过程中获取的信息并不全是及时有效的,这就降低了它的统计使用价值
复制代码