《java并发编程实战》笔记(一)

  最近在看《java并发编程实战》,希望自己有毅力把它读完。

  线程本身有很多优势,比如可以发挥多处理器的强大能力、建模更加简单、简化异步事件的处理、使用户界面的相应更加灵敏,但是更多的需要程序猿面对的是安全性问题。看下面例子:

public class UnsafeSequence {
    private int value;

    /*返回一个唯一的数值*/
    public int getNext(){
        return value++;
    }
}

  UnsafeSequence的问题在于,如果执行时机不对,那么两个线程在调用getNext时会得到相同的值,图1给出了这种错误情况。虽然递增运算value++看上去是单个操作,但事实上它包含三个独立的操作:  读取value、将value加1、将计算结果写入value。由于运行时可能将多个线程之间的操作交替执行,因此这两个线程可能同时执行读操作,从而使它们得到相同的值,并都将这个值加1。结果就是,在不同线程的调用中返回了相同的值。

在UnsafeSequence中说明的是一种常见的并发安全问题,称为竞态条件。当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。

再举个例子,延迟初始化中的竞态条件:

public class LazyInitRace {
    private HashMap<String, String> instance = null;

    public HashMap<String, String> getInstance(){
        if (instance == null) {
            instance = new HashMap<String, String>();
        }

        return instance;
    }
}

  在LazyInitRace中包含一个竞态条件,它可能会破坏这个类的正确性。假定线程A和线程B同时执行getInstance,A看到instance为空,因而创建一个新的HashMap实体,B同样需要判断instance是否为空,此时的instance是否为空,要取决于不可预测的时序,如果当B检查时,instance也为空,那么在两次调用getInstance时可能会得到不同的结果,即使getInstance通常被认为是返回相同的实例。

  java提供了锁机制来解决这一问题,但这些终归只是一些机制,要编写线程安全的代码,其核心在于要对状态访问操作进行管理。

一、对象的状态是指存储在状态变量(例如实例或者静态域)中的数据。如果一个对象无状态,它一定是线程安全的。

  

public class StatelessServlet implements Servlet {
    public void service(ServletRequest request, ServletResponse response)
            throws ServletException, IOException {
        int i = 1;
        i++;
        ...
    }
}

与大多数servlet相同,StatelessServlet是无状态的:它即不包含任何域,也不包含任何对其他类中域的引用。计算过程中的临时状态仅存在于线程栈上的局部变量中(这块需要对jvm内存分配有基础了解),并且只能由正在执行的线程访问。线程之间没有共享状态,由于线程访问无状态对象的行为并不会影响其他线程中操作的正确性,因此无状态对象是线程安全的。

二、用锁来保护状态

  内置锁

  java提供了一种内置的锁机制来支持原子性:同步代码块。同步代码块包括两部分:一个作为锁的对象引用,一个作为由这个锁保护的代码块。以关键字synchronized来修饰的方法就是一种横跨整个方法体的同步代码块,其中该同步代码块的锁就是方法调用所在的对象,一般不要这么做,这样会影响效率。

  synchronized(lock){

  //访问或修改由锁保护的共享状态

  }

  每一个java对象都可以用作一个实现同步的锁,这些锁被称为内置锁或者监视器锁。线程在进入同步代码块之前会自动获得锁,并且在退出同步代码块时自动释放锁。

  对象的内置锁与其状态之间没有内在的关联。虽然大多数类都将内置锁用做一种有效的加锁机制,但对象的域不一定要通过内置锁来保护。当获取与对象关联的锁时,并不能阻止其他线程访问该对象,某个线程在获得对象的锁以后,只能阻止其他线程获得同一个锁。之所以每个对象都有一个内置锁,只是为了免去显式的创建锁对象。

  开发中常见的内置锁的使用方法是,将所有的可变状态都封装在对象内部,并通过对象的内置锁对所有访问可变状态饿代码路径进行同步,使得在该对象上不会发生并发访问,例如,Vector和其他的同步集合类。

  

总之,以对象的状态来理解线程安全以及为什么加锁,是这部分的核心意思。

时间: 2024-12-28 08:42:12

《java并发编程实战》笔记(一)的相关文章

《OC疯狂讲义》笔记(一)

1.OC简介 Objective-C    继承自C和smalltalk 最小程度增加了面向对象的部分,是一门面向对象编程语言 1986. Next 得到授权 关于 Cocoa 框架(96) Cocoachina.com code4app.com 2.OC HelloWorld OC的文件有哪几种? .h  头文件(声明类) .m  OC源文件(类的实现) #import <Foundation/Foundation.h>  //导入文件 //main函数是OC得主入口函数 int main(

