Java并发编程:volatile关键字解析

volatile这个关键字可能很多朋友都听说过,或许也都用过。在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果。在Java 5之后,volatile关键字才得以重获生机。

volatile关键字虽然从字面上理解起来比较简单,但是要用好不是一件容易的事情。由于volatile关键字是与Java的内存模型有关的,因此在讲述volatile关键之前,我们先来了解一下与内存模型相关的概念和知识,然后分析了volatile关键字的实现原理,最后给出了几个使用volatile关键字的场景。

以下是本文的目录大纲:

内存模型的相关概念
并发编程中的三个概念
Java内存模型
深入剖析volatile关键字
使用volatile关键字的场景
一.内存模型的相关概念

大家都知道,计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。

Spring boot整合Redis做数据缓存

redis是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set –有序集合)和hash(哈希类型)。这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。在此基础上,redis支持各种不同方式的排序。与memcached一样,为了保证效率,数据都是缓存在内存中。区别的是redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了master-slave(主从)同步。

Redis 是一个高性能的key-value数据库。 redis的出现,很大程度补偿了memcached这类key/value存储的不足,在部 分场合可以对关系数据库起到很好的补充作用。它提供了Java,C/C++,C#,PHP,JavaScript,Perl,Object-C,Python,Ruby,Erlang等客户端,使用很方便。

Redis支持主从同步。数据可以从主服务器向任意数量的从服务器上同步,从服务器可以是关联其他从服务器的主服务器。这使得Redis可执行单层树复制。存盘可以有意无意的对数据进行写操作。由于完全实现了发布/订阅机制,使得从数据库在任何地方同步树时,可订阅一个频道并接收主服务器完整的消息发布记录。同步对读取操作的可扩展性和数据冗余很有帮助。

redis的官网地址,非常好记,是redis.io。(特意查了一下,域名后缀io属于国家域名,是british Indian Ocean territory,即英属印度洋领地)目前,Vmware在资助着redis项目的开发和维护。

下面是官方的bench-mark数据:
测试完成了50个并发执行100000个请求。
设置和获取的值是一个256字节字符串。
Linux box是运行Linux 2.6,这是X3320 Xeon 2.5 ghz。
文本执行使用loopback接口(127.0.0.1)。
结果:读的速度是110000次/s,写的速度是81000次/s

redis提供五种数据类型:string,hash,list,set及zset(sorted set)。
string(字符串)
string是最简单的类型,你可以理解成与Memcached一模一样的类型,一个key对应一个value,其上支持的操作与Memcached的操作类似。但它的功能更丰富。
redis采用结构sdshdr和sds封装了字符串,字符串相关的操作实现在源文件sds.h/sds.c中。

list(双向链表)
list是一个链表结构,主要功能是push、pop、获取一个范围的所有值等等。操作中key理解为链表的名字。

dict(hash表)
set是集合,和我们数学中的集合概念相似,对集合的操作有添加删除元素,有对多个集合求交并差等操作。操作中key理解为集合的名字。

dict中table为dictEntry指针的数组,数组中每个成员为hash值相同元素的单向链表。set是在dict的基础上实现的,指定了key的比较函数为dictEncObjKeyCompare,若key相等则不再插入。

zset(排序set)
zset是set的一个升级版本,他在set的基础上增加了一个顺序属性,这一属性在添加修改元素的时候可以指定,每次指定后,zset会自动重新按新的值调整顺序。可以理解了有两列的mysql表,一列存value,一列存顺序。操作中key理解为zset的名字。

zset利用dict维护key -> value的映射关系,用zsl(zskiplist)保存value的有序关系。zsl实际是叉数不稳定的多叉树,每条链上的元素从根节点到叶子节点保持升序排序。

Spring对mongoDB分布式数据库整合

MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的。它支持的数据结构非常松散,是类似的格式,因此可以存储比较复杂的数据类型。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。

它的特点是高性能、易部署、易使用,存储数据非常方便。主要功能特性有:

面向集合存储,易存储对象类型的数据。
模式自由。
支持动态查询。
支持完全索引,包含内部对象。
支持查询。
支持复制和故障恢复。
使用高效的二进制数据存储,包括大型对象(如视频等)。
自动处理碎片,以支持云计算层次的扩展性。
支持RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
文件存储格式为BSON(一种JSON的扩展)。
可通过网络访问。
所谓“面向集合”(Collection-Oriented),意思是数据被分组存储在数据集中,被称为一个集合(Collection)。每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档。集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)。Nytro MegaRAID技术中的闪存高速缓存算法,能够快速识别数据库内大数据集中的热数据,提供一致的性能改进。

模式自由(schema-free),意味着对于存储在mongodb数据库中的文件,我们不需要知道它的任何结构定义。如果需要的话,你完全可以把不同结构的文件存储在同一个数据库里。

