美文网首页
synchronized中耗时操作或者sleep问题

synchronized中耗时操作或者sleep问题

作者: zhengaoly | 来源:发表于2021-12-14 13:56 被阅读0次

    先说结论

    synchronized为非公平锁,耗时代码块在while(true)中,执行完毕以后,大概率下次还会抢占锁,导致其他线程一直拿不到锁,从而阻塞。

    synchronized代码块中,如果有sleep,此时锁并不会被释放,就算本次while循环执行完毕,释放了,由于synchronized是非公平锁,下次大概率还会进入sleep代码块,这样会导致锁被长期占用,从而阻塞其他线程。
    sleep不会释放锁,但会让出cpu,wait会释放锁

    image.png
    如上代码:
    线程1,向容器中写入缓存
    线程2,判断容器中有没有数据,没有数据,sleep 100ms
    有数据处理数据
    

    代码看上去没什么问题。
    但是,由于sleep期间,锁并未释放,导致线程1被阻塞,即容器无法被写入。

    线程2调度分两种情况

    1. 线程调度时,线程2拿到锁,sleep 100ms,此时让出了cpu,但是由于synchronized非公平锁,线程可能会再次调度到线程2,导致线程2while(true)再次拿到锁,接着拿着锁sleep 100ms,如此往复,线程2可能一直拿到锁然后sleep,可达几十次甚至上百次。在此期间,线程1会一直阻塞。
      2.线程2拿到锁,然后sleep 100ms,此时线程2让出cpu,然后切到其他不相关线程如线程3,线程3执行了一段耗时的代码,然后又切回了线程2,然后在执行线程2,在此期间,线程1一直阻塞,因为线程2一直站着锁。

    以上两种情况都是线程2切换期间,都为释放锁导致。
    因此,sleep期间,一定不能拿着锁睡,否则会导致锁长时间被占用,引起其他线程阻塞。
    如果线程需要等待,需要搭配wait,notify将线程暂停,并释放锁
    例如:

    线程2:
    synchronized(queue){
      while(queue.isEmpty()){
      queue.wait();
      }
    
    线程1:
    synchronized(queue){
      queue.add();
      queue.notifyAll();
      }
    

    如下代码演示同步中的耗时代码块问题

    import lombok.extern.slf4j.Slf4j;
    
    import java.util.Queue;
    import java.util.concurrent.ConcurrentLinkedDeque;
    
    public class TestSync {
        public static void main(String[] args) {
            Queue<String> con = new ConcurrentLinkedDeque<>();
            ThreadA threadA = new ThreadA(con);
            ThreadB threadB = new ThreadB(con);
            threadA.setName("threadA");
            threadB.setName("threadB");
            threadB.start();
            threadA.start();
        }
    
    
    }
    
    @Slf4j
    class ThreadA extends Thread {
        Queue<String> con;
    
        ThreadA(Queue<String> p) {
            con = p;
        }
    
        @Override
        public void run() {
            super.run();
            int a = 0;
            while (true) {
                synchronized (con) {
                    log.info("线程A获得锁"+a++);
                    try {
                        //模拟耗时操作
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
    }
    
    @Slf4j
    class ThreadB extends Thread {
        Queue<String> con;
    
        ThreadB(Queue<String> p) {
            con = p;
        }
    
        @Override
        public void run() {
            super.run();
            int b=0;
            while (true) {
                synchronized (con) {
                    log.info("线程B获得锁"+b++);
                }
            }
        }
    }
    

    1.线程A,线程B启动以后竞争con锁
    2.线程A获得锁以后,执行一段耗时的代码(sleep模拟),然后释放锁
    3.线程B获得锁很快执行完成,并释放锁。

    通过jprofiler查看阻塞情况。


    image.png
    image.png

    相关文章

      网友评论

          本文标题:synchronized中耗时操作或者sleep问题

          本文链接:https://www.haomeiwen.com/subject/vbwefrtx.html