《OC疯狂讲义》笔记(二)

1.NSString的使用 C的字符串保存:1)字符数组      2)字符串的指针 OC中又专门的字符串处理的类(有字符串类型) NSString  不可变的字符串 NSMutableString 可变字符串 NSString 是OC字符串的类 1) NSString保存字符串 NSString *str = @"abc";    //用str保存字符串常量 创建空字符串 NSString *str = [NSString new]; str = @"xxxx";

《OC疯狂讲义》笔记(三)

1.类方法 1)什么是类方法 对象方法: -(返回值类型)方法名:(形参的类型) 形参名; 类方法 +(返回值类型)方法名:(形参的类型) 形参名; 2)类方法怎么去定义 +(返回值类型)方法名:(形参的类型) 形参名; 3)类方法的使用 调用: 对象方法:  [对象名    方法名:实参] 类方法:    [类名      方法名:实参] 4)使用类方法的好处 1)提高效率 2)减少内存空间占用 3)代码更加简洁 缺点:不能访问对象的成员变量 5)对象方法和类方法的对比 类方法        

《OC疯狂讲义》笔记(四)

1.两个关键字:self 和 super self 可以用在对象和类方法中 1)self用在对象方法中:指代的时调用当前对象方法的那个对象 2)self用在类方法中:指代的时当前类(实质是类对象) 总结:当self在方法中使用,谁调用这个方法,self指代的就是谁 super: 使用super的地方,一定存在继承关系 super调用父类的方法 2.面向对象的其他特性:继承 继承概念: 假设有两个类:A   B 继承是两个类之间的关系 假设声明B类的时候, 1)导入父类的头文件 2)设定继承关系

《OC疯狂讲义》笔记(五)

1.OC中的点语法 点语法: 对象名.成员变量名   (点语法不是访问成员变量,而是方法的调用) 它是一个编译器特性 点语法作用:可以替换传统的get和set方法的调用 点语法的前提: 首先得先有 get和set方法 点语法实质: 调用get和set方法 如果点语法出现在 = 的左边,表示设置值,相当于调用了set方法 p.age = 10; 替换为:[p setAge:10]; 如果点语法出现在 = 的右边,表示获取值,相当于调用了get方法 int age = p.age; 替换为:int

李刚OC语言疯狂讲义笔记

设计一个”学生“类1> 属性* 姓名* 生日用结构体作为类的实例变量(生日) #import <Foundation/Foundation.h> //定义生日的结构体 typedef struct{ int year; int month; int day; }MyDate; @interface Person : NSObject { @public NSString *_name;//定义姓名 MyDate _birthday;//定义生日 } @end @implementatio

java疯狂讲义笔记整理(第二版第一部分)

第一章    java语言概述 1.1    java语言的发展简史 1990末:    sun公司“Green计划”(James Gosling领导)        ----目的是智能家电编写一个通用嵌入式控制系统,为此创建oak 1992夏天:    "Green计划"完成新平台的部分功能 1992年11月:    "Green计划"被转为"FirstPerson有限公司"-一个sun的全资子公司,致力于创建一个高度互动的设备 1994夏天:

java8--类加载机制与反射(java疯狂讲义3复习笔记)

本章重点介绍java.lang.reflect包下的接口和类 当程序使用某个类时,如果该类还没有被加载到内存中,那么系统会通过加载,连接,初始化三个步骤来对该类进行初始化. 类的加载时指将类的class文件读入内存,并为之创建一个java.lang.class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象.(几乎所有的类都是java.lang.Class的实例); 所以JVM最先初始化的总是java.long.Object类. 在java中,一个类用

《Java疯狂讲义》中关联、组合和聚合的谬误

本文參考例如以下资料(建议阅读): a). difference-aggregation-acquaintance-and-composition-as-used-by-gang-of-four b). AssociationVsAggregationVsComposition c). Design Patterns Elements of Reusable Object-Oriented Software d). Java疯狂讲义(第三版) 1. 问题的由来 我看到的是这本书的第三版,在2.2

《小强软件测试疯狂讲义-性能及自动化》一书正式发布

本书即日起将在各大网上书店陆续发布,欢迎大家选购!可在china-pub.com,京东,当当,天猫等网店购买,搜索"小强软件测试疯狂讲义"关键字即可查找并购买了哦!   出版社官方天猫购买地址(推荐):https://detail.tmall.com/item.htm?id=547310727717 当当购买地址:http://product.dangdang.com/24239858.html 京东购买地址:https://item.jd.com/12161074.html chin