阅读更多

17顶
2踩

编程语言

转载新闻 Java EE 6引入的JPA 2.0四大新特性详解

2010-01-26 10:05 by 正式记者 YiSingQ 评论(0) 有9749人浏览
    Java EE 5平台引入了Java持久化API(Java Persistence API,JPA),它为Java EE和Java SE应用程序提供了一个基于POJO的持久化模块。JPA处理关系数据与Java对象之间的映射,它使对象/关系(O/R)映射标准化,JPA已经被广泛采用,已经成为事实上的O/R持久化企业标准。
  Java EE 6带来了JPA的最新版本 — JSR 317:Java持久化2.0,JPA 2.0带来了许多新特性和增强,包括:
  1、对象/关系映射增强;
  2、Java持久化查询语言增强;
  3、一种新的基于标准的查询API;
  4、支持悲观锁定。
  对象/关系映射增强
  JPA 1.0支持集合的映射,但这些集合只能包含实体,JPA 2.0增加了集合映射的基础数据类型,如String和Integer,以及嵌入式对象的集合。JPA中的嵌入式对象是一个不能存在于它自身的对象,而是作为父对象的一部分存在,即它的数据不是存在于它自己的表中,而是嵌入在父对象的表中。
  JPA 2.0增加了两个支持新的集合映射的注解:@ElementCollection 和 @CollectionTable。使用@ElementCollection注解指定集合的嵌入式对象,这些集合是独立存储 href="http://storage.it168.com/" target=_blank>存储在集合表中的,使用@CollectionTable注解指定集合表的详细信息,如它包含的列。
  下面是一个嵌入式类,表示了车辆的访问服务,它存储了访问的日期,描述和费用,此外,车辆可以配备一或多个可选功能,每个功能是FeatureType类型的一个枚举值。
public enum FeatureType { AC, CRUISE, PWR, BLUETOOTH, TV, ... }
        @Embeddable
        public class ServiceVisit {
        @Temporal(DATE)
        @Column(name="SVC_DATE")
         Date serviceDate; 
        String workDesc;  
        int cost;
    } 
  枚举值和嵌入式对象可以在一个表示车辆服务历史的实体中使用,如:
@Entity
    public class Vehicle {
          @Id int vin;
          @ElementCollection
          @CollectionTable(name="VEH_OPTNS")
    .     @Column(name="FEAT")Set<FeatureType> optionalFeatures;
          @ElementCollection
          @CollectionTable(name="VEH_SVC")
          @OrderBy("serviceDate")
          List<ServiceVisit> serviceHistory;
          ...    
    } 
  Vehicle实体中的第一对注解@ElementCollection 和 @CollectionTable指定FeatureType值存储在VEH_OPTNS集合表中,第二对注解@ElementCollection 和 @CollectionTable指定ServiceVisit嵌入式对象存储在VEH_SVC集合表中。
  虽然在例子中没有显示,@ElementCollection注解有两个属性:targetClass 和 fetch。targetClass属性指定基础类或嵌入式类的类名,如果字段或属性是使用泛型定义的,那这两个属性是可选的,上面这个例子就是这样。Fetch属性是可选的,它指定集合是延后检索还是立即检索,使用javax.persistence.FetchType常量,值分别用LAZY和EAGER,默认情况下,集合是延后匹配的。
  JPA 2.0中还有其它许多关于对象/关系映射的增强,例如,JPA 2.0支持嵌套式嵌入,关系嵌入和有序列表,也增加了新的注解增强映射功能,通过@Access注解更灵活地支持特定的访问类型,更多用于实体关系的映射选项,如对单向一对多关系的外键映射支持,通过@MapsId注解支持派生身份,支持孤体删除。
  Java持久化查询语言增强
  JPA 1.0定义了一个广泛的Java持久化查询语言,使用它你可以查询实体和它们的持久化状态。JPA 2.0对JPQL做了大量改进,如现在可以在查询中使用case表达式。在下面的查询中,如果雇员的评分为1,则通过乘以1.1对雇员的薪水进行了增长,如果评分为2,则乘以1.05,其它评分则乘以1.01。