存储在集合中的文档,被存储为键-值对的形式。键用于唯一标识一个文档,为字符串类型,而值则可以是各种复杂的文件类型。我们称这种存储形式为BSON(Binary Serialized Document Format)。

Spring boot JPA实现

每次开发基本上都是使用mybatis,对于hibernate,估计也忘了差不多。今天来重温了一下hibernate的配置实现。

第一步,肯定是配置文件:

/**
 * Created by alan on 2018/6/18.
 */
@Configuration
public class HibernateConfig {

    private String driver = "com.mysql.jdbc.Driver";
    private String url = "jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8";
    private String username = "root";
    private String password = "root";

    public HibernateConfig(){
    }

    @Bean
    public BasicDataSource dataSource(){
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setInitialSize(5);
        dataSource.setMaxIdle(100);
        dataSource.setMaxOpenPreparedStatements(100);

        dataSource.setCacheState(true);
        dataSource.setMaxTotal(1000);
        dataSource.setTestWhileIdle(true);
        dataSource.setValidationQuery("SELECT 1");
        dataSource.setTimeBetweenEvictionRunsMillis(3600000L);
        dataSource.setMinEvictableIdleTimeMillis(1800000L);
        dataSource.setTestOnBorrow(true);
        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(){

        HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
        adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");

        Properties properties = new Properties();
        properties.setProperty("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
        properties.setProperty("hibernate.show_sql","true");
        properties.setProperty("hibernate.format_sql","true");
        properties.setProperty("hibernate.auto","update");

        LocalContainerEntityManagerFactoryBean factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setPersistenceUnitName("SpringJPA");
        factoryBean.setJpaVendorAdapter(adapter);
        factoryBean.setDataSource(this.dataSource());
        factoryBean.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
        factoryBean.setValidationMode(ValidationMode.NONE);

        /**
         * scan @Entity
         */
        factoryBean.setPackagesToScan("com.example.demo.model","com.example.demo.repository");
        //factoryBean.setMappingResources("com/example/demo/repository/resource/User.hbm.xml");
        factoryBean.setJpaProperties(properties);
        return factoryBean;
    }

    @Bean
    public PlatformTransactionManager platformTransactionManager(){
        return new JpaTransactionManager(this.entityManagerFactory().getObject());
    }

    @Bean
    public PersistenceAnnotationBeanPostProcessor persistenceAnnotationBeanPostProcessor(){
        return new PersistenceAnnotationBeanPostProcessor();
    }
    
}

配置文件由dateSource、LocalContainerEntityManagerFactoryBean、PlatformTransactionManager组成,改有的都有了,现在来实现DAO层,emmmmm,本人比较懒,直接在service层里直接写代码了,如下:

public class TestByHibernateServiceImpl {

    private static final Logger logger = LoggerFactory.getLogger(TestByHibernateServiceImpl.class);

    @PersistenceContext
    private EntityManager manager;

    @Resource
    private LxUserMapper userMapper;

    public TestByHibernateServiceImpl() {
    }

    public List<LxUserModel> getAll() {
        Query query = manager.createNativeQuery("SELECT *  FROM lx_user ORDER BY id DESC",LxUserModel.class);
        return query.getResultList();
    }

    public LxUserModel getRow() {
//        return manager.createQuery("SELECT a  FROM LxUserModel a ORDER BY id DESC",LxUserModel.class).setMaxResults(1).getSingleResult();
        return userMapper.getRow();
    }

    public int add(LxUserModel userModel) {
        manager.persist(userModel);
        return userModel.getId();
    }

哇塞,跟昨天写的那篇文章比,正篇肯真简短,没错大部分代码都是EntityManager去实现了,很多人包括我都因为懒,不去写自己的DAO实现,而去用开源的,如果有时间还是建议自己实现或看一下EntityManager的原理。

/**
 * Created by alan on 2018/6/18.
 */

@Repository
public interface LxUserMapper extends JpaRepository<LxUserModel,Integer> {

    @Query(value = "SELECT * FROM lx_user ORDER BY id DESC limit 1",nativeQuery = true)
    LxUserModel getRow();
}

 

下面提供一个例子,在真实场景中,查询数据可是千奇百怪,所以按照自己的思维封装了几个通用的方法,当然啦,肯定是使用了EntityManager:

/**
 * Created by alan.luo on 2017/10/22.
 */

public abstract class AbstractDefaultRepository<E extends Serializable> extends AbstractDefaultBaseRepository<E> {

    @PersistenceContext
    private EntityManager manager;

    /**
     * get all the data.
     * @param page
     * @param where
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public List<E> getAll(int page, List<PredicatePojo> where, String order, boolean isDesc) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<E> query = builder.createQuery(this.entityClass);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<E> select = query.select(root);
        if (order != null && !"".equals(order)){
            if (isDesc){
                select.orderBy(builder.desc(root.get(order)));
            }else {
                select.orderBy(builder.asc(root.get(order)));
            }
        }

        select.where(getListPredicate(where,builder,root));

        TypedQuery<E> typedQuery = this.manager.createQuery(select);
        typedQuery.setFirstResult((page - 1) * ConstantInit.PAGE_SIZE);
        typedQuery.setMaxResults(ConstantInit.PAGE_SIZE);
        return typedQuery.getResultList();
    }

    /**
     * search all the data.
     * @param page
     * @param condition
     * @param order
     * @param isDesc
     * @return
     */
    @Override
    public PagePojo<E> search(int page, List<PredicatePojo> condition, String order, boolean isDesc){

        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaQuery<Long> query = builder.createQuery(Long.class);

        Root<E> root = query.from(this.entityClass);
        CriteriaQuery<Long> select = query.select(builder.count(root));

        select.where(getListPredicate(condition,builder,root));

        long total = this.manager.createQuery(select).getSingleResult();

        PagePojo<E> pages = new PagePojo<E>((long) page,total);
        pages.setList(this.getAll(page,condition,order,isDesc));
        return  pages;
    }

    @Override
    public E getRowById(int id) {
        return this.manager.find(this.entityClass,id);
    }

    @Override
    public void add(E o) {
        this.manager.persist(o);
    }

    @Override
    public void update(E o) {
        this.manager.merge(o);
    }

    @Override
    public void remove(E o) {
        this.manager.remove(o);
    }

    @Override
    public int removeById(int id) {
        CriteriaBuilder builder = this.manager.getCriteriaBuilder();
        CriteriaDelete<E> q = builder.createCriteriaDelete(this.entityClass);

        return this.manager
                .createQuery(q.where(builder.equal(q.from(this.entityClass).get("id"),id)))
                .executeUpdate();
    }

    /**
     *
     * List<PredicatePojo> where = new ArrayList<>();
     *  where.add(new PredicatePojo("id","81", Criteria.lt));
     *  where.add(new PredicatePojo("age","10", Criteria.gt));
     *
     * List<PredicatePojo> where = new ArrayList<>();
     * if (userName != null){
     *     userName = "%" + userName + "%";
     *     List<PredicatePojo> like = new ArrayList<>();
     *     like.add(new PredicatePojo("username",userName, Criteria.like));
     *     like.add(new PredicatePojo("mobile",userName, Criteria.like));
     *     like.add(new PredicatePojo("email",userName, Criteria.like));
     *
     *     PredicatePojo pojo = new PredicatePojo();
     *     pojo.setLikeObj(like);
     *     pojo.setCriteria(Criteria.like);
     *     where.add(pojo);
     * }
     *
     *
     *
     * parser predicate.
     * @param where
     * @param builder
     * @param root
     * @return
     */
    protected Predicate[] getListPredicate(List<PredicatePojo> where,CriteriaBuilder builder,Root<E> root){

        Predicate[] predicates = new Predicate[where.size()];
        Predicate[] like;

        if (where != null && where.size() > 0){
            Predicate p = null;

            for (int i = 0; i < where.size();i++){

                Criteria ca = where.get(i).getCriteria();
                switch (ca){
                    case equal:
                        p = builder.equal(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case notEqual:
                        p = builder.notEqual(root.get(where.get(i).getKey()),where.get(i).getValue());
                        break;
                    case isNull:
                        p = builder.isNull(root.get(where.get(i).getKey()));
                        break;
                    case isNotNull:
                        p = builder.isNotNull(root.get(where.get(i).getKey()));
                        break;
                    case gt:
                        p = builder.gt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case lt:
                        p = builder.lt(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case ge:
                        p = builder.ge(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case le:
                        p = builder.le(root.get(where.get(i).getKey()),Double.valueOf(where.get(i).getValue()));
                        break;
                    case isFalse:
                        p = builder.isFalse(root.get(where.get(i).getKey()));
                        break;
                    case isTrue:
                        p = builder.isTrue(root.get(where.get(i).getKey()));
                        break;
                    case not:
                        p = builder.not(root.get(where.get(i).getKey()));
                        break;
                    case like:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.like(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.or(like);
                        break;
                    case notLike:
                        like = new Predicate[where.get(i).getLikeObj().size()];
                        for (int j = 0; j < where.get(i).getLikeObj().size();j++){
                            like[j] = builder.notLike(root.get(where.get(i).getLikeObj().get(j).getKey())
                                    ,where.get(i).getLikeObj().get(j).getValue());
                        }
                        p = builder.and(like);
                        break;
                }

                predicates[i] = p;
            }
        }
        return predicates;
    }

}

重温了一下JdbcTemplate读取数据库的例子

JdbcTemplate需要是一个DataSource实例去实现数据库链接,如下面代码:

private BasicDataSource dataSource;
private JdbcTemplate template;

public TestServiceImpl() {
    dataSource = new BasicDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(password);

    template = new JdbcTemplate(dataSource);
}

Spring提供了很多数据访问模板,这里使用的是JDBC,当然你也可以使用,比如orm.jpa.jpaTemplate、orm.jdo.JdoTemplate等。

访问模板实例化好了,接下来就是代码的实现,这里会给出3个例子,getAll、getRow、add。取全部,取一行,插入一行数据,直接看代码:

public List<LxUserModel> getAll() {
    RowMapper<List<LxUserModel>> rowMapper = new RowMapper<List<LxUserModel>>() {
        @Nullable
        @Override
        public List<LxUserModel> mapRow(ResultSet rs, int i) throws SQLException {

            List<LxUserModel> list = new ArrayList<>();

            ResultSetMetaData metaData = rs.getMetaData();
            int closNum = metaData.getColumnCount();

            do {
                LxUserModel m = new LxUserModel();
                m.setId(rs.getInt(1));
                m.setUsername(rs.getString(2));
                m.setLoginPassword(rs.getString(3));
                m.setNickname(rs.getString(4));
                m.setRealName(rs.getString(5));
                m.setEmail(rs.getString(6));
                m.setMobile(rs.getString(7));
                m.setSex(rs.getString(8));
                m.setAge(rs.getInt(9));
                m.setCreateTime(rs.getInt(10));
                list.add(m);

            } while (rs.next());

            return list;
        }
    };
    List<LxUserModel> list = template.queryForObject("SELECT * FROM `lx_user` WHERE 1 ORDER BY id DESC", rowMapper, (Object[]) null);

    return list;
}

调用模板方法queryForObject,返回的数据是一个List对象,这里需要我们实现RowMapper接口,并在mapRow方法中实现数据的组装。看完这里,如果你可以举一反三,那么不管是getRow还是getOne,我觉得对你而言都是很简单。下面来看看取一行的代码:

public LxUserModel getRow() {
    RowMapper<LxUserModel> rowMapper = new RowMapper<LxUserModel>() {
        @Nullable
        @Override
        public LxUserModel mapRow(ResultSet rs, int i) throws SQLException {

            LxUserModel m = new LxUserModel();
            m.setId(rs.getInt(1));
            m.setUsername(rs.getString(2));
            m.setLoginPassword(rs.getString(3));
            m.setNickname(rs.getString(4));
            m.setRealName(rs.getString(5));
            m.setEmail(rs.getString(6));
            m.setMobile(rs.getString(7));
            m.setSex(rs.getString(8));
            m.setAge(rs.getInt(9));
            m.setCreateTime(rs.getInt(10));
            return m;
        }
    };
    LxUserModel object = template.queryForObject("SELECT * FROM `lx_user` WHERE 1 ORDER BY id DESC limit 1", rowMapper, (Object[]) null);

    return object;
}

同样实现了RowMapper接口,只是返回的方法不一样,简单吧?

现在来看看add的方法,插入一行数据,意味着需要执行一条SQL语句。

public int add(LxUserModel userModel) {

    String sql = "INSERT INTO `lx_user`(`username`, `loginPassword`, `nickname`, `createTime`) VALUES ('%s','%s','%s','%s')";
    String finalSql = String.format(sql, userModel.getUsername(), userModel.getLoginPassword(), userModel.getNickname(), (System.currentTimeMillis() / 1000));

    logger.info("sql {}", finalSql);
    class StatementCallback2 implements StatementCallback<Integer> {

        @Nullable
        @Override
        public Integer doInStatement(Statement statement) throws SQLException, DataAccessException {
            return statement.execute(finalSql) ? 1 : 0;
        }
    }
    template.execute(finalSql);

    class ResRowMapper implements RowMapper<Integer> {

        @Nullable
        @Override
        public Integer mapRow(ResultSet resultSet, int i) throws SQLException {
            return resultSet.getInt("id");
        }
    }

    return template.queryForObject("select id  from `lx_user` where 1 order by id desc limit 1", Integer.class);
}

代码很直接粗暴,直接执行execute方法,这个方法是不会返回数据的,但是如果我们需要读取到刚刚插入数据的Id怎么办?这里返回使用
queryForObject方法只读取最后一行数据的id,并返回出去。

1242526272832
 
Copyright © 2008-2021 lanxinbase.com Rights Reserved. | 粤ICP备14086738号-3 |