精华内容
下载资源
问答
  • 多线程锁定同一资源会造成死锁 线程池中的任务使用当前线程池也可能出现死锁 参考连接: https://blog.csdn.net/qq_35064774/article/details/51793656 情况一: 死锁是两个或多个线程互相等待对方所有用的资源...

    死锁

    接下来从几个方面介绍:

    1. 多线程死锁
    2. 多线程死锁解决办法
    3. 数据库死锁
    4. 数据库死锁解决办法

    多线程死锁是怎么造成的?

    1. 多线程锁定同一资源会造成死锁
    2. 线程池中的任务使用当前线程池也可能出现死锁
    参考连接:
    https://blog.csdn.net/qq_35064774/article/details/51793656
    

    情况一: 死锁是两个或多个线程互相等待对方所有用的资源情形:现在有线程1和线程2。线程1执行过程中,先锁定了对象a,然后需要再锁定b才能继续执行代码;而线程2正巧相反,先锁定了b,需要再锁定a才能继续执行代码。这时,两个线程都等着对方解锁,才能继续执行,这时,两个线程就进入等待状态,最终不会有线程执行。这就变成了死锁。

    接下来是代码实例:

    class DeadLock implements Runnable{
    
        boolean lockFormer;
        static Object o1 = new Object();
        static Object o2 = new Object();
    
        DeadLock(boolean lockFormer){
            this.lockFormer = lockFormer;
        }
    
        @Override
        public void run() {
    
            if (this.lockFormer){
                synchronized(o1){
                    try{
                        Thread.sleep(500);
                        System.out.println("线程1");
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    synchronized (o2){
                        System.out.println("lok");
                    }
                }
            }else {
                synchronized (o2){
                    try{
                        Thread.sleep(500);
                        System.out.println("线程2");
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                    synchronized (o1){
                        System.out.println("lok");
                    }
                }
            }
    
        }
    }
    

    尽量避免加多个锁,避免死锁。

    参考连接:
    http://www.importnew.com/30277.html#comment-795471
    https://www.cnblogs.com/caoshenglu/p/9461567.html
    

    情况二: 线程池自己引发的死锁
    单线程使用不当引发死锁,实现代码:

    @Slf4j
        class DeadLock2 {
    
            public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(10);
                pool.submit(() -> {
                    try{
                        log.info("First");
                        pool.submit(() -> log.info("second")).get();
                        log.info("third");
                    }catch (InterruptedException | ExecutionException e){
                        log.error("Error",e);
                    }
                });
                System.out.println("process is over");
            }
    }
    

    看起来没有什么问题-所有信息都按预期输出:

    process is over
    15:46:07.470 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - First
    15:46:07.474 [pool-1-thread-2] INFO xiaowang.org.prictice.DeadLock2 - second
    15:46:07.474 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - third
    

    注意我们用 get() 阻塞线程,在显示“Third”之前必须等待内部线程(Runnable)运行完成。这是个大坑!等待内部任务完成意味着需要从线程池额外获取一个线程来执行任务。然而,我们已经使用到了一个线程,所以内部任务在获取到第二个线程前将一直阻塞。当前我们的线程池足够大,运行没问题。让我们稍微改变一下代码,将线程池缩减到只有一个线程,另外关键的一点是我们移除 get() 方法:

    @Slf4j
        class DeadLock2 {
    
            public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(10);
                pool.submit(() -> {
                        log.info("First");
                        pool.submit(() -> log.info("second"));
                        log.info("third");
                });
                System.out.println("process is over");
            }
    }
    

    代码运行正常,只是有点乱:

    15:51:06.334 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - First
    15:51:06.339 [pool-1-thread-2] INFO xiaowang.org.prictice.DeadLock2 - second
    15:51:06.339 [pool-1-thread-1] INFO xiaowang.org.prictice.DeadLock2 - third
    

    顺序的改变完全在预料之内,没有涉及线程间的竞态条件(事实上我们只有一个线程)。仔细分析一下发生了什么:我们向线程池提交了一个新任务(打印“Second”的任务),但这次我们不需要等待这个任务完成。因为线程池中唯一的线程被打印“First”和“Third”的任务占用,所以这个外层任务继续执行,并打印“Third”。当这个任务完成时,将单个线程释放回线程池,内部任务最终开始执行,并打印“Second”。那么死锁在哪里?来试试在内部任务里加上 get() 方法:

    只有一个线程,
    ExecutorService pool = Executors.newFixedThreadPool(1)

    @Slf4j
        class DeadLock2 {
    
            public static void main(String[] args) {
                ExecutorService pool = Executors.newFixedThreadPool(1);
                pool.submit(() -> {
                    try{
                        log.info("First");
                        pool.submit(() -> log.info("second")).get();
                        log.info("third");
                    }catch (InterruptedException | ExecutionException e){
                        log.error("Error",e);
                    }
                });
                System.out.println("process is over");
            }
    }
    

    死锁出现了!我们来一步一步分析:

    • 打印“First”的任务被提交到只有一个线程的线程池
    • 任务开始执行并打印“First”
    • 我们向线程池提交了一个内部任务,来打印“Second”
    • 内部任务进入等待任务队列。没有可用线程因为唯一的线程正在被占用
    • 我们阻塞住并等待内部任务执行结果。不幸的是,我们等待内部任务的同时也在占用着唯一的可用线程
    • get() 方法无限等待,无法获取线程
    • 死锁

    多线程死锁解决办法

    参考地址:
    http://ifeve.com/deadlock-prevention/
    https://mp.weixin.qq.com/s/BVGtDDCa7yjtfJJPNKOC_g
    

    方法一: 加锁顺序
    当索格线程需要相同的锁,但按照不同的顺序加锁,死锁就很容易发生。
    如果能确保所有的线程都是按照相同的顺序获取锁,那么死锁就不会发生,实例如下:

    Thread 1:
    	lock A
    	Lock B
    
    Thread 2 :
    	wait for A
    	lock C (when A locked)
    	
    Thread 3:
    	wait for A
    	wait for B
    	wait for C
    

    如果一个线程(比如线程3)需要一些锁,那么必须按照确定的顺序获取锁。他只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。

    例如,线程2和线程3只有在获取了锁A之后才能尝试获取锁C。因为线程1已经拥有了锁A,所以线程2和3需要等到锁A被释放。然后他们尝试对B和C加锁之前,必须成功的对A加锁。

    按顺序加锁是一种有效的死锁预防机制。但是,这种方式需要你事先知道所有可能会用到的锁,但总有些时候是无法预知的。

    方法二: 加锁限时
    另外一个避免死锁的方法,尝试获取锁时候加一个超时时间,这也意味着在尝试获取的过程中,若超过了这个时限,该线程则放弃对该锁的请求。若一个线程没有在给定的时间内获取到所需要的锁,则进行回退并释放所有以获得的锁,然后再等待一段随机时间再尝试。这段随机的等待时间,让其他线程有机会尝试获取相同的这些锁,并且让该应用再没有获得锁的时候可以继续进行。
    实例如下:

    Thread 1 locks A
    Thread 2 locks B
    
    Thread 1 attempts to lock B but is blocked
    Thread 2 attempts to lock A but is blocked
    
    Thread 1's lock attempt on B times out
    Thread 1 backs up and releases A as well
    Thread 1 waits randomly (e.g. 257 millis) before retrying.
    
    Thread 2's lock attempt on A times out
    Thread 2 backs up and releases B as well
    Thread 2 waits randomly (e.g. 43 millis) before retrying.
    
    

    在以上的例子中,线程2比线程1早200毫秒经行重试加锁,因此它可以先成功地获取到两个锁。这时,线程1尝试获取锁A并且处于等待状态。当线程2结束时,线程1也可以顺利的获得这两个锁(除非线程2或者其它线程在线程1成功获得两个锁之前又获得其中的一些锁)。

    需要注意的是,由于存在锁的超时,所以我们不能认为这种场景就一定是出现了死锁。也可能是因为获得了锁的线程(导致其它线程超时)需要很长的时间去完成它的任务。

    此外,如果有非常多的线程同一时间去竞争同一批资源,就算有超时和回退机制,还是可能会导致这些线程重复地尝试但却始终得不到锁。如果只有两个线程,并且重试的超时时间设定为0到500毫秒之间,这种现象可能不会发生,但是如果是10个或20个线程情况就不同了。因为这些线程等待相等的重试时间的概率就高的多(或者非常接近以至于会出现问题)。
    (译者注:超时和重试机制是为了避免在同一时间出现的竞争,但是当线程很多时,其中两个或多个线程的超时时间一样或者接近的可能性就会很大,因此就算出现竞争而导致超时后,由于超时时间一样,它们又会同时开始重试,导致新一轮的竞争,带来了新的问题。)

    这种机制存在一个问题,在Java中不能对synchronized同步块设置超时时间。你需要创建一个自定义锁,或使用Java5中java.util.concurrent包下的工具。写一个自定义锁类不复杂,但超出了本文的内容。后续的Java并发系列会涵盖自定义锁的内容。

    方法三: 死锁检测
    Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。

    我们在命令行中敲入jconsole命令,会自动弹出以下对话框,选择进程1362,并点击“链接”
    在这里插入图片描述
    进入所检测的进程后,选择“线程”选项卡,并点击“检测死锁”
    在这里插入图片描述

    数据库死锁

    参考连接:
    https://juejin.im/entry/57e7685abf22ec00586ed574
    

    1. innodb隔离级别、索引与锁

    假设我们有一张表(msg),里面有3个字段。假设id是主键,token是非唯一索引,message没有索引。

    id:bigint           token:varchar(30)                               message:varchar(4096)
    

    innodb对于主键使用密集索引,这是一种数据存储的方式,表数据是和主键一起存储,主键索引的叶子结点存储行数据。对于普通索引,其叶子结点存储的是主键值。

    Alt
    下面分析索引和锁的关系(在RC级别下):

    1. delete from msg where id = 2;

      由于id是主键,因此直接锁住整行记录即可。

    Alt
    2. delete from msg where token = ‘cvs’;
    由于token是二级索引,因此先锁住二级索引(两行),接着会锁住相应主键对应的记录;
    Alt
    3. delete from msg where message = ‘订单号是多少’;
    message没有索引,所以走的是全表扫描过滤。这时表上的各个记录都能将添加上X锁。
    Atl

    2. 锁与隔离级别的关系

    数据库的事务隔离级别:

    1. 未提交读(read uncommitted)
    2. 已提交读(read committed):能读到已经提交的数据。
    3. 可重复读(repeatable read):在同一个事务内查询都是事务开始时刻一致的,InnoDB默认级别.
    4. 串行化(Serializable)

    我们较常用的是RC和RR.
    如下图所示,事务A在第一次查询时得到1条记录,在第二次执行相同查询时却得到两条记录。从事务A角度上看是见鬼了!这就是幻读,RC级别下尽管加了行锁,但还是避免不了幻读。
    Alt
    innodb的RR隔离级别可以避免幻读发生,怎么实现?当然需要借助于锁了!

    为了解决幻读问题,innodb引入了gap锁。

    在事务A执行:update msg set message=‘订单’ where token=‘asd’;

    innodb首先会和RC级别一样,给索引上的记录添加上X锁,此外,还在非唯一索引’asd’与相邻两个索引的区间加上锁。

    这样,当事务B在执行insert into msg values (null,‘asd’,’hello’); commit;时,会首先检查这个区间是否被锁上,如果被锁上,则不能立即执行,需要等待该gap锁被释放。这样就能避免幻读问题。
    Alt

    3. 死锁成因

    情况一: 不同的表相同记录行锁冲突
    这种情况很好理解,事务A和事务B操作两张表,但是出现循环等待情况。
    Atl
    情况二: 相同表记录行锁冲突
    这种情况比较常见,之前遇到两个job在执行数据批量更新时,jobA处理的的id列表为[1,2,3,4],而job处理的id列表为[8,9,10,4,2],这样就造成了死锁。
    alt
    情况三: 不同索引锁冲突
    这种情况比较隐晦,事务A在执行时,除了在二级索引加锁外,还会在聚簇索引上加锁,在聚簇索引上加锁的顺序是[1,4,2,3,5],而事务B执行时,只在聚簇索引上加锁,加锁顺序是[1,2,3,4,5],这样就造成了死锁的可能性。
    alt
    情况四: gap锁冲突
    innodb在RR级别下,如下的情况也会产生死锁,比较隐晦。不清楚的同学可以自行根据上节的gap锁原理分析下。
    alt

    如何尽可能避免数据库死锁

    1. 以固定的顺序访问表和行。比如对第2节两个job批量更新的情形,简单方法是对id列表先排序,后执行,这样就避免了交叉等待锁的情形;又比如对于3.1节的情形,将两个事务的sql顺序调整为一致,也能避免死锁。
    2. 大事务拆小。大事务更倾向于死锁,如果业务允许,将大事务拆小。
    3. 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减少死锁概率。
    4. 降低隔离级别。如果业务允许,将隔离级别调低也是较好的选择,比如将隔离级别从RR调整为RC,可以避免掉很多因为gap锁造成的死锁。
    5. 为表添加合理的索引。可以看到如果不走索引将会为表的每一行记录添加上锁,死锁的概率大大增大。
    展开全文
  • 死锁是指两个或线程被阻塞,等待获得死锁中其他一些线程所持有的锁。当线程同时需要相同的锁,但以不同的顺序获取它们时,可能会发生死锁。 例如,如果线程1锁定A并试图锁定B,而线程2已经锁定B并试图锁定A...

    翻译:GentlemanTsao ,2020-05-26

    线程死锁

    死锁是指两个或多个线程被阻塞,等待获得死锁中其他一些线程所持有的锁。当多个线程同时需要相同的锁,但以不同的顺序获取它们时,可能会发生死锁。

    例如,如果线程1锁定A并试图锁定B,而线程2已经锁定B并试图锁定A,则会出现死锁。线程1永远得不到B,线程2永远得不到A。此外,他们都不会知道。他们将永远被阻塞在他们各自的对象上,A和B。这种情况就是死锁。

    这种情况如下所示:

    Thread 1  locks A, waits for B
    Thread 2  locks B, waits for A

    下面的示例是在不同实例中调用同步方法的TreeNode类:

    public class TreeNode {
     
      TreeNode parent   = null;  
      List     children = new ArrayList();
    
      public synchronized void addChild(TreeNode child){
        if(!this.children.contains(child)) {
          this.children.add(child);
          child.setParentOnly(this);
        }
      }
      
      public synchronized void addChildOnly(TreeNode child){
        if(!this.children.contains(child){
          this.children.add(child);
        }
      }
      
      public synchronized void setParent(TreeNode parent){
        this.parent = parent;
        parent.addChildOnly(this);
      }
    
      public synchronized void setParentOnly(TreeNode parent){
        this.parent = parent;
      }
    }

    如果线程(1)调用parent.addChild(child)方法,另一个线程(2)同时调用child.setParent(parent)方法,在同一parent实例和child实例上,可能会发生死锁。可以用下面的伪代码说明这一点:

    Thread 1: parent.addChild(child); //locks parent
              --> child.setParentOnly(parent);
    
    Thread 2: child.setParent(parent); //locks child
              --> parent.addChildOnly()

    首先,线程1调用parent.addChild(child)。由于addChild()是同步的,因此线程1实际上锁定了parent对象,使其他线程无法访问。

    然后,线程2调用child.setParent(parent)。由于setParent()是同步的,因此线程2实际上锁定了child对象,使其他线程无法访问。

    现在child对象和parent对象都被两个不同的线程锁定了。接下来,线程1试图调用child.setParentOnly() 方法,但child对象被线程2锁定,因此该方法就阻塞了。线程2也尝试调用parent.addChildOnly(),但parent对象被线程1锁定,导致线程2在该方法上阻塞。现在,两个线程都被阻塞,等待获得另一个线程持有的锁。

    注意:两个线程必须同时调用parent.addChild(child)和child.setParent(parent),就像上面描述的那样,在相同的parent实例和child实例上,才发生死锁。上面的代码可能会在很长一段时间内执行正常,直到突然死锁。

    线程真的需要“在同一时刻”锁定。例如,如果线程1比线程2提前一点,从而锁定了A和B,那么线程2在尝试锁定B时将被阻塞。也就不会发生死锁。由于线程调度通常是不可预测的,因此无法预测何时发生死锁。只能知道死锁“会”发生。

    更复杂的死锁

    死锁还可以包含两个以上的线程。这使得它更难被发现。下面是四个线程死锁的示例:

    线程 1  锁定 A, 等待 B
    线程 2  锁定 B, 等待 C
    线程 3  锁定 C, 等待 D
    线程 4  锁定 D, 等待 A

    线程1等待线程2,线程2等待线程3,线程3等待线程4,线程4等待线程1.

    数据库死锁

    发生死锁的一种更复杂的情况是数据库业务。一笔数据库业务可能包含许多SQL更新请求。当一笔数据库业务更新记录时,该记录将被锁定,使其他数据库业务无法更新,直到第一笔业务完成。因此,同一业务中的各个更新请求可能会锁定数据库中的某些记录。

    例如:

    Transaction 1, request 1, locks record 1 for update
    Transaction 2, request 1, locks record 2 for update
    Transaction 1, request 2, tries to lock record 2 for update.
    Transaction 2, request 2, tries to lock record 1 for update.

    由于锁是在不同的请求中获取的,并且无法全部知道业务所需的所有锁,因此很难检测或防止数据库业务中出现死锁。

    下一篇:
    2020版Java并发和多线程教程(十七):防范死锁

    并发系列专栏:
    Java并发和多线程教程2020版

    展开全文
  • 多线程数据库进行操作会出现 database is locked..... 解决办法是采取单例模式获得DatabaseHelper  private static DatabaseHelper instance = null; public static DatabaseHelper getInstance(Context...

    多线程对数据库进行操作会出现 database is locked.....

    解决办法是采取单例模式获得DatabaseHelper 

          private static DatabaseHelper instance = null;
    
    
    	public static DatabaseHelper getInstance(Context context) {
    
    
    		if (instance == null) {
    
    
    			instance = new DatabaseHelper(context);
    		}
    
    
    		return instance;
    	}
           然后不关闭数据库,这样就不会出现 database is locked了

    展开全文
  • 线程死锁、数据库死锁、慢sql、长事务等性能问题

    服务器资源(cpu、内存、磁盘io、网络)是否存在性能瓶颈、是否存在队列、线程池、连接池、线程死锁、数据库死锁、慢sql、长事务等性能问题

    一、队列

    队列是一种常见的数据结构,也是我们学习,在计算机的世界里,基础的数据结构只有两种,一种是线性连续的存储结构–数组,还有一种是随机的存储结构—链表,很多常用的数据结构都是基于这两种基础数据结构衍生出来的,队列如它的名字,是一种先进先出(FIFO)的数据结构,在我们的实际生活或者技术方面由很广泛的应用,比如各种消息队列,阻塞队列等

    二、线程池

    线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务
    线程池线程都是后台线程。每个线程都使用默认的堆栈大小,以默认的优先级运行,并处于多线程单元中。如果某个线程在托管代码中空闲(如正在等待某个事件),则线程池将插入另一个辅助线程来使所有处理器保持繁忙。如果所有线程池线程都始终保持繁忙,但队列中包含挂起的工作,则线程池将在一段时间后创建另一个辅助线程但线程的数目永远不会超过最大值,超过最大值的线程可以排队,但他们要等到其他线程完成后才启动

    2.1 线程池模式

    2.1.1 HS/HA半同步/半异步模式

    半同步/半异步模式又称为生产者消费者模式,是比较常见的实现方式,比较简单。分为同步层、队列层、异步层三层。同步层的主线程处理工作任务并存入工作队列,工作线程从工作队列取出任务进行处理,如果工作队列为空,则取不到任务的工作线程进入挂起状态,由于线程间有数据通信,因此不适于大数据量交换的场合

    2.1.2 L/F领导者与跟随者模式

    领导者跟随者模式,在线程池中的线程可处在3种状态之一:领导者leader、追随者follower或工作者processor。任何时刻线程池只有一个领导者线程。事件到达时,领导者线程负责消息分离,并从处于追随者线程中选出一个来当继任领导者,然后将自身设置为工作者状态去处置该事件。处理完毕后工作者线程将自身的状态置为追随者。这一模式实现复杂,但避免了线程间交换任务数据,提高了CPU cache相似性。在ACE(Adaptive Communication Environment)中,提供了领导者跟随者模式实现

    2.2 线程池的伸缩性对性能有较大的影响

    创建太多线程,将会浪费一定的资源,有些线程未被充分使用。
    销毁太多线程,将导致之后浪费时间再次创建它们。
    创建线程太慢,将会导致长时间的等待,性能变差。
    销毁线程太慢,导致其它线程资源饥饿

    三、连接池

    连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用

    3.1 连接池主要的优点

    减少连接创建时间
    虽然与其它数据库相比 GBase 提供了较为快速连接功能,但是创建新的 JDBC 连接仍会招致网络和 JDBC 驱动的开销。如果这类连接是“循环”使用的,使用该方式这些花销就可避免

    简化的编程模式
    当使用连接池时,每一个单独的线程能够像创建了一个自己的 JDBC 连接一样操作,允许用户直接使用JDBC编程技术

    受控的资源使用
    如果用户不使用连接池,而是每当线程需要时创建一个新的连接,那么用户的应用程序的资源使用会产生非常大的浪费并且可能会导致高负载下的异常发生。
    注意,每个连到 GBase 的连接在客户端和服务器端都有花销(内存,CPU,上下文切换等等)。每个连接均会对应用程序和 GBase 服务器的可用资源带来一定的限制。不管这些连接是否在做有用的工作,仍将使用这些资源中的相当一部分

    连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢

    四、线程死锁

    4.1 死锁概述

    线程死锁是指两个或两个以上的线程互相持有对方所需要的资源,由于synchronized的特性,一个线程持有一个资源,或者说获得一个锁,在该线程释放这个锁之前,其它线程是获取不到这个锁的,而且会一直死等下去,因此这便造成了死锁

    4.2 死锁产生的条件

    互斥条件:一个资源,或者说一个锁只能被一个线程所占用,当一个线程首先获取到这个锁之后,在该线程释放这个锁之前,其它线程均是无法获取到这个锁的。
    占有且等待:一个线程已经获取到一个锁,再获取另一个锁的过程中,即使获取不到也不会释放已经获得的锁。
    不可剥夺条件:任何一个线程都无法强制获取别的线程已经占有的锁
    循环等待条件:线程A拿着线程B的锁,线程B拿着线程A的锁。。

    五、数据库死锁

    5.1 数据库死锁具备条件

    虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件
    1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。
    2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。
    3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。
    4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源

    5.2 产生死锁的情况

    一般情况只发生锁超时,就是一个进程需要访问数据库表或者字段的时候,另外一个程序正在执行带锁的访问(比如修改数据),那么这个进程就会等待,当等了很久锁还没有解除的话就会锁超时,报告一个系统错误,拒绝执行相应的SQL操作
    发生死锁的情况比较少,比如一个进程需要访问两个资源(数据库表或者字段),当获取一个资源的时候进程就对它执行锁定,然后等待下一个资源空闲,这时候如果另外一个进程也需要两个资源,而已经获得并锁定了第二个资源,那么就会死锁,因为当前进程锁定第一个资源等待第二个资源,而另外一个进程锁定了第二个资源等待第一个资源,两个进程都永远得不到满足

    5.3 数据库死锁的解决方案

    死锁的预防和解除:
    理解了死锁的原因,尤其是产生死锁的四个必要条件,就可以最大可能地避免、预防和解除死锁。所以,在系统设计、进程调度等方面注意如何不让这四个必要条件成立,如何确定资源的合理分配算法,避免进程永久占据系统资源。此外,也要防止进程在处于等待状态的情况下占用资源,在系统运行过程中,对进程发出的每一个系统能够满足的资源申请进行动态检查,并根据检查结果决定是否分配资源,若分配后系统可能发生死锁,则不予分配,否则予以分配 。因此,对资源的分配要给予合理的规划

    5.4 如何将死锁减至最少

    虽然不能完全避免死锁,但可以使死锁的数量减至最少。将死锁减至最少可以增加事务的吞吐量并减少系统开销,因为只有很少的事务回滚,而回滚会取消事务执行的所有工作。由于死锁时回滚而由应用程序重新提交
    下列方法有助于最大限度地降低死锁:
    (1)按同一顺序访问对象
    (2)避免事务中的用户交互
    (3)保持事务简短并在一个批处理中
    (4)使用低隔离级别
    (5)使用绑定连接

    5.4.1 按同一顺序访问对象

    如果所有并发事务按同一顺序访问对象,则发生死锁的可能性会降低。例如,如果两个并发事务获得 Supplier 表上的锁,然后获得 Part 表上的锁,则在其中一个事务完成之前,另一个事务被阻塞在 Supplier 表上。第一个事务提交或回滚后,第二个事务继续进行。不发生死锁。将存储过程用于所有的数据修改可以标准化访问对象的顺序

    5.4.2 避免事务中的用户交互

    避免编写包含用户交互的事务,因为运行没有用户交互的批处理的速度要远远快于用户手动响应查询的速度,例如答复应用程序请求参数的提示。例如,如果事务正在等待用户输入,而用户去吃午餐了或者甚至回家过周末了,则用户将此事务挂起使之不能完成。这样将降低系统的吞吐量,因为事务持有的任何锁只有在事务提交或回滚时才会释放。即使不出现死锁的情况,访问同一资源的其它事务也会被阻塞,等待该事务完成

    5.4.3 保持事务简短并在一个批处理中

    在同一数据库中并发执行多个需要长时间运行的事务时通常发生死锁。事务运行时间越长,其持有排它锁或更新锁的时间也就越长,从而堵塞了其它活动并可能导致死锁。
    保持事务在一个批处理中,可以最小化事务的网络通信往返量,减少完成事务可能的延迟并释放锁。

    5.4.4 使用低隔离级别

    确定事务是否能在更低的隔离级别上运行。执行提交读允许事务读取另一个事务已读取(未修改)的数据,而不必等待第一个事务完成。使用较低的隔离级别(例如提交读)而不使用较高的隔离级别(例如可串行读)可以缩短持有共享锁的时间,从而降低了锁定争夺

    5.4.5 使用绑定连接

    使用绑定连接使同一应用程序所打开的两个或多个连接可以相互合作。次级连接所获得的任何锁可以象由主连接获得的锁那样持有,反之亦然,因此不会相互阻塞

    六、慢sql:

    6.1 导致慢 SQL 的原因

    在遇到慢 SQL 情况时,不能简单的把原因归结为 SQL 编写问题(虽然这是最常见的因素),实际上导致慢 SQL 有很多因素,甚至包括硬件和 mysql 本身的 bug。根据出现的概率从大到小,如下
    SQL编写问题

    业务实例相互干绕对 IO/CPU 资源争用
    服务器硬件
    MYSQL BUG

    6.2 由 SQL 编写导致的慢 SQL 优化

    针对SQL编写导致的慢 SQL,优化起来还是相对比较方便的。正如上一节提到的正确的使用索引能加快查询速度,那么我们在编写 SQL 时就需要注意与索引相关的规则

    字段类型转换导致不用索引,如字符串类型的不用引号,数字类型的用引号等,这有可能会用不到索引导致全表扫描

    mysql 不支持函数转换,所以字段前面不能加函数,否则这将用不到索引

    不要在字段前面加减运算

    字符串比较长的可以考虑索引一部份减少索引文件大小,提高写入效率

    like % 在前面用不到索引

    根据联合索引的第二个及以后的字段单独查询用不到索引

    不要使用 select *

    排序请尽量使用升序

    or 的查询尽量用 union 代替 (Innodb)

    复合索引高选择性的字段排在前面

    order by / group by 字段包括在索引当中减少排序,效率会更高

    尽量规避大事务的 SQL,大事务的 SQL 会影响数据库的并发性能及主从同步

    分页语句 limit 的问题

    删除表所有记录请用 truncate,不要用 delete

    不让 mysql 干多余的事情,如计算

    输写 SQL 带字段,以防止后面表变更带来的问题,性能也是比较优的 ( 涉及到数据字典解析,请自行查询资料)

    七、长事务:

    长事务,顾名思义就是一个事务执行了很长时间仍未结束。那么一个事务执行多长时间算是长事务?这在不同的数据库产品中有不同的定义,对于informix数据库来说占用逻辑日志个数的百分比达到长事务高水位线就被定义为一个长事务,而在oracle中一个事务执行时间超过6秒钟就被认为是长事务。一般来说在一个联机事务处理系统中事务执行时间应该控制在一定范围内,如果一个事务执行时间太长,会长时间锁定某些资源,不利于并发处理,有时还会因为某些资源耗尽导致数据库挂起

    展开全文
  • 可参考: 数据库死锁那些事儿 https://blog.csdn.net/eseaqyq/article/details/7795023 MySQL InnoDB死锁死锁检测(四)http://www.ywnds.com/?p=4949 二、MySQL InnoDB死锁检测 1) 尽量不出现死锁 在代码层...
  • 场景一:多线程并行插入,插入失败则更新导致的死锁(不易排查, 事务隔离级别RR和RC都会出现) 场景描述 有一个每日交易任务,当交易额度满足一定数量时,就可以增加一次任务完成记录(user_no和trade_date组成唯一...
  •  线上生产环境在某些时候经常性的出现数据库操作死锁,导致业务人员无法进行操作。经过DBA的分析,是某一张表的insert操作和delete操作发生了死锁。简单介绍下数据库的情况(因为涉及到真实数据,这里做了模拟,不...
  • 多线程死锁

    2018-11-05 10:20:32
    死锁是两个或线程阻塞着等待其他处于死锁状态的线程所持有的锁。 死锁通常发生在线程同时但以不同的顺序请求同一组锁的时候。 数据库死锁 更加复杂的死锁场景发生在数据库事务中。一个数据库事务可能由...
  • Java多线程死锁、活锁与饥饿

    千次阅读 2020-02-17 19:27:23
    文章目录Java多线程死锁死锁的影响死锁的四个必要条件如何定位死锁修复死锁的策略线上发生死锁应该怎么办常见修复策略避免策略哲学家就餐问题多种解决方案检测与恢复策略死锁检测算法死锁恢复机制鸵鸟策略实际工程...
  • 问题: 事务开启 1、在事物中新增一条记录A 并获取到A的自增长ID 为A_ID 2、获取到A_ID并根据它更新当前记录的某些字段 ...在多线程的情况下,出现数据库死锁现象,求大神指教 如何解决这问题
  • 数据库死锁

    千次阅读 2015-11-02 14:08:10
    需求:客户下单,根据客户对应的销售(销售字段userid),进行金额和客户数统计,数据库表内一天一个销售只能有一条记录,第一次下单插入数据,以后每次下单对金额进行加加减减后更新。数据库表, 原数据库表居然有...
  • 数据库死锁

    2009-06-02 10:54:00
    什么是数据库死锁 每个使用关系型数据库的程序都可能遇到数据死锁的情况。理解什么是死锁之前先要了解锁定的概念:如果需要“修改”一条数据,首先数据库管理系统会在上面加锁,以保证在同一时间只有一个事务能进行...
  • 多线程死锁什么是多线程死锁代码如下功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、...数据库死锁,线程死...
  • java多线程死锁

    2017-08-24 15:53:01
    前言 死锁单独写一篇文章是因为这是一个很严重的、必须要引起重视的问题。这不是夸大死锁的风险,尽管锁被持有的时间通常很短,但是作为商业产品的应用程序每天...一个经典的多线程问题。 当一个线程永远地持有
  • 如何避免多线程死锁?

    千次阅读 2017-09-08 23:23:09
    一个经典的多线程问题。 当一个线程永远地持有一个锁,并且其他线程都尝试去获得这个锁时,那么它们将永远被阻塞,这个我们都知道。如果线程A持有锁L并且想获得锁M,线程B持有锁M并且想获得锁L,那么这两个线程将...
  • java中的死锁和数据库死锁

    千次阅读 2010-08-05 21:51:00
    一般来说,每一种使用线程的语言中都存在线程死锁问题,Java开发中遇到线程死锁问题也是非常普遍。笔者在程序开发中就常常碰到死锁的问题,并经常...多线程允许在程 序中并发执行多个指令流,每个指令流都称为
  • Sybase数据库死锁对策

    千次阅读 2014-08-14 14:55:16
    《Sybase数据库在UNIX、Windows上的实施和...死锁的发生对系统的性能和吞吐量都有重要影响,经检测发现,管理信息系统的死锁主要是因为两个或线程(登录)抢占同一表数据资源。引起长时间抢占同一资源不是因为我
  • 一种情形,此时执行程序中两个或线程发生永久堵塞(等待),每个线程都在等待被其他线程占用并堵塞了的资源。例如,如果线程A锁住了记录1并等待记录2,而线程B锁住了记录2并等待记录1,这样两个线程就发生了...
  • 数据库死锁,导致其他更新操作无法进行 此时需要查询出该死锁的进程,将其杀死
  • 文章目录引言线程饥饿死锁资源死锁Timer 定时器死锁分析 引言 线程饥饿死锁 《Java并发编程实践》中对线程饥饿死锁的解释是这样的:在使用线程池执行任务时,如果任务依赖于其他任务,那么就可能产生死锁问题。在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 75,536
精华内容 30,214
关键字:

多线程数据库死锁