UPDATE Employeee
     SET e.salary =
        CASE WHEN e.rating = 1 THEN e.salary * 1.1
             WHEN e.rating = 2 THEN e.salary * 1.05
             ELSE e.salary * 1.01
        END
  JPA 2.0也为JPQL增加了大量新的运算符,如NULLIF和COALESCE,当数据库使用其它非空数据解码时,NULLIF运算符是非常有用的,使用NULLIF,你可以在查询中将这些值转换为空值,如果参数等于NULLIF,NULLIF会返回空值,否则返回第一个参数的值。
  假设薪水数据保存在employee表中,数据类型为整数,却掉的薪水解码为-9999,下面的查询返回薪水的平均值,为了正确地忽略却掉的薪水,查询使用NULLIF将-9999转换为空值。
SELECT AVG(NULLIF(e.salary, -99999))
     FROM Employeee
  COALESCE运算符接收一串参数,从列表中返回第一个非空值,相当于下面的case表达式:
CASE WHEN value1 IS NOT NULL THEN value1
          WHEN value2 IS NOT NULL THEN value2
          WHEN value3 IS NOT NULL THEN value3
          ...
          ELSE NULL    END
  COALESCE运算符接收一串参数,从列表中返回第一个非空值,相当于下面的case表达式:
SELECT Name, COALESCE(e.work_phone, e.home_phone) phone
     FROM Employeee 
  假设employee表包括一个办公电话号码和家庭电话号码列,无电话号码的列使用空值表示。下面的查询返回每个雇员的姓名和电话号码,COALESCE运算符指定查询返回办公电话号码,但如果为空,则返回家庭电话号码,如果两者都为空,则返回一个空值。
  JPA 2.0向JPQL增加的其它运算符是INDEX,TYPE,KEY,VALUE和ENTRY。INDEX运算符指定查询时的排序顺序,TYPE运算符选择一个实体的类型,将查询限制到一或多个实体类型,KEY,VALUE和ENTRY运算符是JPA 2.0中的泛化映射功能的一部分。使用KEY运算符提取映射键,VALUE运算符提取映射值,ENTRY运算符选择一个映射实体。
  此外,JPA 2.0增加了选择列表、以及集合值参数和非多态查询中运算符的支持。

  标准的API
  JPA 2.0中引入的另一个重要特性是标准的API,利用这个API可以动态地构建基于对象的查询,本质上,这个标准API等价于面向对象的JPQL,使用它,你可以使用基于对象的方法创建查询,不再需要JPQL使用的字符串操作。
  标准API是基于元模型的,元模型是一个提供了架构级关于持久化单元托管类的元数据的抽象模型, 元模型让你构建强类型的查询,它也允许你浏览持久化单元的逻辑结构。
  通常,一个注解处理器使用元模型生成静态元模型类,这些类提供持久化状态和持久化单元托管类的关系,但你可以对静态元模型类编码。下面是一个实体实例:
@Entity public class Employee {
       @Id Long Id;
       String firstName;
       String lastName;
       Department dept;     } 
  下面是对应的静态元模型类:
import javax.persistence.meta,model.SingularAttribute;
import javax.persistence.meta,model.StaticMetamodel; 
    @Generated("EclipseLink JPA 2.0 Canonical Model Generation"
    @StaticMetamodel(Employee.class)
    public class Employee_ {
       public static volatile SingularAttribute<Employee, Long> id;
      public static volatileSingularAttribute<Employee, String> firstName;
       public static volatile SingularAttribute<Employee, String> lastName;
       public static volatile SingularAttribute<Employee, Department> dept;
      } 
  此外,JPA 2.0元模型API允许你动态访问元模型,因此当你使用标准API时,既可以静态访问元模型类,也可以动态访问元模型类。标准API提供了更好的灵活性,既可以使用基于对象的方法,又可以使用基于字符串的方法导航元模型,这意味着你有四种使用标准API的方法:
  1、静态使用元模型类
  2、静态使用字符串
  3、动态使用元模型
  4、动态使用字符串
  无论你使用哪种方法,通过构造一个CriteriaQuery对象定义一个基于标准API的查询时,使用一个工厂对象CriteriaBuilder构造CriteriaQuery,可以从EntityManager 或 EntityManagerFactory类中获得CriteriaBuilder。下面的代码构造一个CriteriaQuery对象:
EntityManager em = ... ;
     CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<Customer> cq = cb.createQuery(Customer.class); 
  注意CriteriaQuery对象是泛型类型,使用CriteriaBuilder 的createQuery方法创建一个CriteriaQuery,并为查询结果指定类型。在这个例子中,createQuery 方法的Employee.class参数指定查询结果类型是Employee。CriteriaQuery对象和创建它们的方法是强类型的。
接下来,为CriteriaQuery对象指定一个或多个查询源,查询源表示查询基于的实体。你创建一个查询源,然后使用AbstractQuery接口的from()方法将其添加到查询。AbstractQuery接口是众多接口中的一员,如CriteriaQuery,From和root,它们都定义在标准API中。CriteriaQuery接口继承AbstractQuery接口的属性。
  from()方法的参数是实体类或EntityType实体的实例,from()方法的结果是一个Root对象,Root接口扩展From接口,它表示某个查询的from子句中可能出现的对象。
  下面的代码增加一个查询源到CriteriaQuery对象:
CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
     Root<Employee> emp = cq.from(Employee.class); 
  当你向CriteriaQuery对象添加一个或多个查询源后,你访问元模型,然后构造一个查询表达式,你如何做取决于你是以静态方式提交查询还是以动态方式提交查询,以及是使用元模型还是字符串导航元模型。下面是一个使用元模型类静态查询的例子:
cq.select(emp);
     cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));
     TypedQuery<Employee> query = em.createQuery(cq);
     List<Employee> rows = query.getResultList(); 
  CriteriaQuery接口的select() 和 where()方法指定查询结果返回的选择项目。
  注意,你使用EntityManager创建查询时,可以在输入中指定一个CriteriaQuery对象,它返回一个TypedQuery,它是JPA 2.0引入javax.persistence.Query接口的一个扩展,TypedQuery接口知道它返回的类型。
  在元模型术语中,Employee_是对应于Employee实体类的规范化元模型类,一个规范化元模型类遵循JPA 2.0规范中描述的某些规则。例如,元模型类的名字来源于托管类,一般都是在托管类名字后面追加一个下划线“_”。一个规范化元模型是一个包含静态元模型类的元模型,这个静态元模型对应于实体,映射的超类,以及持久化单元中的嵌入式类。实际上,这个查询使用了规范化元模型。下面是一个完整的查询:
EntityManager em = ... ;
     CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
     Root<Employee> emp = cq.from(Employee.class);
     cq.select(emp);
     cq.where(cb.equal(emp.get(Employee_.lastName), "Smith"));
     TypedQuery<Employee> query = em.createQuery(cq);
     List<Employee> rows = query.getResultList(); 
  下面是使用元模型API查询的动态版本:
EntityManager em = ... ;
     CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
     Root<Employee> emp = cq.from(Employee.class);
     EntityType<Employee> emp_ = emp.getModel();
     cq.select(emp);
     cq.where(cb.equal(emp.get(emp_.getSingularAttribute("lastName", String.class)),"Smith"));
     TypedQuery<Employee> query=em.createQuery(cq);
     List<Employee> rows=query.getResultList(); 
  使用元模型API的标准查询提供了与使用规范化元模型相同的类型,但它比基于规范化元模型的查询更冗长。
  Root的getModel()方法返回根对应的元模型实体,它也允许运行时访问在Employee实体中声明的持久化属性。
  getSingularAttribute()方法是一个元模型API方法,它返回一个持久化的单值属性或字段,在这个例子中,它返回值为Smith 的lastName属性。下面是使用字符串的元数据导航查询的静态版本:
