精华内容
下载资源
问答
  • java高并发

    千次阅读 多人点赞 2018-02-02 18:11:32
    并发

    转载地址:https://www.cnblogs.com/lr393993507/p/5909804.html

        对于开发的网站,如果网站的访问量非常大,那么我们应该考虑相关的、并发访问问题,并发是绝大部分程序员头疼的问题;

    为了更好的理解并发和同步,先明白两个重要的概念:异步和同步;

     1、同步和异步的区别和联系

    所谓同步就是执行完一个函数后方法后,一直等待系统返回值或消息,这是程序是阻塞的,只有接受到返回值或者消息才会往下执行其他命令

    异步,执行完函数或方法后,不必阻塞的等待系统返回值或消息,只需向系统委托一个异步过程,那么当系统接受到返回值或者消息,系统会

    自动触发异步过程,完成一个完整的流程

    同步在一定程度上可以看成单线程,线程请求一个方法后就等待这个方法给回复,否则不会继续执行下去(死心眼);

    异步在一定程度上可以看成多线程,请求一个方法后就不管了,继续执行其他的方法

    同步就是事情一件一件的做,异步就是做一件事,不影响做其他事情。

    例:吃饭和说话是同步的,只能一件一件的做,因为只有一张嘴

    吃饭和听音乐是异步的,吃饭和听音乐互不影响

          

         对于java程序员,我们经常会听到同步关键字synchronized,假如这个同步的监视对象是类,那么如果一个对象访问类里面的同步方法是,另一个对象

    也访问这个类里面的同步方法,这是就会进入阻塞状态,只有等前一个对象执行完同步方法后,后一个对象才能继续执行该方法,这就是同步。相反,

    如果没有被同步关键字synchronize修饰,两个对象可以在同一时间访问同一方法,这就是异步。

        脏数据

    脏读就是指当一个事物正在访问数据,并且对数据做了修改,但是数据修改并未提交到数据库,而另一个事物也在访问这个数据,然后使用数据,因为

    这个数据是还没提交的数据,所以另一个事务读取的数据就是脏数据(dirty data),依据脏数据做的操作是不正确的

    不可重复读

    不可重复读是指一个事务内多次读取同一个数据,在这个事务还没有结束的时候,另一个事务也访问同一数据并做了修改,这时第一个事务中两次读取的

    数据可能是不一样的,这样就发生在 一个事务内读取的数据不一样,因此成为不可重复读


    二、如何处理并发和同步

    处理并发和同步问题主要是通过锁机制,锁机制主要有两个层面

    一种是代码层次的,如java的同步锁,典型的就是同步关键字synchronize,这里不做过多的讲解:

    感兴趣的可以参考:http://www.cnblogs.com/xiohao/p/4151408.html

    另一种是数据库层次的,比较典型的是悲观锁和乐观锁,这里重点讲解的就是悲观锁(传统的物理锁)和乐观锁

    悲观锁(Prssimistic Locking)

    悲观锁:正如其名,它是指对数据被外界(包括本系统当前的其他事务和外部系统的事务处理)修改保持保守态度,因此在数据处理过程中,

    将数据处于锁定状态

    悲观锁的实现机制,往往依靠数据库提供的锁机制(也只有数据库层的锁机制才能真正保持数据访问的排他性,否则即使在本系统中实现了加锁机制,也无法

    保证外部系统不会修改数据)

    一个典型的依赖数据库的悲观锁调用:

    select * form account where  name=‘admin’  for  update

    这条sql语句锁定account表中所有符合条件查询的记录,在事物提交前(事务提交时会释放是事务过程中的锁),外界无法修改这些记录

    hibernate的悲观锁也是基于数据库的锁机制实现的

    下列代码实现了hibernate的悲观锁

    String hql="from User where  user.name=''admin";

    Query query=session.createQuery(hql);

    query.setLock('user",LockMode.UPGRADE);//加锁

    List queryList=query.list()

    query.setLock对查询语句中,特定别名所对应的记录进行加锁,对返回的user记录进行加锁

    观察运行期 Hibernate 生成的 SQL 语句: 
          select tuser0_.id as id, tuser0_.name as name, tuser0_.group_id
          as group_id, tuser0_.user_type as user_type, tuser0_.sex as sex
          from t_user tuser0_ where (tuser0_.name='Erica' ) for update
         这里 Hibernate 通过使用数据库的 for update 子句实现了悲观锁机制。 
          Hibernate 的加锁模式有: 
          Ø LockMode.NONE : 无锁机制。 
          Ø LockMode.WRITE : Hibernate 在 Insert 和 Update 记录的时候会自动获取
          Ø LockMode.READ : Hibernate 在读取记录的时候会自动获取。 
          以上这三种锁机制一般由 Hibernate 内部使用,如 Hibernate 为了保证 Update
          过程中对象不会被外界修改,会在 save 方法实现中自动为目标对象加上 WRITE 锁。 
          Ø LockMode.UPGRADE :利用数据库的 for update 子句加锁。 
          Ø LockMode. UPGRADE_NOWAIT : Oracle 的特定实现,利用 Oracle 的 for
          update nowait 子句实现加锁。 
          上面这两种锁机制是我们在应用层较为常用的,加锁一般通过以下方法实现: 
          Criteria.setLockMode
          Query.setLockMode
          Session.lock

    注意:只有在查询开始之前(hibernate生成sql之前)加锁 ,才会真正通过数据库的锁机制进行加锁,否则数据已经通过不包含for uodate 

    的sql加载进来,此时无法进行加锁

      为了更好的理解select... for update的锁表的过程,本人将要以mysql为例,进行相应的讲解

          1、要测试锁定的状况,可以利用MySQL的Command Mode ,开二个视窗来做测试。

              表的基本结构如下:

              

     

               表中内容如下:

               

     

              开启两个测试窗口,在其中一个窗口执行select * from ta for update0

              然后在另外一个窗口执行update操作如下图:

              

              等到一个窗口commit后的图片如下:

              

               到这里,悲观锁机制你应该了解一些了吧~

    注意:for  update 要放在mysql的事物中,即begin和end之间,否则不起作用

       至于是锁住整个表还是锁住选中的行,请参考:

               http://www.cnblogs.com/xiohao/p/4385768.html

                至于hibernate中的悲观锁使用起来比较简单,这里就不写demo了~感兴趣的自己查一下就ok了~


    乐观锁

    相对于悲观锁,乐观锁采取更加宽松的加锁机制。悲观锁主要是通过数据库的加锁机制实现,保证操作最大程度的独占性,

    但随之而来的是数据库性能的大量开销,对于长事务而言,这样的开销是无法承受的。如一个金融系统,当某个操作员读取用户

    的数据,并在读出的用户数据的基础上进 行修改时(如更改用户帐户余额),如果采用悲观锁机制,也就意味着整个操作过 中

    (从操作员读出数据、开始修改直至提交修改结果的全过程,甚至还包括操作 员中途去煮咖啡的时间),数据库记录始终处于

    加锁状态,可以想见,如果面对几 百上千个并发,这样的情况将导致怎样的后果。 乐观锁机制在一定程度上解决了这个问题。

    乐观锁大多是同过数据库版本(version)记录机制实现的,即为数据增加一个版本标识,在基于数据库表的解决方案中,一般是

    在表中增加一个version字段来实现,读取数据时将版本号一起读取,在更新操作时将版本号加1,将提交的数据的版本号和数据库中

    的数据版本号进行比较,若大于数据库中数据的版本号,则提交更新数据,否则认为是过期数据,无法提交,抛出异常。


    对于上面修改用户帐户信息的例子而言,假设数据库中帐户信息表中有一个 version 字段,当前值为 1 ;而当前帐户余额字段( balance )

    为 $100 。操作员 A 此时将其读出( version=1 ),并从其帐户余额中扣除 $50( $100-$50 )。 2 在操作员 A 操作的过程中,操作员B

    也读入此用户信息( version=1 ),并 从其帐户余额中扣除 $20 ( $100-$20 )。 3 操作员 A 完成了修改工作,将数据版本号加一(

    version=2 ),连同帐户扣 除后余额( balance=$50 ),提交至数据库更新,此时由于提交数据版本大 于数据库记录当前版本,数据被

    更新,数据库记录 version 更新为 2 。 4 操作员 B 完成了操作,也将版本号加一( version=2 )试图向数据库提交数 据( balance=$80 ),

    但此时比对数据库记录版本时发现,操作员 B 提交的 数据版本号为 2 ,数据库记录当前版本也为 2 ,不满足 “ 提交版本必须大于记 录

    当前版本才能执行更新 “ 的乐观锁策略,因此,操作员 B 的提交被驳回。 这样,就避免了操作员 B 用基于version=1 的旧数据修改的

    结果覆盖操作 员 A 的操作结果的可能。 从上面的例子可以看出,乐观锁机制避免了长事务中的数据库加锁开销(操作员 A和操作员 B 操

    作过程中,都没有对数据库数据加锁),大大提升了大并发量下的系 统整体性能表现。

    需要注意的是乐观锁是基于数据存储的逻辑,因此具备一定的局限性,如在上例中,由于乐观锁是在我们的系统内实现的,来自外部系统的

    用户余额更新操作不受系统的限制,因此可能会造成脏数据更新到数据库中。在系统设计时,我们应该充分考虑这些情况,并作出相应的调整,如将

    乐观锁放在数据库的存储过程中,对外开放基于此存储过程的更新途径,而不是直接将数据库表对外公开。 hibernate中数据访问引擎中实现

    了乐观锁,如果不考虑外部系统的数据库的操作,可以考察通过hibernate提供的透明乐观锁实现,将大大提升生产力

    User.hbm.xml


    复制代码
    <?xml version="1.0"?>
    <!DOCTYPE hibernate-mapping PUBLIC
            "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
            "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
     
    <hibernate-mapping package="com.xiaohao.test">
     
        <class name="User"  table="user" optimistic-lock="version" >
                  <id name="id">
                <generator class="native" />
            </id>
            <!--version标签必须跟在id标签后面-->
            <version column="version" name="version"  />
            <property name="userName"/>
            <property name="password"/>
                     
        </class>
         
     
    </hibernate-mapping>

    注意:version节点必须出现在id节点后

    这里我们声明了一个 version 属性,用于存放用户的版本信息,保存在 User 表的version中 
    optimistic-lock 属性有如下可选取值: 
    Ø none
    无乐观锁 
    Ø version
    通过版本机制实现乐观锁 
    Ø dirty
    通过检查发生变动过的属性实现乐观锁 
    Ø all
    通过检查所有属性实现乐观锁 
    其中通过 version 实现的乐观锁机制是 Hibernate 官方推荐的乐观锁实现,同时也 
    是 Hibernate 中,目前唯一在数据对象脱离 Session 发生修改的情况下依然有效的锁机 
    制。因此,一般情况下,我们都选择 version 方式作为 Hibernate 乐观锁实现机制。

    2 . 配置文件hibernate.cfg.xml和UserTest测试类


       hibernate.cfg.xml


    复制代码
    <!DOCTYPE hibernate-configuration PUBLIC
            "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
            "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
     
    <hibernate-configuration>
    <session-factory>
     
        <!-- 指定数据库方言 如果使用jbpm的话,数据库方言只能是InnoDB-->
        <property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <!-- 根据需要自动创建数据表 -->
        <property name="hbm2ddl.auto">update</property>
        <!-- 显示Hibernate持久化操作所生成的SQL -->
        <property name="show_sql">true</property>
        <!-- 将SQL脚本进行格式化后再输出 -->
        <property name="format_sql">false</property>
        <property name="current_session_context_class">thread</property>
     
     
        <!-- 导入映射配置 -->
        <property name="connection.url">jdbc:mysql:///user</property>
        <property name="connection.username">root</property>
        <property name="connection.password">123456</property>
        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
        <mapping resource="com/xiaohao/test/User.hbm.xml" />
     
     
     
    </session-factory>
    </hibernate-configuration>
    复制代码
    UserTest.java


    复制代码
    package com.xiaohao.test;
     
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
     
    public class UserTest {
        public static void main(String[] args) {
            Configuration conf=new Configuration().configure();
            SessionFactory sf=conf.buildSessionFactory();
            Session session=sf.getCurrentSession();
            Transaction tx=session.beginTransaction();
    //      User user=new User("小浩","英雄");
    //      session.save(user);
    //       session.createSQLQuery("insert into user(userName,password) value('张英雄16','123')")
    //                  .executeUpdate();
            User user=(User) session.get(User.class, 1);
            user.setUserName("221");
    //      session.save(user);
         
            System.out.println("恭喜您,用户的数据插入成功了哦~~");
            tx.commit();
        }
     
    }
    复制代码
    每次对 TUser 进行更新的时候,我们可以发现,数据库中的 version 都在递增。


     


    下面我们将要通过乐观锁来实现一下并发和同步的测试用例:


    这里需要使用两个测试类,分别运行在不同的虚拟机上面,以此来模拟多个用户同时操作一张表,同时其中一个测试类需要模拟长事务


    UserTest.java


    复制代码
    package com.xiaohao.test;
     
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
     
    public class UserTest {
        public static void main(String[] args) {
            Configuration conf=new Configuration().configure();
            SessionFactory sf=conf.buildSessionFactory();
            Session session=sf.openSession();
    //      Session session2=sf.openSession();
            User user=(User) session.createQuery(" from User user where user=5").uniqueResult();
    //      User user2=(User) session.createQuery(" from User user where user=5").uniqueResult();
            System.out.println(user.getVersion());
    //      System.out.println(user2.getVersion());
            Transaction tx=session.beginTransaction();
            user.setUserName("101");
            tx.commit();
             
            System.out.println(user.getVersion());
    //      System.out.println(user2.getVersion());
    //      System.out.println(user.getVersion()==user2.getVersion());
    //      Transaction tx2=session2.beginTransaction();
    //      user2.setUserName("4468");
    //      tx2.commit();
         
        }
     
    }
    复制代码
     


    UserTest2.java


    复制代码
    package com.xiaohao.test;
     
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
     
    public class UserTest2 {
        public static void main(String[] args) throws InterruptedException {
            Configuration conf=new Configuration().configure();
            SessionFactory sf=conf.buildSessionFactory();
            Session session=sf.openSession();
    //      Session session2=sf.openSession();
            User user=(User) session.createQuery(" from User user where user=5").uniqueResult();
            Thread.sleep(10000);
    //      User user2=(User) session.createQuery(" from User user where user=5").uniqueResult();
            System.out.println(user.getVersion());
    //      System.out.println(user2.getVersion());
            Transaction tx=session.beginTransaction();
            user.setUserName("100");
            tx.commit();
             
            System.out.println(user.getVersion());
    //      System.out.println(user2.getVersion());
    //      System.out.println(user.getVersion()==user2.getVersion());
    //      Transaction tx2=session2.beginTransaction();
    //      user2.setUserName("4468");
    //      tx2.commit();
         
        }
     
    }
    复制代码
     


    操作流程及简单讲解: 首先启动UserTest2.java测试类,在执行到Thread.sleep(10000);这条语句的时候,当前线程会进入睡眠状态。在10秒钟之内
      启动UserTest这个类,在到达10秒的时候,我们将会在UserTest.java中抛出下面的异常:

    复制代码
    Exception in thread "main" org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [com.xiaohao.test.User#5]
        at org.hibernate.persister.entity.AbstractEntityPersister.check(AbstractEntityPersister.java:1932)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2576)
        at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2476)
        at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2803)
        at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
        at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
        at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
        at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
        at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
        at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
        at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:383)
        at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:133)
        at com.xiaohao.test.UserTest2.main(UserTest2.java:21)

     UserTest2代码将在 tx.commit() 处抛出 StaleObjectStateException 异 常,并指出版本检查失败,当前事务正在试图提交

    一个过期数据。通过捕捉这个异常,我 们就可以在乐观锁校验失败时进行相应处理

     


     3、常见并发同步案例分析

        案例一:订票系统案例,某航班只有一张机票,假定有1w个人打开你的网站来订票,问你如何解决并发问题(可扩展到任何高并发网站要考虑
                   的并发读写问题)
        问题,1w个人来访问,票没出去前要保证大家都能看到有票,不可能一个人在看到票的时候别人就不能看了。到底谁能抢到,那得看这个人的“运气”(网
                 络快慢等)
    其次考虑的问题,并发,1w个人同时点击购买,到底谁能成交?总共只有一张票。
    首先我们容易想到和并发相关的几个方案 :
    锁同步同步更多指的是应用程序的层面,多个线程进来,只能一个一个的访问,java中指的是syncrinized关键字。锁也有2个层面,一个是java中谈到的对
    象锁,用于线程同步;另外一个层面是数据库的锁;如果是分布式的系统,显然只能利用数据库端的锁来实现。
    假定我们采用了同步机制或者数据库物理锁机制,如何保证1w个人还能同时看到有票,显然会牺牲性能,在高并发网站中是不可取的。使用hibernate后我们
    提出了另外一个概念:乐观锁、悲观锁(即传统的物理锁);
    采用乐观锁即可解决此问题。乐观锁意思是不锁定表的情况下,利用业务的控制来解决并发问题,这样即保证数据的并发可读性又保证保存数据的排他性,保
    证性能的同时解决了并发带来的脏数据问题。
    hibernate中如何实现乐观锁:
    前提:在现有表当中增加一个冗余字段,version版本号, long类型
    原理:
    1)只有当前版本号》=数据库表版本号,才能提交
    2)提交成功后,版本号version ++
    实现很简单:在ormapping增加一属性optimistic-lock="version"即可,以下是样例片段
    <hibernate-mapping>
    <class name="com.insigma.stock.ABC" optimistic-lock="version" table="T_Stock" schema="STOCK">

    案例二、股票交易系统、银行系统,大数据量你是如何考虑的

    首先,股票交易系统的行情表,每几秒钟就有一个行情记录产生,一天下来就有(假定行情3秒一个) 股票数量×20×60*6 条记录,一月下来这个表记录数

    量多大? oracle中一张表的记录数超过100w后 查询性能就很差了,如何保证系统性能?

    再比如,中国移动有上亿的用户量,表如何设计?把所有用于存在于一个表么?

    所以,大数量的系统,必须考虑拆分表(表名字不一样,表结构一样),通用的拆分方式有以下几种,视情况区别

    1、按业务分,比如 手机号的表,我们可以考虑 130开头的作为一个表,131开头的另外一张表 以此类推

    2、利用oracle表拆分机制做分表

    3、如果是交易系统,我们可以考虑按时间轴拆分,当日数据一个表,历史数据一个表,历史数据的报表和查询不会影响当日的交易

    当然,表拆分之后我们的应用也应做相应的调整,单纯的or-mapping可能要改动,比如部分业务通过存储过程等。

    此外,我们还得考虑缓存

    这里的缓存不是hibernate的一级二级缓存,而是独立于应用的,依然是内存的读取,如果可以减少与数据库的交互,可以大大的减小数据库的压力,

    比如一个电子商务系统的商品搜索,可以考虑将经常搜索的商品列表放到缓存中,这样可以极大的增加性能。

    简单的缓存大家可以理解为自己做一个hashmap,把常访问的数据做一个key,value是第一次从数据库搜索出来的值,下次访问

    就可以从map里读取,而不读数据库;专业些的目前有独立的缓存框架比如memcached 等,可独立部署成一个缓存服务器。


    4、常见的提高高并发下访问效率的手段

    首先先了解高并发的瓶颈所在

    1、网络带宽不够

    2、web线程连接数不足

    3、数据库连接查询太慢

    根据不同的情况可用以下的方式应对

    1、增加网络带宽,dns域名解析分发多台服务器

    2、负载均衡,前置代理服务器如Nginx,Apache等等

    3、数据库查询优化,分表,读写分离等等

     最后复制一些在高并发下面需要常常需要处理的内容:

    尽量使用缓存,包括用户缓存、信息缓存,多花点内存做缓存,减少与数据库的交互,提交性能

    用jprofiler等工具找出性能瓶颈,减少额外的开销。

    优化数据库查询语句,减少直接使用hibernate等工具的直接生成语句(仅耗时较长的查询做优化)

    优化数据库结构,多做索引,提高查询效率

    统计的功能尽量做缓存,或按每天一统计或定时统计相关报表,避免需要时进行统计的功能。

    能使用静态页面的地方尽量使用,减少容器的解析(尽量将动态内容生成静态html来显示)。

    解决以上问题后,使用服务器集群来解决单台的瓶颈问题。


    ----------------------java高并发 如何解决  用什么方式解决--------------------

    高并发的解决方式主要有两种:

    1、使用缓存

    2、使用静态页面

    还有就是从最基础的地方优化我们减少写代码不必要的资源浪费

    1、不要频繁的new对象,对于在应用中只需要存在一个实例的类采用单例模式,对于String的连接使用

    StringBulider或者StringBuffer,对于工具类采用静态方法访问

    2、避免使用错误的方式,如Exception可以控制方法推出,但是Exception要保留stacktrace消耗性能,除非必

         要不要使用 instanceof做条件判断,尽量使用比的条件判断方式.使用JAVA中效率高的类,比如ArrayList比Vector性能好。)


    首先缓存技术我一直没有使用过,我觉得应该是在用户请求时将数据保存在缓存中,下次请求时会检测缓存中是否有数据存在,防止多次请求服务器,导致服务器性能降低,严重导致服务器崩溃,这只是我自己的理解,详细的资料还是需要在网上收集;

     

    使用生成静态页面我想大家应该不模式,我们见过很多网站当在请求的时候页面的后最已经变了,如“http://developer.51cto.com/art/201207/348766.htm”该页面其实是一个服务器请求地址,在转换成htm后,访问速度将提升,因为静态页面不带有服务器组件;在这里我就多多介绍一下:

    一、什么是页面静态化:

    简 单的说,我们如果访问一个链接 ,服务器对应的模块会处理这个请求,转到对应的jsp界面,最后生成我们想要看到的数据。这其中的缺点是显而易见的:因为每次请求服务器都会进行处理,如 果有太多的高并发请求,那么就会加重应用服务器的压力,弄不好就把服务器 搞down 掉了。那么如何去避免呢?如果我们把对 test.do 请求后的结果保存成一个 html 文件,然后每次用户都去访问 ,这样应用服务器的压力不就减少了?

    那么静态页面从哪里来呢?总不能让我们每个页面都手动处理吧?这里就牵涉到我们要讲解的内容了,静态页面生成方案… 我们需要的是自动的生成静态页面,当用户访问 ,会自动生成 test.html ,然后显示给用户。

    二、下面我们在简单介绍一下要想掌握页面静态化方案应该掌握的知识点:

    1、 基础- URL Rewrite

    什么是 URL Rewrite 呢 ? URL 重写。用一个简单的例子来说明问题:输入网址 ,但是实际上访问的却是 abc.com/test.action,那我们就可以说 URL 被重写了。这项技术应用广泛,有许多开源的工具可以实现这个功能。

    2、 基础- Servlet web.xml

    如果你还不知道 web.xml 中一个请求和一个 servlet 是如何匹配到一起的,那么请搜索一下 servlet 的文档。这可不是乱说呀,有很多人就认为 /xyz/*.do 这样的匹配方式能有效。

    如果你还不知道怎么编写一个 servlet ,那么请搜索一下如何编写 servlet.这可不是说笑呀,在各种集成工具漫天飞舞的今天,很多人都不会去从零编写一个 servlet了

    三、基本的方案介绍

    java高并发,如何解决,什么方式解决 - 我学坊 - 励志-我学坊
    其中,对于 URL Rewriter的部分,可以使用收费或者开源的工具来实现,如果 url不是特别的复杂,可以考虑在 servlet 中实现,那么就是下面这个样子:

     

    java高并发,如何解决,什么方式解决 - 我学坊 - 励志-我学坊
     
    总 结:其实我们在开发中都很少考虑这种问题,直接都是先将功能实现,当一个程序员在干到1到2年,就会感觉光实现功能不是最主要的,安全性能、质量等等才是 一个开发人员最该关心的。今天我所说的是高并发。
    我的解决思路是:
    1、采用分布式应用设计
    2、分布式缓存数据库
    3、代码优化


    Java高并发的例子:

    具体情况是这样: 通过java和数据库,自己实现序列自动增长。
    实现代码大致如下:
     id_table表结构, 主要字段:

     id_name  varchar2(16);
     id_val  number(16,0);
     id_prefix  varchar2(4);
       


    复制代码
    //操作DB 
       public synchronized String nextStringValue(String id){
            SqlSession sqlSess = SqlSessionUtil.getSqlSession();
            sqlSess.update("update id_table set id_val = id_val + 1 where id_name="+id);
            Map map = sqlSess.getOne("select id_name, id_prefix, id_val from id_table where id_name="+ id);
            BigDecimal val = (BigDecimal) map.get("id_val");
          //id_val是具体数字,rePack主要是统一返回固定长度的字符串;如:Y0000001, F0000001, T0000001等
            String idValue = rePack(val, map); 
            return idValue;
      }
       
      //公共方法
    public class IdHelpTool{
         public static String getNextStringValue(String idName){
              return getXX().nextStringValue(idName);
        }
    }
    复制代码

    具体使用者,都是通过类似这种方式:IdHelpTool.getNextStringValue("PAY_LOG");来调用。

    问题:
          (1) 当出现并发时, 有时会获取重复的ID;
          (2) 由于服务器做了相关一些设置,有时调用这个方法,好像还会导致超时。

             为了解决问题(1), 考虑过在方法getNextStringValue上,也加上synchronized , 同步关键字过多,会不会更导致超时?
    跪求大侠提供个解决问题的大概思路!!!

    解决思路一:

    1、推荐 https://github.com/adyliu/idcenter
    2、可以通过第三方redis来实现。

     

    解决思路一:

    1、出现重复ID,是因为脏读了,并发的时候不加 synchronized  比如会出现问题

    2、但是加了 synchronized  ,性能急剧下降了,本身 java 就是多线程的,你把它单线程使用,不是明智的选择,同时,如果分布式部署的时候,加了 synchronized  也无法控制并发

    3、调用这个方法,出现超时的情况,说明你的并发已经超过了数据库所能处理的极限,数据库无限等待导致超时

    基于上面的分析,建议采用线程池的方案,支付宝的单号就是用的线程池的方案进行的。

    数据库 update 不是一次加1,而是一次加几百甚至上千,然后取到的这 1000个序号,放在线程池里慢慢分配即可,能应付任意大的并发,同时保证数据库没任何压力。


    展开全文
  • JAVA高并发的三种实现

    万次阅读 多人点赞 2018-07-23 10:50:59
    是用它可以解决一切并发问题,但是,对于系统吞吐量要求更的话,我们这提供几个小技巧。帮助大家减小锁颗粒度,提高并发能力。 初级技巧-乐观锁 乐观锁使用的场景是,读不会冲突,写会冲突。同时读的频率远大于...

    提到锁,大家肯定想到的是sychronized关键字。是用它可以解决一切并发问题,但是,对于系统吞吐量要求更高的话,我们这提供几个小技巧。帮助大家减小锁颗粒度,提高并发能力。

    初级技巧-乐观锁

    乐观锁使用的场景是,读不会冲突,写会冲突。同时读的频率远大于写。

     悲观锁的实现

    悲观的认为所有代码执行都会有并发问题,所以将所有代码块都用sychronized锁住

    乐观锁的实现

    乐观的认为在读的时候不会产生冲突为题,在写时添加锁。所以解决的应用场景是读远大于写时的场景。

    中级技巧-String.intern()

    乐观锁不能很好的解决大量的写冲突的问题,但是很多场景下,锁只是针对某个用户或者某个订单。 比如一个用户先创建session,才能进行后面的操作,但是由于网络的问题,创建session的请求和后续请求几乎同时到达,而并行线程可能会先处理后面的请求。一般情况需要对用户sessionMap加锁,比如上面的乐观锁。在这样的场景下,可以将锁限定在用户本身上,即原来的

    这个比较类似行锁和数据库表锁的概念。显然行锁的并发能力比表锁的高很多。

    实用String.intern();是这种方式的具体实现。类String维护了一个字符串池。当调用intern方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals(Object)方法确定),则返回池中的字符串。可见,当String 相同时,总返回同一个对象,因此就实现了对同一用户加锁。由于所的颗粒度局限于具体用户,使得系统获得最大程度的并发。

    CopyOnWriteMap?

     

    既然说到了“类似于数据库中的行锁的概念”,就不得不提一下MVCC,Java中CopyOnWrite类实现了MVCC。Copy On Write是这样一种机制。当我们读取共享数据的时候,直接读取,不需要同步。当我们修改数据的时候,我们就把当前数据Copy一份副本,然后在这个副本 上进行修改,完成之后,再用修改后的副本,替换掉原来的数据。这种方法就叫做Copy On Write。

     

    但是,,,JDK并没有提供CopyOnWriteMap,为什么?下面有个很好的回答,那就是已经有了ConcurrentHashMap,为什么还需要CopyOnWriteMap?

     

    高级技巧 - 类ConcurrentHashMap

    String.inter()的缺陷是类 String 维护一个字符串池是放在JVM perm区的,如果用户数特别多,导致放入字符串池的String不可控,有可能导致OOM错误或者过多的Full GC。怎么样能控制锁的个数,同时减小粒度锁呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精细的控制?那么可以借鉴ConcurrentHashMap的方式,将需要加锁的对象分为多个bucket,每个bucket加一个锁,伪代码如下:

     

     

     

     

     

     

    展开全文
  • JAVA并发编程与高并发解决方案 JAVA高并发项目实战课程
  • JAVA高并发

    2016-12-26 11:29:24
    分享链接: java.util.concurrent包源码分析 ... 聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源码分析 http://blog.csdn.net/ITer_ZC/article/


    分享链接:

    java.util.concurrent包源码分析

    http://www.cnblogs.com/wanly3643/category/437878.html


    聊聊高并发(四十)解析java.util.concurrent各个组件(十六) ThreadPoolExecutor源码分析

    http://blog.csdn.net/ITer_ZC/article/details/46913841


    Java 并发工具包 java.util.concurrent 用户指南

    http://blog.csdn.net/defonds/article/details/44021605/

    展开全文
  • 并发编程篇:java 高并发面试题

    万次阅读 多人点赞 2018-02-28 21:43:18
    3、java thread状态 NEW 状态是指线程刚创建, 尚未启动 RUNNABLE Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。...

    1、线程与进程

    1. 进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
    2. 一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
    3. 区别不同
      a,地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
      b,资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资
      c,线程是处理器调度的基本单位,但进程不是.
      d,二者均可并发执行.

    2、 守护线程

    在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。
    守护线程和用户线程的区别在于:守护线程依赖于创建它的线程,而用户线程则不依赖。举个简单的例子:如果在main线程中创建了一个守护线程,当main方法运行完毕之后,守护线程也会随着消亡。而用户线程则不会,用户线程会一直运行直到其运行完毕。在JVM中,像垃圾收集器线程就是守护线程。

    3、java thread状态

    1. NEW 状态是指线程刚创建, 尚未启动
    2. RUNNABLE Java线程中将就绪(ready)和运行中(running)两种状态笼统的成为“运行”。线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权,此时处于就绪状态(ready)。就绪状态的线程在获得cpu 时间片后变为运行中状态(running)。
    3. BLOCKED 这个状态下, 是在多个线程有同步操作的场景, 比如正在等待另一个线程的synchronized 块的执行释放, 也就是这里是线程在等待进入临界区
    4. WAITING 这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll 一遍该线程可以继续下一步操作, 这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束
    5. TIMED_WAITING 这个状态就是有限的(时间限制)的WAITING, 一般出现在调用wait(long), join(long)等情况下, 另外一个线程sleep后, 也会进入TIMED_WAITING状态
    6. TERMINATED 这个状态下表示 该线程的run方法已经执行完毕了, 基本上就等于死亡了(当时如果线程被持久持有, 可能不会被回收)

    4、请说出与线程同步以及线程调度相关的方法。

    1. wait():使一个线程处于等待(阻塞)状态,并且释放所持有的对象的锁;
    2. sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要处理InterruptedException异常;
    3. notify():唤醒一个处于等待状态的线程,当然在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且与优先级无关;
    4. notityAll():唤醒所有处于等待状态的线程,该方法并不是将对象的锁给所有线程,而是让它们竞争,只有获得锁的线程才能进入就绪状态;

    5、进程调度算法

    实时系统:FIFO(First Input First Output,先进先出算法),SJF(Shortest Job First,最短作业优先算法),SRTF(Shortest Remaining Time First,最短剩余时间优先算法)。
    交互式系统:RR(Round Robin,时间片轮转算法),HPF(Highest Priority First,最高优先级算法),多级队列,最短进程优先,保证调度,彩票调度,公平分享调度。

    6、wait()和sleep()的区别

    1. sleep来自Thread类,和wait来自Object类
    2. 调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁
    3. sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU
    4. sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒
      ##7、ThreadLocal,以及死锁分析
      hreadLocal为每个线程维护一个本地变量。
      采用空间换时间,它用于线程间的数据隔离,为每一个使用该变量的线程提供一个副本,每个线程都可以独立地改变自己的副本,而不会和其他线程的副本冲突。
      ThreadLocal类中维护一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值为对应线程的变量副本。
      彻底理解ThreadLocal

    8、Synchronized 与Lock

    1. ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
      线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
      如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
      如果 使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情

    2. ReentrantLock获取锁定与三种方式:
      a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
      b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
      c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
      d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断

    总体的结论先摆出来:

    synchronized:
    在资源竞争不是很激烈的情况下,偶尔会有同步的情形下,synchronized是很合适的。原因在于,编译程序通常会尽可能的进行优化synchronized,另外可读性非常好,不管用没用过5.0多线程包的程序员都能理解。
    ReentrantLock:
    ReentrantLock提供了多样化的同步,比如有时间限制的同步,可以被Interrupt的同步(synchronized的同步是不能Interrupt的)等。在资源竞争不激烈的情形下,性能稍微比synchronized差点点。但是当同步非常激烈的时候,synchronized的性能一下子能下降好几十倍。而ReentrantLock确还能维持常态。

    详解synchronized与Lock的区别与使用

    9、Volatile和Synchronized

    Volatile和Synchronized四个不同点:

    1. 粒度不同,前者针对变量 ,后者锁对象和类
    2. syn阻塞,volatile线程不阻塞
    3. syn保证三大特性,volatile不保证原子性
    4. syn编译器优化,volatile不优化
      要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
    5. 对变量的写操作不依赖于当前值。
    6. 该变量没有包含在具有其他变量的不变式中。

    JAVA多线程之volatile 与 synchronized 的比较

    10、CAS

    CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
    ##11、Java中Unsafe类详解

    1. 通过Unsafe类可以分配内存,可以释放内存;类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存,与C语言中的3个方法对应。
    2. 可以定位对象某字段的内存位置,也可以修改对象的字段值,即使它是私有的;
    3. 挂起与恢复:将一个线程进行挂起是通过park方法实现的,调用 park后,线程将一直阻塞直到超时或者中断等条件出现。unpark可以终止一个挂起的线程,使其恢复正常。整个并发框架中对线程的挂起操作被封装在 LockSupport类中,LockSupport类中有各种版本pack方法,但最终都调用了Unsafe.park()方法。
    4. cas
      Java中Unsafe类详解

    12、线程池

    线程池的作用:
    在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程
    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性。
    常用线程池:ExecutorService 是主要的实现类,其中常用的有
    Executors.newSingleT
    hreadPool(),newFixedThreadPool(),newcachedTheadPool(),newScheduledThreadPool()。

    13、ThreadPoolExecutor

    ###构造方法参数说明
    corePoolSize:核心线程数,默认情况下核心线程会一直存活,即使处于闲置状态也不会受存keepAliveTime限制。除非将allowCoreThreadTimeOut设置为true。
    maximumPoolSize:线程池所能容纳的最大线程数。超过这个数的线程将被阻塞。当任务队列为没有设置大小的LinkedBlockingDeque时,这个值无效。
    keepAliveTime:非核心线程的闲置超时时间,超过这个时间就会被回收。
    unit:指定keepAliveTime的单位,如TimeUnit.SECONDS。当将allowCoreThreadTimeOut设置为true时对corePoolSize生效。
    workQueue:线程池中的任务队列.
    常用的有三种队列,SynchronousQueue,LinkedBlockingDeque,ArrayBlockingQueue。

    threadFactory:线程工厂,提供创建新线程的功能。ThreadFactory是一个接口,只有一个方法

    ###原理

    1. 如果当前池大小 poolSize 小于 corePoolSize ,则创建新线程执行任务。
    2. 如果当前池大小 poolSize 大于 corePoolSize ,且等待队列未满,则进入等待队列
    3. 如果当前池大小 poolSize 大于 corePoolSize 且小于 maximumPoolSize ,且等待队列已满,则创建新线程执行任务。
    4. 如果当前池大小 poolSize 大于 corePoolSize 且大于 maximumPoolSize ,且等待队列已满,则调用拒绝策略来处理该任务。
    5. 线程池里的每个线程执行完任务后不会立刻退出,而是会去检查下等待队列里是否还有线程任务需要执行,如果在 keepAliveTime 里等不到新的任务了,那么线程就会退出。

    13、Executor拒绝策略

    1. AbortPolicy:为java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try
      catch,否则程序会直接退出.
    2. DiscardPolicy:直接抛弃,任务不执行,空方法
    3. DiscardOldestPolicy:从队列里面抛弃head的一个任务,并再次execute 此task。
    4. CallerRunsPolicy:在调用execute的线程里面执行此command,会阻塞入
    5. 用户自定义拒绝策略:实现RejectedExecutionHandler,并自己定义策略模式

    14、CachedThreadPool 、 FixedThreadPool、SingleThreadPool

    1. newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
      适用场景:任务少 ,并且不需要并发执行
    2. newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.
      线程没有任务要执行时,便处于空闲状态,处于空闲状态的线程并不会被立即销毁(会被缓存住),只有当空闲时间超出一段时间(默认为60s)后,线程池才会销毁该线程(相当于清除过时的缓存)。新任务到达后,线程池首先会让被缓存住的线程(空闲状态)去执行任务,如果没有可用线程(无空闲线程),便会创建新的线程。
      适用场景:处理任务速度 > 提交任务速度,耗时少的任务(避免无限新增线程)
    3. newFixedThreadPool :创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
    4. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

    15、CopyOnWriteArrayList

    CopyOnWriteArrayList : 写时加锁,当添加一个元素的时候,将原来的容器进行copy,复制出一个新的容器,然后在新的容器里面写,写完之后再将原容器的引用指向新的容器,而读的时候是读旧容器的数据,所以可以进行并发的读,但这是一种弱一致性的策略。
    使用场景:CopyOnWriteArrayList适合使用在读操作远远大于写操作的场景里,比如缓存。

    16、AQS

    1. AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
    private volatile int state;//共享变量,使用volatile修饰保证线程可见性
    
    1. 2种同步方式:独占式,共享式。独占式如ReentrantLock,共享式如Semaphore,CountDownLatch,组合式的如ReentrantReadWriteLock
    2. 节点的状态
      CANCELLED,值为1,表示当前的线程被取消;
      SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;
      CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;
      PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;
      值为0,表示当前节点在sync队列中,等待着获取锁。
    3. 模板方法模式
       protected boolean tryAcquire(int arg) : 独占式获取同步状态,试着获取,成功返回true,反之为false
       protected boolean tryRelease(int arg) :独占式释放同步状态,等待中的其他线程此时将有机会获取到同步状态;
       protected int tryAcquireShared(int arg) :共享式获取同步状态,返回值大于等于0,代表获取成功;反之获取失败;
       protected boolean tryReleaseShared(int arg) :共享式释放同步状态,成功为true,失败为false
      AQS维护一个共享资源state,通过内置的FIFO来完成获取资源线程的排队工作。该队列由一个一个的Node结点组成,每个Node结点维护一个prev引用和next引用,分别指向自己的前驱和后继结点。双端双向链表。
    4. 独占式:乐观的并发策略
      acquire
       a.首先tryAcquire获取同步状态,成功则直接返回;否则,进入下一环节;
      b.线程获取同步状态失败,就构造一个结点,加入同步队列中,这个过程要保证线程安全;
       c.加入队列中的结点线程进入自旋状态,若是老二结点(即前驱结点为头结点),才有机会尝试去获取同步状态;否则,当其前驱结点的状态为SIGNAL,线程便可安心休息,进入阻塞状态,直到被中断或者被前驱结点唤醒。
      release
      release的同步状态相对简单,需要找到头结点的后继结点进行唤醒,若后继结点为空或处于CANCEL状态,从后向前遍历找寻一个正常的结点,唤醒其对应线程。
    5. 共享式:
      共享式地获取同步状态.同步状态的方法tryAcquireShared返回值为int。
      a.当返回值大于0时,表示获取同步状态成功,同时还有剩余同步状态可供其他线程获取;
       b.当返回值等于0时,表示获取同步状态成功,但没有可用同步状态了;
       c.当返回值小于0时,表示获取同步状态失败。
    6. AQS实现公平锁和非公平锁
      非公平锁中,那些尝试获取锁且尚未进入等待队列的线程会和等待队列head结点的线程发生竞争。公平锁中,在获取锁时,增加了isFirst(current)判断,当且仅当,等待队列为空或当前线程是等待队列的头结点时,才可尝试获取锁。
       Java并发包基石-AQS详解

    16、Java里的阻塞队列

    7个阻塞队列。分别是

    ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。
    LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。
    PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。
    DelayQueue:一个使用优先级队列实现的无界阻塞队列。
    SynchronousQueue:一个不存储元素的阻塞队列。
    LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
    LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

    ###添加元素
    Java中的阻塞队列接口BlockingQueue继承自Queue接口。BlockingQueue接口提供了3个添加元素方法。
    add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
    offer:添加元素到队列里,添加成功返回true,添加失败返回false
    put:添加元素到队列里,如果容量满了会阻塞直到容量不满

    ###删除方法
    3个删除方法
    poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。
    remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false
    take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除

    17、condition

    对Condition的源码理解,主要就是理解等待队列,等待队列可以类比同步队列,而且等待队列比同步队列要简单,因为等待队列是单向队列,同步队列是双向队列。

    java condition使用及分

    18、DelayQueue

    队列中每个元素都有个过期时间,并且队列是个优先级队列,当从队列获取元素时候,只有过期元素才会出队列。

    并发队列-无界阻塞延迟队列delayqueue原理探究

    19、Fork/Join框架

    Fork/Join框架是Java 7提供的一个用于并行执行任务的框架,是一个把大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务结果的框架。Fork/Join框架要完成两件事情:

    1.任务分割:首先Fork/Join框架需要把大的任务分割成足够小的子任务,如果子任务比较大的话还要对子任务进行继续分割

    2.执行任务并合并结果:分割的子任务分别放到双端队列里,然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都放在另外一个队列里,启动一个线程从队列里取数据,然后合并这些数据。

    在Java的Fork/Join框架中,使用两个类完成上述操作

    1.ForkJoinTask:我们要使用Fork/Join框架,首先需要创建一个ForkJoin任务。该类提供了在任务中执行fork和join的机制。通常情况下我们不需要直接集成ForkJoinTask类,只需要继承它的子类,Fork/Join框架提供了两个子类:

    a.RecursiveAction:用于没有返回结果的任务

    b.RecursiveTask:用于有返回结果的任务

    2.ForkJoinPool:ForkJoinTask需要通过ForkJoinPool来执行

    任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务(工作窃取算法)。
    Fork/Join框架的实现原理
      ForkJoinPool由ForkJoinTask数组和ForkJoinWorkerThread数组组成,ForkJoinTask数组负责将存放程序提交给ForkJoinPool,而ForkJoinWorkerThread负责执行这

    20、原子操作类

    在java.util.concurrent.atomic包下,可以分为四种类型的原子更新类:原子更新基本类型、原子更新数组类型、原子更新引用和原子更新属性。

    1. 原子更新基本类型
      使用原子方式更新基本类型,共包括3个类:
      AtomicBoolean:原子更新布尔变量
      AtomicInteger:原子更新整型变量
      AtomicLong:原子更新长整型变量
    2. 原子更新数组
      通过原子更新数组里的某个元素,共有3个类:
      AtomicIntegerArray:原子更新整型数组的某个元素
      AtomicLongArray:原子更新长整型数组的某个元素
      AtomicReferenceArray:原子更新引用类型数组的某个元素
      AtomicIntegerArray常用的方法有:
      int addAndSet(int i, int delta):以原子方式将输入值与数组中索引为i的元素相加
      boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式更新数组中索引为i的值为update值
    3. 原子更新引用类型
      AtomicReference:原子更新引用类型
      AtomicReferenceFieldUpdater:原子更新引用类型里的字段
      AtomicMarkableReference:原子更新带有标记位的引用类型。
    4. 原子更新字段类
      如果需要原子更新某个类的某个字段,就需要用到原子更新字段类,可以使用以下几个类:
      AtomicIntegerFieldUpdater:原子更新整型字段
      AtomicLongFieldUpdater:原子更新长整型字段
      AtomicStampedReference:原子更新带有版本号的引用类型。
      要想原子更新字段,需要两个步骤:
      每次必须使用newUpdater创建一个更新器,并且需要设置想要更新的类的字段
      更新类的字段(属性)必须为public volatile

    21、同步屏障CyclicBarrier

    CyclicBarrier 的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
    CyclicBarrier和CountDownLatch的区别

    CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。所以CyclicBarrier能处理更为复杂的业务场景,比如如果计算发生错误,可以重置计数器,并让线程们重新执行一次。
    CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得CyclicBarrier阻塞的线程数量。isBroken方法用来知道阻塞的线程是否被中断。比如以下代码执行完之后会返回true。
    ##22、Semaphore
    Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源
    Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。这个时候,我们就可以使用Semaphore来做流控,代码如下:

    控制并发线程数的Semaphore

    23、死锁,以及解决死锁

    ###死锁产生的四个必要条件
    互斥条件:资源是独占的且排他使用,进程互斥使用资源,即任意时刻一个资源只能给一个进程使用,其他进程若申请一个资源,而该资源被另一进程占有时,则申请者等待直到资源被占有者释放。
    不可剥夺条件:进程所获得的资源在未使用完毕之前,不被其他进程强行剥夺,而只能由获得该资源的进程资源释放。
    请求和保持条件:进程每次申请它所需要的一部分资源,在申请新的资源的同时,继续占用已分配到的资源。
    循环等待条件:在发生死锁时必然存在一个进程等待队列{P1,P2,…,Pn},其中P1等待P2占有的资源,P2等待P3占有的资源,…,Pn等待P1占有的资源,形成一个进程等待环路,环路中每一个进程所占有的资源同时被另一个申请,也就是前一个进程占有后一个进程所深情地资源。
    ###解决死锁
    一是死锁预防,就是不让上面的四个条件同时成立。
    二是,合理分配资源。
    三是使用银行家算法,如果该进程请求的资源操作系统剩余量可以满足,那么就分配。

    24、进程间的通信方式

    1. 管道( pipe):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
    2. 有名管道 (named pipe) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
    3. 信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
    4. 消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
    5. 信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
    6. 共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
    7. 套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

    中断

    interrupt()的作用是中断本线程。
    本线程中断自己是被允许的;其它线程调用本线程的interrupt()方法时,会通过checkAccess()检查权限。这有可能抛出SecurityException异常。
    如果本线程是处于阻塞状态:调用线程的wait(), wait(long)或wait(long, int)会让它进入等待(阻塞)状态,或者调用线程的join(), join(long), join(long, int), sleep(long), sleep(long, int)也会让它进入阻塞状态。若线程在阻塞状态时,调用了它的interrupt()方法,那么它的“中断状态”会被清除并且会收到一个InterruptedException异常。例如,线程通过wait()进入阻塞状态,此时通过interrupt()中断该线程;调用interrupt()会立即将线程的中断标记设为“true”,但是由于线程处于阻塞状态,所以该“中断标记”会立即被清除为“false”,同时,会产生一个InterruptedException的异常。
    如果线程被阻塞在一个Selector选择器中,那么通过interrupt()中断它时;线程的中断标记会被设置为true,并且它会立即从选择操作中返回。
    如果不属于前面所说的情况,那么通过interrupt()中断线程时,它的中断标记会被设置为“true”。
    中断一个“已终止的线程”不会产生任何操作。

    1. 终止处于“阻塞状态”的线程
      通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
      当线程由于被调用了sleep(), wait(), join()等方法而进入阻塞状态;若此时调用线程的interrupt()将线程的中断标记设为true。由于处于阻塞状态,中断标记会被清除,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程,
    2. 终止处于“运行状态”的线程

    ##interrupted() 和 isInterrupted()的区别
    最后谈谈 interrupted() 和 isInterrupted()。
    interrupted() 和 isInterrupted()都能够用于检测对象的“中断标记”。
    区别是,interrupted()除了返回中断标记之外,它还会清除中断标记(即将中断标记设为false);而isInterrupted()仅仅返回中断标记。
    interrupt()和线程终止方式

    展开全文
  • java高并发,编程,技术宝典。java高并发,编程,技术宝典。java高并发,编程,技术宝典。java高并发,编程,技术宝典。java高并发,编程,技术宝典。
  • java高并发秒杀api源码

    热门讨论 2016-11-29 23:01:35
    java高并发秒杀api源码
  • Java高并发秒杀API(四)之高并发优化

    万次阅读 多人点赞 2017-10-06 17:07:54
    Java高并发秒杀API(四)之高并发优化1. 高并发优化分析 关于并发 并发性上不去是因为当多个线程同时访问一行数据时,产生了事务,因此产生写锁,每当一个获取了事务的线程把锁释放,另一个排队线程才能拿到写锁,QPS...
  • Java高并发秒杀——高并发优化 高并发优化:由于该系统还是存在高并发问题,并发访问量大概只有100左右,可以通过高并发优化将并发量提高到5000左右 高并发优化分析目录   1、高并发优化分析   2、redis后端缓存...
  • 此课程是对java高并发编程原理及源码的深度解析,课程内容为Java对象头的布局 java对象头的组成部分 JOL的的使用 什么是小端存储莫 mark word的介绍 mark word的hash存储
  • Java高并发秒杀高并发优化 具体可以参考github 获取系统时间不用优化,获取一次内存是需要10纳秒,所以获取一次系统时间是非常快的。 redis后端缓存 并发优化 降低mysql的rowlock的持有时间 利用存储过程...
  • Java 高并发 spring springMVC mybatis bootstrap js readis 存储过程 mysql
  • java高并发程序设计视频全集,并发场景,死锁,活锁,阻塞,非阻塞...
  • Java高并发系列4-并发容器

    千次阅读 2019-07-08 15:32:01
    Java高并发系列4-并发容器 接上一篇Java高并发系列3-再写单例我们继续, 并发容器在高并发中有这举足轻重的地步,这一篇我们主要看并发容器。 1、并发List 在List下中有ArrayList 、LinkedList 、Vector 三种数据...
  • Java 高并发思路

    2019-02-28 21:13:14
    此文记录了自己学习Java 高并发思路的基础知识,纪念自己面试前的准备。 扩容 垂直扩容(纵向扩容):提高系统部件能力 水平扩展(横向扩容):增加更多系统成员 读操作扩展 memcache、redis、CDN 写操作扩展 ...
  • Java高并发编程

    2019-06-02 10:22:07
    从今天开始,开启Java高并发编程专栏。 代码请见: https://github.com/horacehe15/HighConcurrency
  • Java并发编程之美 、Java并发编程的艺术 、 实战Java高并发程序设计 这三本书哪本好一点 感觉都差不多 哪本适合找实习的大学生
  • Java 高并发系列2-并发锁

    千次阅读 2019-07-06 17:25:02
    Java 高并发系列2-并发锁 接着上一篇并发文章我们继续 Java 高并发系列1-开篇 本篇的主要内容是以下几点: wait 、notify 的简单使用 Reentrantlock的简单使用 synchronized 与Reentrantlock的区别 ThreadLocal的...
  • 聊聊Java高并发

    千次阅读 2019-02-25 19:00:00
    今儿和大家聊聊Java高并发。搞了多年Java,许多朋友提到“并发”仍头疼:学了很多Java技术,一多线程就出BUG?遇不到并发问题,高并发经验该怎么积累?面试又卡在并发...
  • 葛一鸣 实战java高并发程序设计 源码,方便大家调试代码和理解知识点
  • java高并发解决思路

    千次阅读 2017-03-10 14:07:54
    java高并发解决方案
  • 超推荐提升java高并发下的开发能力!!!
  • Java 高并发,什么方式解决 高并发和大流量解决方案
  • 《实战Java高并发程序设计》高清完整PDF版 《实战Java高并发程序设计》高清完整PDF版
  • JAVA高并发编程

    2018-07-05 15:16:58
    java高并发编程 synchronized 锁定的不是代码块,方法,而是对象,一定要明确,也就是对象锁 静态的属性和方法要锁定当前类的Class对象,也就是所谓的类锁 public static void mm(){ synchronized(T....
  • Java高并发编程笔记

    千次阅读 2019-05-28 17:44:54
    观老齐《Java高并发编程笔记》笔记。 老齐的IT加油站网址:http://www.itlaoqi.com/ 什么是并发 并发就是指程序同时处理多个任务的能力。 并发编程的根源在于对多个任务情况下对访问资源的有效控制。 程序、进程...
  • 实战java高并发程序设计,学习java并发编程的一手资料,值得一看。在过去单核CPU时代,单任务在一个时间点只能执行单一程序,随着多核CPU的发展,并行程序开发就显得尤为重要。, 《实战Java高并发程序设计》主要介绍...
  • Java高并发分布式架构核心 链接: https://pan.baidu.com/s/1okFms0_86JHROlUFW3vphw 密码: 630w
  • Java高并发与JVM

    2019-07-02 14:48:57
    1、Java高并发 1.1CPU cache 所有的计算都是由CPU进行操作的,然而CPU的读写速度远远大于Main Memory的读写速度,故在CPU中添加了高速缓存,缓存分为3级,每一级都是都是由指令和数据构成。 1.2Java内存模型-JMM ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,813
精华内容 9,925
关键字:

java高并发

java 订阅