Java并发编程实践第二天

版权声明:转载标明来源! https://blog.csdn.net/qq_39213969/article/details/88785541

一,线程安全(原子性以及锁)

1.要构建并发程序就要正确使用>线程和加锁。
本质是要:编写线程安全的代码,管理对状态的访问,通常是共享的,可变的状态。

2.java中首要的同步机制关键字就是synchronized,他提供立独占锁。
持此之外 ,“同步还包括volatile变量",显示锁以及原子变量的使用。

3.在没有使用synchronized关键字修饰同步机制时,多个线程访问了同一个变量,程序如何避免安全隐患。(不建议这样做)
(1)不要跨线程共享变量
(2)使状态变量为不可变的。
(3)在任何使用状态变量的时候使用同步(即使变量设置为同步机制)。
注意:一开始就将一个类设计成线程安全的比以后对其重新修复要简单得多。

4.无状态对象永远是线程安全的(不包含域也没有引用其他类的域)。多数的servlet都可以设计为状态的。

5.自增不是原子操作,加入到无状态对像将会线程不安全(因为其分为三步:获得当前值,加1,写回新值)。

6.应对竞争条件

(1)检查在运行,惰性(延迟)初始化:目的是延迟对象的初始化,直到程序真正使用它,同时确保它只初始化一次。(必须设置为原子操作)注意:原子引用 自身呢也是线程安全的。不变约束(值对应不变):更新一个变量时,要更新在同一个原子操作中其他变量。
**注意:**为了保护状态的一致性,要在单一的原子操作中更新相互关联的的状态变量。
(2)没有线程安全同步机制的变量或者类,必须为原子性操作或者是不可以分割的。为了避免竞争条件,必须阻止其它线程访问我们正在访问和修改的变量。当一个线程想要查看或者修改一个状态时,必须是在我们之前或者我们之后,不能在操作过程中。

注意:检查在运行和自增操作(读改写)均不是院子操作,他们都市复合操作。(为了保证原子性的操作,必须使用java提供的锁机制)。

(3)处理自增线程可能不安全问题:将long类型的计数器变量转换为AtonicLong原子变量类型,就可以确保所有访问计数器状态的操作都是原子性的。计数器状态已经转为线程安全的了,所以servlet也就是再一次变成线程安全的了。

***注意:***当只向无状态中加入唯一的状态元素,而这个状态完全被线程安全的对象所管理,那么新的类仍然是线程安全的。
在进行时序交替进行操作时,不能改变唯一性约束。(着个是原则以及前提)。

二,内部锁(强制原子性)

1.java的内置锁机制:synchronized块。(组成:锁对象的引用,以及这个锁要保护的代码块)。
2.synchronized方法是对跨越了整个方法体的synchronized快的描述。至于synchronized方法的锁就在这个方法的对象本身。
注意:静态的synchronized方法需要从Class对象上获取锁。
简单的结构:

  synchronized(lock)
   {
          //访问或者修改被锁保护的共享状态。
   }

3.内部锁(监视器锁,或者对象内置的锁):每个java对象都可以隐式扮演一个用于同步的锁的角色。
4.获得内部锁的唯一途径:进入这个内部所得synchronized块或者方法。

注意:、内部锁在java扮演的就是互斥锁(mutex)的角色,一个时间点只能有一个线程获取锁,其他线程只能等待以及阻塞。要等待释放,否则一直等下去。

5.重进入(reentrancy):线程视图获取自己占用的锁时,它是被允许的,请求是成功的)。
***注意:***可重进入的性质表明:锁的请求是基于每线程的而不是每调用的。(子类,父类关系)。
6.可以用锁创建相关的协议,以保证线程对共享状态的独占访问,只要始终如一的遵循这些协议,就能确保状态的一致性。
复合操作在运行期间占有锁以保证其行为是原子的。
7。每一个共享的可变变量都需要由唯一一个确定的锁保护。
8.若类的不变约束涉及到多个状态变量,那么另外还需要一个附加请求也就是说每个参与到不变约束的变量由都有同一锁保护。
9.同步一个类的所有方法并不能确保其执行的复合 操作就是原子的。

10.synchronizedFactorizer 的同步策略就是:用servlet对象的内部锁去保护每一个状态变量,然而这个策略是通过同步整个service方法实现的,这种简单粗暴的方式虽然使我们重获安全性,但是代价比较高昂。
注意:假如把service声明成synchronized的,那么每一次只能有一个线程去执行它,这个是违背service能够同时处理多个请求的初中的。当负载过高时用户体验度极差。等待是很大的问题。。。

11.解决方案(演示案例 Factoring Service)

弱并发:缩小synchronized块的范围来维护线程安全性,能比较容易的提升servlet的并发性。但也不能太小,不能将一个原子操作分解成多个synchronized块。(1)块,检查在运行 (2)块,缓存number与factors的同步与更新。

猜你喜欢

转载自blog.csdn.net/qq_39213969/article/details/88785541