EntityManager em = ... ;
     CriteriaBuilder cb = em.getCriteriaBuilder();
     CriteriaQuery<Employee> cq = cb.createQuery(Employee.class);
     Root<Employee> emp = cq.from(Employee.class);
     cq.select(emp);
     cq.where(cb.equal(emp.get("lastName"), "Smith"));
     TypedQuery query = em.createQuery(cq);
     List <Employee>rows = query.getResultList(); 
  这个基于字符串的方法要相对容易使用些,但却失去了元模型具有的类型安全href="http://safe.it168.com/" target=_blank>安全。


  支持悲观锁
  锁是处理数据库事务并发的一种技术,当两个或更多数据库事务并发地访问相同数据时,锁可以保证同一时间只有一个事务可以修改数据。
  锁的方法通常有两种:乐观锁和悲观锁。乐观锁认为多个并发事务之间很少出现冲突,也就是说不会经常出现同一时间读取或修改相同数据,在乐观锁中,其目标是让并发事务自由地同时得到处理,而不是发现或预防冲突。两个事务在同一时刻可以访问相同的数据,但为了预防冲突,需要对数据执行一次检查,检查自上次读取数据以来发生的任何变化。
  悲观锁认为事务会经常发生冲突,在悲观锁中,读取数据的事务会锁定数据,在前面的事务提交之前,其它事务都不能修改数据。
  JPA 1.0只支持乐观锁,你可以使用EntityManager类的lock()方法指定锁模式的值,可以是READ或WRITE,如:
EntityManager em = ... ;
     em.lock (p1, READ);
  对于READ锁模式,JPA实体管理器在事务提交前都会锁定实体,检查实体的版本属性确定实体自上次被读取以来是否有更新,如果版本属性被更新了,实体管理器会抛出一个OptimisticLockException异常,并回滚事务。
  对于WRITE锁模式,实体管理器执行和READ锁模式相同的乐观锁操作,但它也会更新实体的版本列。
  JPA 2.0增加了6种新的锁模式,其中两个是乐观锁。JPA 2.0也允许悲观锁,并增加了3种悲观锁,第6种锁模式是无锁。
  下面是新增的两个乐观锁模式:
  1、OPTIMISTIC:它和READ锁模式相同,JPA 2.0仍然支持READ锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC。
  2、OPTIMISTIC_FORCE_INCREMENT:它和WRITE锁模式相同,JPA 2.0仍然支持WRITE锁模式,但明确指出在新应用程序中推荐使用OPTIMISTIC_FORCE_INCREMENT。
  下面是新增的三个悲观锁模式:
  1、PESSIMISTIC_READ:只要事务读实体,实体管理器就锁定实体,直到事务完成锁才会解开,当你想使用重复读语义查询数据时使用这种锁模式,换句话说就是,当你想确保数据在连续读期间不被修改,这种锁模式不会阻碍其它事务读取数据。
  2、PESSIMISTIC_WRITE:只要事务更新实体,实体管理器就会锁定实体,这种锁模式强制尝试修改实体数据的事务串行化,当多个并发更新事务出现更新失败几率较高时使用这种锁模式。
  3、PESSIMISTIC_FORCE_INCREMENT:当事务读实体时,实体管理器就锁定实体,当事务结束时会增加实体的版本属性,即使实体没有修改。
  你也可以指定新的锁模式NONE,在这种情况下表示没有锁发生。
  JPA 2.0也提供了多种方法为实体指定锁模式,你可以使用EntityManager的lock() 和 find()方法指定锁模式。此外,EntityManager.refresh()方法可以恢复实体实例的状态。
  下面的代码显示了使用PESSIMISTIC_WRITE锁模式的悲观锁:
// read
     Part p = em.find(Part.class, pId);
      // lock and refresh before update
    em.refresh(p, PESSIMISTIC_WRITE);
     int pAmount = p.getAmount();
     p.setAmount(pAmount - uCount); 
  在这个例子中,它首先读取一些数据,然后应用PESSIMISTIC_WRITE锁,在更新数据之前调用EntityManager.refresh()方法,当事务更新实体时,PESSIMISTIC_WRITE锁锁定实体,其它事务就不能更新相同的实体,直到前面的事务提交。
  更多JPA 2.0的新特性
  除了前面描述的增强和新特性外,JPA 2.0可以使用Bean验证自动验证实体,这意味着你可以在实体上指定一个约束,例如,实体中字段的最大长度为15,当实体持久化,更新或移除时自动验证字段,你可以在persistence.xml配置文件中使用元素指定自动验证生效的周期。
17
2
评论 共 0 条 请登录后发表评论

发表评论

您还没有登录,请您登录后再发表评论

