精华内容
下载资源
问答
  • 多线程并发中控制事务的解决方案

    在多线程或并发中控制事务的解决方案

    参考文章:

    (1)在多线程或并发中控制事务的解决方案

    (2)https://www.cnblogs.com/zhangxue/p/6907889.html


    备忘一下。


    展开全文
  • 多线程事务并发

    千次阅读 2005-01-13 13:12:00
    多线程事务并发Author:gnicky URL: http://blog.csdn.net/loconfuse/“在实际问题中,不要在事件处理线程中做任何主要工作,而应当使用另外的线程作为工作线程,并允许事件处理线程快速地返回到处理事件等待状态”...

    多线程与事务并发

    Author:gnicky  URL: http://blog.csdn.net/loconfuse/

    “在实际问题中,不要在事件处理线程中做任何主要工作,而应当使用另外的线程作为工作线程,并允许事件处理线程快速地返回到处理事件等待状态”。这句话看起来非常有道理,多线程的优点要充分体现!那么线程处理要完成的工作以及业务要处理的工作如何划分?

    实际上这个问题可以归结为控制驱动体系与商业逻辑层的进程管理协调,而事务的并发性的考虑与设计在OOD阶段注重,也就是说在OOD的阶段就必须考虑到之后的整体的进程中的多线程问题,从而在其中加入事务并发控制。

    简单的描述抽象图如下:
                  
        
        看一下书本推荐的技巧:
    GUI事件处理代码应当相对简短,让事件处理线程从处理器得到返回,并准备处理下一个触发的事件,如果必须执行更长的任务,应该将工作传递给另外的一个线程进行处理。这有助于保持用户界面处于激活状态并具有高度灵敏“

    这个推荐同样看起来非常有道理,可是,以线程为例,当按钮按下,处理数据库更新交给了另外一个线程处理,用户界面激活,用户再度按下删除按钮,调用其他能够影响到数据修改的数据的代码,这样执行的结果与用户期望的,对于用户来说也许是莫名其妙的。

    对于单机的并且是只能单实例运行的程序而言,为了这种体现多线程的技巧,必须去实现数据处理事务的并发性支持,代价也是需要考虑的。

    因此所谓的技巧,也是因地制宜的!

    如有错误观点,请留言指正!

    展开全文
  • 日常项目中,经常会出现一个场景,同时批量插入数据库数据,由于逻辑复杂或者其它原因,我们无法使用sql进行批量插入,这个时候我们首先想到多线程并发插入,但是如何控制事务呢 … 直接上干货 实现效果 开启多条子...
    背景

    日常项目中,经常会出现一个场景,同时批量插入数据库数据,由于逻辑复杂或者其它原因,我们无法使用sql进行批量插入。串行效率低,耗时长,为了提高效率,这个时候我们首先想到多线程并发插入,但是如何控制事务呢 … 直接上干货

    实现效果
    • 开启多条子线程,并发插入数据库
    • 当其中一条线程出现异常,或者处理结果为非预期结果,则全部线程均回滚
    代码实现
    @Service
    public class CompanyUserBatchServiceImpl implements CompanyUserBatchService {
        private static final Logger logger = LoggerFactory.getLogger(CompanyUserBatchServiceImpl.class);
    
        @Autowired
        private CompanyUserService companyUserService;
    
        @Override
        public ReturnData addNewCurrentCompanyUsers(String params) {
            logger.info("addNewCompanyUsers 新增参保人方法");
            logger.info(">>>>>>>>>>>>参数:{}", params);
            ReturnData rd = new ReturnData();
            rd.setRetCode(CommonConstants.RETURN_CODE_FAIL);
            if (StringUtils.isBlank(params)) {
                rd.setMsg("入参为空!");
                logger.info(">>>>>>入参为空。");
                return rd;
            }
    
            List<CompanyUserResultVo> companyUsers;
            try {
                companyUsers = JSONObject.parseArray(params, CompanyUserResultVo.class);
            } catch (Exception e) {
                logger.info(">>>>>>>>>入参格式有误: {}", e);
                rd.setMsg("入参格式有误!");
                return rd;
            }
    
    
            //每条线程最小处理任务数
            int perThreadHandleCount = 1;
            //线程池的最大线程数
            int nThreads = 10;
            int taskSize = companyUsers.size();
    
            if (taskSize > nThreads * perThreadHandleCount) {
                perThreadHandleCount = taskSize % nThreads == 0 ? taskSize / nThreads : taskSize / nThreads + 1;
                nThreads = taskSize % perThreadHandleCount == 0 ? taskSize / perThreadHandleCount : taskSize / perThreadHandleCount + 1;
            } else {
                nThreads = taskSize;
            }
    
            logger.info("批量添加参保人taskSize: {}, perThreadHandleCount: {}, nThreads: {}", taskSize, perThreadHandleCount, nThreads);
            CountDownLatch mainLatch = new CountDownLatch(1);
            //监控子线程
            CountDownLatch threadLatch = new CountDownLatch(nThreads);
            //根据子线程执行结果判断是否需要回滚
            BlockingDeque<Boolean> resultList = new LinkedBlockingDeque<>(nThreads);
            //必须要使用对象,如果使用变量会造成线程之间不可共享变量值
            RollBack rollBack = new RollBack(false);
            ExecutorService fixedThreadPool = Executors.newFixedThreadPool(nThreads);
    
            List<Future<List<Object>>> futures = Lists.newArrayList();
            List<Object> returnDataList = Lists.newArrayList();
            //给每个线程分配任务
            for (int i = 0; i < nThreads; i++) {
                int lastIndex = (i + 1) * perThreadHandleCount;
                List<CompanyUserResultVo> companyUserResultVos = companyUsers.subList(i * perThreadHandleCount, lastIndex >= taskSize ? taskSize : lastIndex);
                AddNewCompanyUserThread addNewCompanyUserThread = new AddNewCompanyUserThread(mainLatch, threadLatch, rollBack, resultList, companyUserResultVos);
                Future<List<Object>> future = fixedThreadPool.submit(addNewCompanyUserThread);
                futures.add(future);
            }
    
            /** 存放子线程返回结果. */
            List<Boolean> backUpResult = Lists.newArrayList();
            try {
                //等待所有子线程执行完毕
                boolean await = threadLatch.await(20, TimeUnit.SECONDS);
                //如果超时,直接回滚
                if (!await) {
                    rollBack.setRollBack(true);
                } else {
                    logger.info("创建参保人子线程执行完毕,共 {} 个线程", nThreads);
                    //查看执行情况,如果有存在需要回滚的线程,则全部回滚
                    for (int i = 0; i < nThreads; i++) {
                        Boolean result = resultList.take();
                        backUpResult.add(result);
                        logger.debug("子线程返回结果result: {}", result);
                        if (result) {
                            /** 有线程执行异常,需要回滚子线程. */
                            rollBack.setRollBack(true);
                        }
                    }
                }
            } catch (InterruptedException e) {
                logger.error("等待所有子线程执行完毕时,出现异常");
                throw new SystemException("等待所有子线程执行完毕时,出现异常,整体回滚");
            } finally {
                //子线程再次开始执行
                mainLatch.countDown();
                logger.info("关闭线程池,释放资源");
                fixedThreadPool.shutdown();
            }
    
            /** 检查子线程是否有异常,有异常整体回滚. */
            for (int i = 0; i < nThreads; i++) {
                if (CollectionUtils.isNotEmpty(backUpResult)) {
                    Boolean result = backUpResult.get(i);
                    if (result) {
                        logger.info("创建参保人失败,整体回滚");
                        throw new SystemException("创建参保人失败");
                    }
                } else {
                    logger.info("创建参保人失败,整体回滚");
                    throw new SystemException("创建参保人失败");
                }
            }
    
            //拼接结果
            try {
                for (Future<List<Object>> future : futures) {
                    returnDataList.addAll(future.get());
                }
            } catch (Exception e) {
                logger.info("获取子线程操作结果出现异常,创建的参保人列表: {} ,异常信息: {}", JSONObject.toJSONString(companyUsers), e);
                throw new SystemException("创建参保人子线程正常创建参保人成功,主线程出现异常,回滚失败");
            }
    
            rd.setRetCode(CommonConstants.RETURN_CODE_SUCCESS);
            rd.setData(returnDataList);
            return rd;
        }
    
        public class AddNewCompanyUserThread implements Callable<List<Object>> {
            /**
             * 主线程监控
             */
            private CountDownLatch mainLatch;
            /**
             * 子线程监控
             */
            private CountDownLatch threadLatch;
            /**
             * 是否回滚
             */
            private RollBack rollBack;
            private BlockingDeque<Boolean> resultList;
            private List<CompanyUserResultVo> taskList;
    
            public AddNewCompanyUserThread(CountDownLatch mainLatch, CountDownLatch threadLatch, RollBack rollBack, BlockingDeque<Boolean> resultList, List<CompanyUserResultVo> taskList) {
                this.mainLatch = mainLatch;
                this.threadLatch = threadLatch;
                this.rollBack = rollBack;
                this.resultList = resultList;
                this.taskList = taskList;
            }
    
            @Override
            public List<Object> call() {
                //为了保证事务不提交,此处只能调用一个有事务的方法,spring 中事务的颗粒度是方法,只有方法不退出,事务才不会提交
                return companyUserService.addNewCompanyUsers(mainLatch, threadLatch, rollBack, resultList, taskList);
            }
    
        }
    
        public class RollBack {
            private Boolean isRollBack;
    
            public Boolean getRollBack() {
                return isRollBack;
            }
    
            public void setRollBack(Boolean rollBack) {
                isRollBack = rollBack;
            }
    
            public RollBack(Boolean isRollBack) {
                this.isRollBack = isRollBack;
            }
        }
    
    
    public List<Object> addNewCompanyUsers(CountDownLatch mainLatch, CountDownLatch threadLatch, CompanyUserBatchServiceImpl.RollBack rollBack, BlockingDeque<Boolean> resultList, List<CompanyUserResultVo> taskList) {
            List<Object> returnDataList = Lists.newArrayList();
            Boolean result = false;
            logger.debug("线程: {}创建参保人条数 : {}", Thread.currentThread().getName(), taskList.size());
            try {
                for (CompanyUserResultVo companyUserResultVo : taskList) {
                    ReturnData returnData = addSingleCompanyUser(companyUserResultVo);
                    if (returnData.getRetCode() == CommonConstants.RETURN_CODE_FAIL) {
                        result = true;
                    }
                    returnDataList.add(returnData.getData());
                }
                //Exception 和 Error 都需要抓
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                logger.info("线程: {}创建参保人出现异常: {} ", Thread.currentThread().getName(), throwable);
                result = true;
            }
    
            resultList.add(result);
            threadLatch.countDown();
            logger.info("子线程 {} 计算过程已经结束,等待主线程通知是否需要回滚", Thread.currentThread().getName());
    
            try {
                mainLatch.await();
                logger.info("子线程 {} 再次启动", Thread.currentThread().getName());
            } catch (InterruptedException e) {
                logger.error("批量创建参保人线程InterruptedException异常");
                throw new SystemException("批量创建参保人线程InterruptedException异常");
            }
    
            if (rollBack.getRollBack()) {
                logger.error("批量创建参保人线程回滚, 线程: {}, 需要更新的信息taskList: {}",
                        Thread.currentThread().getName(),
                        JSONObject.toJSONString(taskList));
                logger.info("子线程 {} 执行完毕,线程退出", Thread.currentThread().getName());
                throw new SystemException("批量创建参保人线程回滚");
            }
    
            logger.info("子线程 {} 执行完毕,线程退出", Thread.currentThread().getName());
            return returnDataList;
        }
    

    思想就是使用两个CountDownWatch实现子线程的二段提交
    步骤:

    1. 主线程将任务分发给子线程,然后 使用 boolean await = threadLatch.await(20,
      TimeUnit.SECONDS); 阻塞主线程,等待所有子线程处理向数据库中插入的业务
    2. 使用 threadLatch.countDown(); 释放子线程锁定,同时使用 mainLatch.await(); 阻塞子线程,将程序的控制权交还给主线程
    3. 主线程检查子线程执行插入数据库的结果,若有非预期结果出现,主线程标记状态告知子线程回滚,然后使用 mainLatch.countDown(); 将程序控制权再次交给子线程,子线程检测回滚标志,判断是否回滚
    4. 子线程执行结束,主线程拼接处理结果,响应给请求方

    整个过程类似于GC的标记-清除过程(串行的垃圾收集器)

    展开全文
  • Java多线程并发之ThreadLocal

    千次阅读 多人点赞 2020-01-07 07:43:46
    ThreadLocal是线程本地变量,可以为多线程并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会...

    1. ThreadLocal是什么?使用场景

    ThreadLocal简介

    ThreadLocal是线程本地变量,可以为多线程的并发问题提供一种解决方式,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

    ThreadLocal使用场景

    多个线程去获取一个共享变量时,要求获取的是这个变量的初始值的副本。每个线程存储这个变量的副本,对这个变量副本的改变不去影响变量本身。适用于多个线程依赖不同变量值完成操作的场景。比如:

    • 多数据源的切换
    • spring声明式事务

    2. ThreadLocal的使用案例

    ThreadLocal类接口:

    • void set(T value):设置当前线程的线程局部变量的值
    • T get():获取当前线程所对应的线程局部变量
    • void remove():删除当前线程局部变量的值,目的是为了减少内存的占用
    • T initialValue():该线程局部变量的初始值(默认值为null),该方法是一个protected的懒加载方法,线程第1次调用get()或set(T value)时才执行在,而且也是为了让子类覆盖而设计的。
    public class ThreadLocalDemo {
        private static ThreadLocal<Index> index = new ThreadLocal(){
            @Override
            protected Object initialValue() {
                return new Index();
            }
        };
    
        private static class Index{
            private int num;
    
            public void incr(){
                num++;
            }
        }
    
        public static void main(String[] args) {
            for(int i=0; i<5; i++){
                new Thread(() ->{
                    Index local = index.get();
                    local.incr();
                    System.out.println(Thread.currentThread().getName() + " " + index.get().num);
                }, "thread_" + i).start();
            }
        }
    }
    

    输出结果:

    thread_1 1
    thread_0 1
    thread_3 1
    thread_4 1
    thread_2 1

    可以看到每个线程的获取的初始值都是0,并且对num++的操作也互不影响

    3.ThreadLocal如何实现的

    3.1 ThreadLocal的数据结构

    ThreadLocal内部维护的是一个类似Map的ThreadLocalMap数据结构,而每个Thread类,都有一个ThreadLocalMap成员变量。ThreadLocalMap将线程本地变量(ThreadLocal)作为key,线程变量的副本作为value,如图所示:

    需要注意的是ThreadLocal中的Entry的key和value的关系有系统进行维护,若维护不当则可能导致多线程状态下的不安全(一般不会,至少需要注意)。

    3.2 get()源码分析

      public T get() {
          	//获取当前线程
              Thread t = Thread.currentThread();
          	//获取当前线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
                  ThreadLocalMap.Entry e = map.getEntry(this);
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      T result = (T)e.value;
                      return result;
                  }
              }
          	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
              return setInitialValue();
          }
    

    3.2.1 ThreadLocalMap没初始化

    ThreadLocalMap没初始化,ThreadLocalMap为null时,会调用setInitialValue()方法:

      private T setInitialValue() {
          	//initialValue方法一般会被重写,不重写的话,直接返回null
              T value = initialValue();
              Thread t = Thread.currentThread();
          	//获取当前线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null)
                  //ThreadLocalMap已经被创建,那么直接设置初始值(即保存变量副本),初始值来自initialValue方法
                  map.set(this, value);
              else
                  //创建ThreadLocalMap
                  createMap(t, value);
              return value;
          }
    

    其中,initialValue()方法是由我们重写的,需要注意的是,返回值必须为new一个对象,而不是直接返回一个对象引用。因为如果多个线程都保存同一个引用的副本的话,那他们通过这个引用修改共享变量的值,是相互影响的。我们本来的目的便是为了获取共享变量的初始值副本,各个线程对副本的修改不影响变量本身。

    再来看看createMap是如何创建threadLocalMap的

      void createMap(Thread t, T firstValue) {
              t.threadLocals = new ThreadLocalMap(this, firstValue);
          }
    
      ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
          	   //创建一个初始容量为16的Entry数组
                  table = new Entry[INITIAL_CAPACITY];
          		//通过threadLocal的threadLocalHashCode来定位在数组中的位置
                  int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
          		//保存在数组中
                  table[i] = new Entry(firstKey, firstValue);
          		//记录下已用的大小
                  size = 1;
          		//设置阈值为容量的2/3
                  setThreshold(INITIAL_CAPACITY);
              }
    

    3.2.2 初始化threadLocalMap

    初始化threadLocalMap之后,此线程再次调用get()方法,又做了哪些操作呢

      public T get() {
          	//获取当前线程
              Thread t = Thread.currentThread();
          	//获取当前线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
                  ThreadLocalMap.Entry e = map.getEntry(this);
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      T result = (T)e.value;
                      return result;
                  }
              }
          	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
              return setInitialValue();
          }
    

    可以看到是通过map.getEntry(this)去查找元素的

      private Entry getEntry(ThreadLocal<?> key) {
                  int i = key.threadLocalHashCode & (table.length - 1);
                  Entry e = table[i];
                  if (e != null && e.get() == key)
                      return e;
                  else
                      //如果定位的元素的key与传入的key不相等,那么一直往后找
                      return getEntryAfterMiss(key, i, e);
              }
    
      private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
                  Entry[] tab = table;
                  int len = tab.length;
      
                  while (e != null) {
                      ThreadLocal<?> k = e.get();
                      if (k == key)
                          return e;
                      if (k == null)
                          //将过期的key清除掉,并把后面的元素(移动过位置的)往前移
                          expungeStaleEntry(i);
                      else
                          //往后移一位
                          i = nextIndex(i, len);
                      e = tab[i];
                  }
                  return null;
              }
    
      private static int nextIndex(int i, int len) {
                  return ((i + 1 < len) ? i + 1 : 0);
              }
    
      private int expungeStaleEntry(int staleSlot) {
          Entry[] tab = table;
          int len = tab.length;
      
          // 清除当前元素
          tab[staleSlot].value = null;
          tab[staleSlot] = null;
          size--;
      
          //将此元素后面的,因为hash冲突移动过位置的元素往前移
          Entry e;
          int i;
          for (i = nextIndex(staleSlot, len);
               (e = tab[i]) != null;
               i = nextIndex(i, len)) {
              ThreadLocal<?> k = e.get();
              if (k == null) {
                  e.value = null;
                  tab[i] = null;
                  size--;
              } else {
                  int h = k.threadLocalHashCode & (len - 1);
                  //h != i说明有过hash冲突
                  if (h != i) {
                      tab[i] = null;
      
                      // Unlike Knuth 6.4 Algorithm R, we must scan until
                      // null because multiple entries could have been stale.
                      while (tab[h] != null)
                          h = nextIndex(h, len);
                      tab[h] = e;
                  }
              }
          }
          return i;
      }
    

    如果map.getEntry(this)也找不到元素怎么办?

      public T get() {
          	//获取当前线程
              Thread t = Thread.currentThread();
          	//获取当前线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null) {
                  //如果ThreadLocalMap已经被创建了,那么通过当前的threadLocal对象作为key,获取value
                  ThreadLocalMap.Entry e = map.getEntry(this);
                  if (e != null) {
                      @SuppressWarnings("unchecked")
                      T result = (T)e.value;
                      return result;
                  }
              }
          	//如果ThreadLocalMap还没有被创建或者在ThreadLocalMap中查找不到此元素
              return setInitialValue();
          }
    

    那么继续调用setInitialValue()方法

      private T setInitialValue() {
          	//initialValue方法一般会被重写,不重写的话,直接返回null
              T value = initialValue();
              Thread t = Thread.currentThread();
          	//获取当前线程的ThreadLocalMap
              ThreadLocalMap map = getMap(t);
              if (map != null)
                  //ThreadLocalMap已经被创建,那么直接设置初始值(即保存变量副本),初始值来自initialValue方法
                  map.set(this, value);
              else
                  //创建ThreadLocalMap
                  createMap(t, value);
              return value;
          }
    

    可以看到将会调用它里面的map.set(this, value)方法

      private void set(ThreadLocal<?> key, Object value) {
                  Entry[] tab = table;
                  int len = tab.length;
                  int i = key.threadLocalHashCode & (len-1);
      
                  for (Entry e = tab[i];
                       e != null;
                       e = tab[i = nextIndex(i, len)]) {
                      ThreadLocal<?> k = e.get();
      
                      if (k == key) {
                          e.value = value;
                          return;
                      }
      
                      if (k == null) {
                          //替代过期的元素,并清除后面的一些过期元素
                          replaceStaleEntry(key, value, i);
                          return;
                      }
                  }
      			
          		//如果在table中确实找不到,那么新建一个
                  tab[i] = new Entry(key, value);
                  int sz = ++size;
                  if (!cleanSomeSlots(i, sz) && sz >= threshold)
                      //如果没有元素被清除,且超过阈值,那么扩容并重新hash定位
                      rehash();
              }
    

    3.3 set()源码分析

    在看看set()方式处理与setInitialValue类似,少了获取初始化值,其他都相同,可参考get()方式中ThreadLocalMap没初始化方式:

    public void set(T value) {
        //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    3.4 总结

    • 首先判断当前线程的threadLocalMap是否存在,若不存在则创建一个。将ThreadLocal作为key,共享变量初始值的副本作为value,保存在threadLocalMap中
    • 若threadLocalMap存在,那么将ThreadLocal作为key,通过hash散列定位在数组中的位置,查找value
    • 如果在threadLocalMap中找不到(原因:被删除了,或者此ThreaLocal没有加入到threadLocalMap,毕竟threadLocalMap可以存放多个threadLocal),那么将ThreadLocal作为key,共享变量初始值的副本作为value,保存在threadLocalMap中
    • 在set的过程中,如果当前位置有其他元素(即hash冲突),那么往后找,直到不存在其他元素。并且在set的过程中会清除一些过期(key为null)的元素。最后将根据size大小,决定是否扩容,重新hash定位

    往期文章:

    各位看官还可以吗?喜欢的话,动动手指点个💗,点个关注呗!!谢谢支持!
    欢迎关注公众号【Ccww技术博客】,原创技术文章第一时间推出

    在这里插入图片描述

    展开全文
  • 本文作者:陈进坚 ... 摘要 ​ 现在很多主流的编程语言都支持多线程或者协程处理,例如天生...但是多线程和单线程不同,多线程如果不对数据库做处理的话,有时候单线程跑的程序没有问题,但是一旦开启多线程,数据库就.
  • 多线程常常应用在大量密集型事务处理、高并发以及性能亟待提升的敏感区域,好处不言而喻:充分利用CPU资源、提高吞吐、减少用户等待、同事增强了程序的灵活性,更有利于内存任务调度、数据交互、资源分配等;...
  • Lockers在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性,在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作,下面看...
  • 多线程与高并发笔记

    千次阅读 多人点赞 2020-09-13 10:14:52
    1. 创建线程的三种方式 实现Runnable 重写run方法 继承Thread 重写run方法 线程池创建 Executors.newCachedThreadPool() 2. Thread线程操作方法 Thread.sleep([mills]) 当前线程睡眠指定mills毫秒 Thread....
  • 多线程&并发-实例与解决方案

    千次阅读 2020-06-23 11:27:24
    java中你知道哪些锁? 问题回答: 乐观锁/悲观锁 共享锁/独享锁 公平锁/非公平锁 互斥锁/读写锁 可重入锁 自旋锁 分段锁 偏向锁/轻量级锁/重量级锁...2.就绪/运行(RUNNABLE):该状态包含了经典线程模型的两种状态:就
  • Servlet 单例多线程并发安全性

    千次阅读 2015-02-04 00:22:31
    Servlet 单例多线程 Servlet如何处理多个请求访问? Servlet容器默认是采用单实例多线程的方式处理多个请求的: 1.当web服务器启动的时候(或客户端发送请求到服务器时),Servlet就被加载并实例化(只存在一...
  • 串行效率低,耗时长,为了提高效率,这个时候我们首先想到多线程并发插入,但是如何控制事务呢 … 直接上干货 实现效果 开启多条子线程,并发插入数据库 当其中一条线程出现异常,或者处理结果为非预期结果...
  • 多线程并发库应用十-锁

    千次阅读 2018-01-09 21:52:36
    在java 1.5 之后加入并发包加入了一个锁的类 在学习或者使用Java的过程中进程会遇到各种...公平锁是指线程在等待同一个锁时,必须按照申请锁的先后顺序来一次获得锁。公平锁的好处是等待锁的线程不会饿死,但是整
  • 缺点,需要自己实现等待的逻辑,等待的变量如果过多,逻辑就会,代码就是非臃肿,循环多久是不确定的。没法精准的控制。 二、使用Thread 类的join() 阻塞当前线程以等待子线程处理完毕。缺点粒度不够细 三、通过...
  • spring 多线程 事务的实际应用场景

    千次阅读 2020-03-04 18:37:06
    今天分享的是spring 事务部分的源码: 环境: spring-jdbc 5.0.4.REALEASE 今天分享一下spring事务的方法,这一篇会涉及到多线程的几种场景使用和分析。
  • MongoDB 在多线程并发下的问题

    千次阅读 2014-08-26 17:09:08
    因为是结合了最近流行的 Storm进行大数据的分析处理,并将分析结果插入Vertica数据库,所以在多线程并发的情境下, 会发现 Vertica 数据库中有部分重复的数据. 这到底是什么原因导致的呢?笔者开始也是一筹莫 展,...
  •   假设是多线程请求,那么第一个请求来到,获取值,在修改保存到数据库之前。第二个请求也来获取值。这时两个请求拿到的是同一份值,然后第一个请求保存,接着第二个请求保存。这种情况下如果每个请求修改值的逻辑...
  • 多线程并发内部实现机制

    千次阅读 2016-11-02 17:18:10
    多线程和共享内存线程模型争用及并发访问如何能够打破不变量作为争用标准解决方案的锁定何时需要锁定如何使用锁定;理解开销锁定如何能够各行其道   十年前,只有核心系统程序员会担心在多个执行线程的情况下...
  • 电商平台中最重要的一点就是卖东西。...假设平台上同时有很用户在操作,在不考虑效率的情况下,我们用同步方法来模拟这个场景。首先写一个订单处理类:class OrderHandler{ /*初始某商品库存量*/ int StockSome
  • spring mvc 多线程并发

    万次阅读 2018-05-04 17:28:13
    ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。http://www.xuebuyuan.com/1628190.html 我们知道Spring通过各种DAO模板类降低了开发者使用各种...
  • 关于事务并发线程同步概念问题

    千次阅读 2016-11-06 23:33:30
    事务,是并发控制的单位,解决了多个客户端并发方式访问数据库 ,造成的 并发问题。 多线程的好处:开启多个线程是为了同时运行多部分代码。解决了多部分同时运行的问题。 多线程带来的线程安全问题的原因: ...
  • JAVA多线程并发处理方式

    千次阅读 2016-10-26 11:16:22
     ...ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。 ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别:  Synchroni
  • java中多线程并发的处理方式

    千次阅读 2014-03-16 12:49:58
    ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。 ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别: synchronized是利用锁的机制,使变量或代码...
  • 对于同时运行的事务,当这些 访问数据库中相同的数据时,如果没有必要的隔离机制,就会导致各种并发问题,这些问题可以归纳为以下几类: 1、第一类丢失更新:撤销一个事务时,把其他事务已提交更新的数据覆盖。...
  • Java中多线程并发处理方式

    千次阅读 2019-01-11 10:12:03
    ThreadLocal使用场合主要解决多线程中数据因并发产生不一致问题。 ThreadLocal和Synchonized都用于解决多线程并发访问。但是ThreadLocal与synchronized有本质的区别: synchronized是利用锁的机制,使变量或代码块在...
  • 多线程事务回滚

    万次阅读 2016-05-10 14:43:08
    使用Callable接口获取并发任务结果,实现对线程异常的感知并重新抛出运行时异常实现事务回滚。
  • SpringBoot 多线程事务处理

    千次阅读 2021-01-22 14:49:22
    Springboot 在多线程下,处理事务; 目前很多开发的小伙伴们,都开始采用Springboot了,因为给我们带来了许多的开发便利,只需要我们关注编写逻辑代码。在工作中是会有很多小伙伴在项目逐步实用到多线程、线程池等...
  • 多线程并发同一个表问题

    千次阅读 2011-09-12 18:48:16
    现有数据库开发过程中对事务的控制、事务锁、行锁、表锁的发现缺乏必要的方法和手段,...For Update和For Update of使用户能够锁定指定表或表的数据行这个功能在实际应用中具有很重要的意义,特别对于多用户多线程
  • import java.util.concurrent.*; /** * 线程池测试类 */ ...public class ThreadPoolTest { ... * 多线程的任务类 */ static class Task implements Runnable{ //线程id private int id; ...
  • 分布式、多线程、高并发都不懂,拿什么去跳槽

    万次阅读 多人点赞 2019-10-09 01:03:16
    当提起这三个词的时候,是不是很多人都认为分布式=高并发=多线程?当面试官问到高并发系统可以采用哪些手段来解决,或者被问到分布式系统如何解决一致性的问题,是不是一脸懵逼?确...
  • 解决多线程并发问题

    千次阅读 2017-12-21 03:02:26
    如果对该表的更新或插入的操作,都会经过一个统一的文件,这种方式是可以解决的进程并发的问题; 实现方式如下: public static function cbInventoryReserve() { $LOCK_FILE_PATH = $_SERVER['DOCUMENT_...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 147,166
精华内容 58,866
关键字:

多线程事务并发