相关推荐

  • .NET中的内存管理,GC机制,内存释放过程,各种内存释放方法

    GC,通往自由的大道 -------------------------------------------------------------------------------- 摘要 本文将讲述.NET中的内存管理,GC机制,内存释放过程,各种内存释放方法等,并利用大量示例讲述如何操作并优化回收,另外也讨论一些有关的注意事项。 -----------...

  • .NET中栈和堆的比较 #4

    终于翻完了第四篇,本来每次都是周末发的,可惜上周末有些事儿没忙过来,所以今天中午给补上来。不知道这套文章还能不能继续了,因为作者也只写到了第四篇,连他都不知道第五篇什么时候出得来...原文出处http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_memory_401282006141834PM/csharp_memory_4.aspx可以...

  • .NET Framework 自动内存管理机制深入剖析 (转载)

    在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全性检查),而非托管资源则不必接受.NET Framework的CLR管理. (了解更多区别请参阅.NET Framework或C#的高级编程资料) 托管资源在.NET Framework中...

  • .NET Framework 自动内存管理机制深入剖析 (C#分析篇)

    在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为”托管资源”和”非托管资源”.托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全性检查),而非托管资源则不必接受.NET Framework的CLR管理. (了解更多区别请参阅.NET Framework或C#的高级编程资料)托管资源在.NET Framework中又分别存放在两

  • 垃圾回收GC:.Net自动内存管理 上(一)内存分配

    .NET CLR将所有资源分配到托管堆上,这有点像C语言中的堆但是你不用去释放资源因为闲置资源在.NET中将被自动释放。现在就有一个问题了,托管堆是怎么知道一个对象什么时候将不再被程序使用?现今有很多的GC算法。每一个算法都针对某一特定环境进行调优,进而获得最好的性能。这篇文章着重于.NET CLR使用的GC算法。.Net下的GC完全解决了开发者跟踪内存使用以及控制释放内存的窘态。然而,你或许想要理解GC是怎么工作的。此系列文章中将会解释内存资源是怎么被合理分配及管理的,并包含非常详细的内在算法描述。同时,

  • .NET内存管理学习

    本文是C#高级编程的学习记录和总结。1,为什么要有内存管理?C#编程的优点是程序员不用担心具体的内存管理,垃圾回收器会自动处理所所有的内存清理工作。用户可以得到接近C++语言那样的效率,而不用考虑C++复杂的内存清理工作,大大提高生产力。虽不用自己关心内存清理,但也要了解回收原理,写出高效率的代码。2,栈和堆a,堆栈里存什么栈存储的数据类型有:值类型,如int,float,bool等;对象的地址(...

  • .NET高性能编程 - C#如何安全、高效地玩转任何种类的内存之Memory(三)

    前言 我们都知道,.Net Core是微软推出的一个通用开发平台,它是跨平台和开源的,由一个.NET运行时、一组可重用的框架库、一组SDK工具和语言编译器组成,旨在让.Net developers可以更容易地编写高性能的服务应用程序和基于云的可伸缩服务,比如微服务、物联网、云原生等等;在这些场景下,对于内存的消耗往往十分敏感,也十分苛刻;为了解决这个棘手问题,同时释放应用开发人员的精力,让他们能够...

  • .Net程序的内存管理和垃圾回收机制

    .NET 内存管理和垃圾回收 C/C++ 程序需要开发者手动分配和释放内存,.Net程序则使用垃圾回收技术自动收集不再使用的内存。垃圾回收器(GC)使用引用 跟踪占用内存的对象,如果对象被设置为null或已不在使用范围,GC就会标志该对象为可回收,这样GC就可以回收被这些对象占用的内存。 垃圾回收器(GC)使用Win32® VirtualAlloc() 接口为自己的堆分配内存,.Ne

  • .NET深入学习笔记(3):垃圾回收与内存管理

    Posted on 2009-02-19 00:27 Frank Xu Lei 阅读(3070) 评论(41)  编辑 收藏 网摘 所属分类: .NET深入学习笔记 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"xmlns:dc="http://purl.org/d

  • w3wp.exe 发生.net framework异常_可能会导致.NET内存泄露的8种行为

    任何有经验的.NET开发人员都知道,即使.NET应用程序具有垃圾回收器,内存泄漏始终会发生。 并不是说垃圾回收器有bug,而是我们有多种方法可以(轻松地)导致托管语言的内存泄漏。内存泄漏是一个偷偷摸摸的坏家伙。 很长时间以来,它们很容易被忽视,而它们也会慢慢破坏应用程序。 随着内存泄漏,你的内存消耗会增加,从而导致GC压力和性能问题。 最终,程序将在发生内存不足异常时崩溃。在本文中,我们将介绍.N...

  • .NET内存管理必备知识

    小型对象如何处理 小型对象是被分配在小型对象堆SOH上的。SOH有3代,分别是:第0代,第1代,第2代。对象根据寿命向上移动。将新对象放在Gen 0上。当第0代充满时,.NET垃圾收集器会处理不需要的对象,并将其它内容移至第1代上,如果第1代充满了那么垃圾回收会再次运行处理不需要的对象,并将其它内容移至第2代上。那么当第2代充满时会发生垃圾回收完全运行。将清除不需要的第2代对象,并将第1代对象移动到第2代上,然后将第0代对象移动到第1代上,最后清除所有未引用内容。每次运行垃圾回收后会压缩受影响的堆,将仍然在

  • [原]发现并解决ASP.NET内存耗尽(OOM),让服务器"永不重启"

    对于大型程序,完全依赖GC是不现实的,对于高负载服务器,往往我们80%的堆都由自己的析构函数接管,并辅助以自行设计的bufferpool接管堆释放工作以达到HEAP可控的目的,减少CPU突发性负荷(CPU尖峰).虽然不像C那样可以控制的那么完全,但是多多少少对OOM的发生起到抑制作用,深入下去可以完全避免OOM......好了IF性能和内存开销没什么追求的 THEN 就不必看了,,,,ELSE

  • .Net 内存管理和垃圾回收(二)垃圾回收机制

    本文是翻译Memory Management and Garbage Collection in .NET,本人英语水平不行,语文水平也不行,若有错误恳请评论指正。本文权当是英语翻译练习。 .Net 内存管理和垃圾回收(一)非托管资源清除 .Net 内存管理和垃圾回收(二)垃圾回收机制 垃圾回收(GC)基础 GC在CLR中充当一个自动的内存管理器,它有以下优点: 使你可以...

  • ASP.NET 的内存不足问题

    ASP.NET中的OutOfMemoryException 问题 OutOfMemoryException异常如何处理 在什么情况下会出现OutOfMemonryException呢 ?引&lt;&lt;.net框架程序设计修订版&gt;&gt;的话: 在我们试图新建一个对象时,而垃圾收集器又找不到任何可用内存时被抛出,这种情况下我们是可以捕获该异常的,另一种情况是,CLR需要内存时...

  • .Net内存管理基础知识

    托管堆不同于堆,它是由CLR(公共语言运行库(Common Language Runtime))管理,当堆中满了之后,会自动清理堆中的垃圾。所以,做为.net开发,我们不需要关心内存释放的问题。堆栈:是一种后进先出的数据结构,它是一个概念,栈是一种后进先出的数据结构。栈区:存放函数的参数、局部变量、返回数据等值,由编译器自动释放。堆区:存放着引用类型的对象,由CLR释放。②内存堆栈:存在内存中的两个存储区(堆区,栈区)。

  • 讨论.NET的替代内存管理策略

    在本文中,我们将讨论依赖垃圾收集器作为内存管理策略带来的问题。然后我们讨论可能的替代解决方案,包括微软自己的研究,将非托管堆引入.NET框架。

  • .NET内存管理

    作为web方面的应用层开发人员,不可能和系统程序员一般对操作系统底层运行原理和机制一清二楚,不同类型的开发者有不同的知识结构,例如要求c程序员去实现论坛,或要求web开发人员精通cpu调度算法,页面调度算法等都不现实。但是对于内存管理、线程安全这些必须的“基础”知识,是非常有必要去学习清楚的,这是作为一个合格的程序员的基础(ps:文章主要内容以及测试Demo大多来源于《CLR.via.C# 第三版

Global site tag (gtag.js) - Google Analytics