精华内容
下载资源
问答
  • MySQL在进行alter table等DDL操作时,有时会出现Waiting for table metadata lock的等待场景。而且,一旦alter table TableA的操作停滞在Waiting for table metadata lock的状态,后续对TableA的任何操作(包括读)...
  • about to fork child process, waiting until server is ready for connections. forked process: 3560 ERROR: child process failed, exited with error number 1 To see additional information in this output, ...
  • 很多人在查看SQL语句等待的时候都是通过sys.dm_exec_requests查看,等待类型也是通过wait_type得出,sys.dm_os_waiting_tasks也可以看到session的等待那么有什么区别呢....,这篇文章给大家介绍SqlServer应用之sys.dm_...
  • 解决Another app is currently holding the yum lock; waiting for it to exit.
  • Pinax等候名单 目录 关于Pinax Pinax是建立在Django Web... 对于已经设置了一个彻头彻尾的现成Django项目pinax-waitinglist和引导模板,看到Pinax waitinglist启动项目。 支持的Django和Python版本 Django的/ Python的
  • Waiting 一个移动端loading UI组件
  • 您可以使用bower bower install bootstrap-waitingfor来安装此模块,并包括来自build目录的文件。 与npm 您可以使用npm npm install --save bootstrap-waitingfor安装此模块,并如下所示包含它: const ...
  • 今天做项目的时候每次修改代码保存后都会跳出一个框框,然后就有两个进度条,上面写the user operation is wating...小编去网上查了查解决了这个问题,下面跟大家分享一下。
  • 排队网络模型中的Jackson开网模型,开网即系统中的顾客与外部有交流。
  • 前端项目-bootstrap-waitingfor,Waiting for dialog with progress bar for Bootstrap
  • 1 Timed Waiting(计时等待) 1.1 状态描述 1.2 代码示例 1.3 线程状态图 2 Blocked(锁阻塞) 2.1 状态描述 2.2 线程状态图 3 Waiting(无限等待) 3.1 状态描述 3.2 代码示例 3.3 线程状态图 4 线程状态总览图

    1 Timed Waiting(计时等待)

    1.1 状态描述

    一个正在限时等待另一个线程执行一个唤醒动作的线程处于这一状态。

    例如在卖票的程序中,为了避免线程执行过快、现象不明显等问题,在run方法中添加了sleep语句,即进入Timed Waiting状态,这样就会强制当前正在执行的线程休眠,以“减慢线程”。

    1.2 代码示例

    实现一个计数器,计数到100,在每个数字之间暂停1秒,每隔10个数字输出一个字符串。

    public class MyThread extends Thread {
        @Override
        public void run() {
            for(int i=0;i<100;i++) {
                if(i % 10 == 0) {
                    System.out.println("--------" + i);
                }
                System.out.println(i);
                try {
                    Thread.sleep(1000);
                    System.out.println("线程休眠1秒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        public static void main(String[] args) {
            new MyThread().start();
        }
    }
    
    --------0
    0
    线程休眠11
    线程休眠12
    ...
    9
    线程休眠1--------10
    10
    线程休眠1

    总结

    1. 进入Timed Waiting状态的一种常见情形是调用Thread.sleep()方法;
    2. 单独线程也可以调用,不一定非要有协作关系;
    3. sleep()与锁无关,线程睡眠到期自动苏醒,并返回Runnable(可运行)状态;
    4. sleep()中指定的时间是线程不会运行的最短时间,不能保证线程睡眠到期后就开始立刻执行。

    1.3 线程状态图

    在这里插入图片描述

    2 Blocked(锁阻塞)

    2.1 状态描述

    一个正在阻塞等待一个锁对象的线程处于这一状态。

    例如:线程A与线程B代码中使用同一锁,如果线程A获取锁,线程A进入Runnable状态,那么线程B就进入到Blocked锁阻塞状态。

    除了由Runnable状态进入Blocked状态以外,Waiting及Time Waiting也会在某种情况下进入Blocked状态。

    2.2 线程状态图

    在这里插入图片描述

    3 Waiting(无限等待)

    3.1 状态描述

    一个正在无限期等待另一个线程执行一个唤醒动作的线程处于这一状态。

    3.2 代码示例

    public class WaitingTest {
        public static Object obj = new Object();
    
        public static void main(String[] args) {
            // 演示waiting
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while(true) {
                        synchronized (obj) {
                            try {
                                System.out.println(Thread.currentThread().getName() +
                                        "获取到锁对象,调用wait方法,进入waiting状态," +"释放锁对象");
                                obj.wait(); // 无限等待
                                //obj.wait(5000);// 计时等待,5秒时间到,自动醒来
                            } catch(InterruptedException e) {
                                e.printStackTrace();
                            }
                            System.out.println(Thread.currentThread().getName() +
                                    "从waiting状态醒来,获取到锁对象,继续执行了");
                        }
                    }
                }
            },"等待线程").start();
    
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 每隔3秒,唤醒一次
                    try {
                        System.out.println(Thread.currentThread().getName() + "等待3秒钟");
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
    
                    synchronized (obj) {
                        System.out.println(Thread.currentThread().getName() +
                                "获取到锁对象,调用notify方法,释放锁对象");
                        obj.notify();
                    }
                }
            },"唤醒线程").start();
        }
    }
    
    等待线程获取到锁对象,调用wait方法,进入waiting状态,释放锁对象
    唤醒线程等待3秒钟
    唤醒线程获取到锁对象,调用notify方法,释放锁对象
    等待线程从waiting状态醒来,获取到锁对象,继续执行了
    等待线程获取到锁对象,调用wait方法,进入waiting状态,释放锁对象
    

    总结

    1. 一个调用了某个对象的wait()方法的线程会等待另一个线程调用此对象的notify()方法或notifyAll()方法;
    2. 多线程协作时,如果A线程在Runnable(可运行)状态中调用了wait()方法,那么A就进入了Waiting(无限等待)状态,同时失去了同步锁;如果B线程获取到同步锁,并在运行中调用了notify()方法,就会将无限等待的A线程唤醒;如果A线程唤醒后获取到锁对象,就进入Runnable(可运行)状态,如果没有获取锁对象,就进入到Blocked(锁阻塞)状态。

    3.3 线程状态图

    在这里插入图片描述

    4 线程状态总览图

    在这里插入图片描述

    展开全文
  • NULL 博文链接:https://zhangyou1010.iteye.com/blog/1436632
  • 使用ssh连接centos服务器,用着c01dkit/pwndocker的pwndocker,开了tmux的情况下,使用context.terminal = ['tmux','splitw','-h']和gdb.attach(xxxx)出现Waiting for debugger,然后无限等待,即便gdb正常attach到...

    本文网址为https://blog.csdn.net/weixin_43483799/article/details/118885208,转载请保留此信息,以确保读者可以追踪本文在未来的更新信息。

    前言

    请先阅读pwntools官方issues,确保其他人的回答仍无法解决问题:
    docker gdb attach #1140

    长文不看版:

    首先使用pwn version确认你的pwntools版本。如果是4.6.0及之前的版本,都存在这个问题。若如此,找个目录下载一下最新的版本:git clone --depth 1 git://github.com/Gallopsled/pwntools,然后不需要cd切换目录,直接使用pip install --upgrade --editable ./pwntools。这条语句会自动卸载当前pwntools并安装开发版本。Waiting for debugger无限等待的问题在这篇博客发表4天前(2021-7-15)刚被修复……

    背景

    使用ssh连接centos服务器,用着c01dkit/pwndocker的pwndocker,开了tmux的情况下,使用context.terminal = ['tmux','splitw','-h']gdb.attach(xxxx)出现Waiting for debugger,然后无限等待,即便gdb正常attach到了程序上。截图如下:
    在这里插入图片描述
    正常情况下,如果debugger在15s内没有return的话程序会报一个failure。但是这里无论等多久都没有返回。

    解决思路

    调试发现,这是由于/usr/local/lib/python3.6/dist-packages/pwnlib/util/proc.py的 wait_for_debugger函数在

    while t.timeout and tracer(pid) is None:
    

    这个循环中无限执行了。通过在这个while外面加上调试语句可以发现,这里源码的问题在于countdown不会按照预想的方式进行倒计时,所以造成无限循环。
    在这里插入图片描述
    有兴趣可以阅读一下/usr/local/lib/python3.6/dist-packages/pwnlib/timeout.py对于倒计时的实现,此处就不从改timeout下手了(因为要解决的不是无限循环,是解决造成无限循环的原因)

    tracer

    进一步调试(在控制台运行一个程序,ps -aux查看其pid,并使用pwntools的pwnlib.util下proc.py的tracer函数手动查看pid的信息)发现tracer函数存在问题。在背景提到的环境下,该函数的返回值一直都是None。该函数实现如下(在/usr/local/lib/python3.6/dist-packages/pwnlib/util/proc.py中):
    在这里插入图片描述

    status

    其调用的status也在这个文件里,实现方式如下:
    在这里插入图片描述
    可见它是去/proc/<pid>/status查看具体信息,然后把它做成字典并返回。然后tracer函数从字典中提取TracerPid字段,以查看该二进制程序到底有没有被gdb追踪到。这里举一个/proc/14792/status文件的例子(是buuctf的一道题)。注意status文件里的TracerPid字段是0,因为没有开启gdb调试。

    #运行程序后,先ps -aux一下
    root@pwn:/usr/local/lib/python3.6/dist-packages/pwnlib/util# ps -aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0  21764  2000 pts/0    Ss+  Jul17   0:00 bash
    root     13799  0.0  0.0  21884  2268 pts/1    Ss+  02:39   0:00 /bin/bash
    root     14375  0.0  0.0  21888  2312 pts/2    Ss   13:39   0:00 /bin/bash
    root     14390  0.0  0.0  21888  2320 pts/3    Ss   13:39   0:00 /bin/bash
    root     14792  0.0  0.0   4504   384 pts/3    S+   15:51   0:00 ./bjdctf_2020_babystack
    root     14795  0.1  0.0  21896  2212 pts/4    Ss+  15:52   0:00 /bin/bash
    root     14811  0.0  0.0  37660  1572 pts/2    R+   15:53   0:00 ps -aux
    # 然后查看该文件的status
    root@pwn:/usr/local/lib/python3.6/dist-packages/pwnlib/util# cat /proc/14792/status
    Name:   bjdctf_2020_bab
    Umask:  0022
    State:  S (sleeping)
    Tgid:   14792
    Ngid:   0
    Pid:    14792
    PPid:   14390
    TracerPid:      0
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    FDSize: 256
    Groups:
    VmPeak:     4504 kB
    VmSize:     4504 kB
    VmLck:         0 kB
    VmPin:         0 kB
    VmHWM:       384 kB
    VmRSS:       384 kB
    RssAnon:              64 kB
    RssFile:             320 kB
    RssShmem:              0 kB
    VmData:      168 kB
    VmStk:       132 kB
    VmExe:         4 kB
    VmLib:      2112 kB
    VmPTE:        36 kB
    VmSwap:        0 kB
    Threads:        1
    SigQ:   0/127718
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000000000000
    SigCgt: 0000000000000000
    CapInh: 0000001fffffffff
    CapPrm: 0000001fffffffff
    CapEff: 0000001fffffffff
    CapBnd: 0000001fffffffff
    CapAmb: 0000000000000000
    NoNewPrivs:     0
    Seccomp:        0
    Speculation_Store_Bypass:       thread vulnerable
    Cpus_allowed:   00ffffff
    Cpus_allowed_list:      0-23
    Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000003
    Mems_allowed_list:      0-1
    voluntary_ctxt_switches:        1
    nonvoluntary_ctxt_switches:     1
    

    然后再开一个窗口,使用gdb -p 14792来追踪这个程序,结果如下:

    #这里已经attach了一个gdb,看一下程序运行情况
    root@pwn:/usr/local/lib/python3.6/dist-packages/pwnlib/util# ps -aux
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root         1  0.0  0.0  21764  2000 pts/0    Ss+  Jul17   0:00 bash
    root     13799  0.0  0.0  21884  2268 pts/1    Ss+  02:39   0:00 /bin/bash
    root     14375  0.0  0.0  21888  2312 pts/2    Ss   13:39   0:00 /bin/bash
    root     14390  0.0  0.0  21888  2320 pts/3    Ss   13:39   0:00 /bin/bash
    root     14792  0.0  0.0   4504   400 pts/3    t+   15:51   0:00 ./bjdctf_2020_babystack
    root     14795  0.0  0.0  21896  2212 pts/4    Ss   15:52   0:00 /bin/bash
    root     14812  7.5  0.1 180148 65140 pts/4    S+   15:53   0:01 gdb -p 14792
    root     14819  0.0  0.0  37660  1568 pts/2    R+   15:53   0:00 ps -aux
    #然后再看一下status
    root@pwn:/usr/local/lib/python3.6/dist-packages/pwnlib/util# cat /proc/14792/status
    Name:   bjdctf_2020_bab
    Umask:  0022
    State:  t (tracing stop)
    Tgid:   14792
    Ngid:   0
    Pid:    14792
    PPid:   14390
    TracerPid:      14812  #这里已经更新,变成了gdb的pid。
    Uid:    0       0       0       0
    Gid:    0       0       0       0
    FDSize: 256
    Groups:
    VmPeak:     4504 kB
    VmSize:     4504 kB
    VmLck:         0 kB
    VmPin:         0 kB
    VmHWM:       400 kB
    VmRSS:       400 kB
    RssAnon:              64 kB
    RssFile:             336 kB
    RssShmem:              0 kB
    VmData:      168 kB
    VmStk:       132 kB
    VmExe:         4 kB
    VmLib:      2112 kB
    VmPTE:        36 kB
    VmSwap:        0 kB
    Threads:        1
    SigQ:   0/127718
    SigPnd: 0000000000000000
    ShdPnd: 0000000000000000
    SigBlk: 0000000000000000
    SigIgn: 0000000000000000
    SigCgt: 0000000000000000
    CapInh: 0000001fffffffff
    CapPrm: 0000001fffffffff
    CapEff: 0000001fffffffff
    CapBnd: 0000001fffffffff
    CapAmb: 0000000000000000
    NoNewPrivs:     0
    Seccomp:        0
    Speculation_Store_Bypass:       thread vulnerable
    Cpus_allowed:   00ffffff
    Cpus_allowed_list:      0-23
    Mems_allowed:   00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000003
    Mems_allowed_list:      0-1
    voluntary_ctxt_switches:        2
    nonvoluntary_ctxt_switches:     1
    

    所以tracer函数的本质就是从系统文件中读取信息,看看哪个程序正在追踪这个程序。然后经过在python文件中添加调试语句并使用相同的方法进行查看,发现确实可以获得tracer pid的。这里的wait_for_debugger函数使用了0.1s一次的检查。(本来以为是这种方法拿不到tracer pid,后来发现确实可以嗷。运行冲程序的前几次是None,之后就是正常的pid了。)

    真正原因

    仔细观察程序,发现是程序逻辑设计有问题……即便是拿到了正确的pid,wait_for_debugger也会进入死循环。超时退出还好,也不知道为什么超时坏了退出不了(hhh)

    简单来说while debugger_pid:这个循环是无法退出的,因为一切正常时debugger_pid = 0不会执行,所以这个就死循环了啊喂!

    #/usr/local/lib/python3.6/dist-packages/pwnlib/util/proc.py
    
    def wait_for_debugger(pid, debugger_pid=None):
        """wait_for_debugger(pid, debugger_pid=None) -> None
    
        Sleeps until the process with PID `pid` is being traced.
        If debugger_pid is set and debugger exits, raises an error.
    
        Arguments:
            pid (int): PID of the process.
    
        Returns:
            None
        """
        t = Timeout()
        with t.countdown(timeout=15):
            with log.waitfor('Waiting for debugger') as l:
                while debugger_pid:
                    debugger = psutil.Process(debugger_pid)
                    while t.timeout and tracer(pid) is None:
                        try:
                            debugger.wait(0.01)
                        except psutil.TimeoutExpired:
                            pass
                        else:
                            debugger_pid = 0
                            break
                else:
                    while t.timeout and tracer(pid) is None:
                        time.sleep(0.01)
    
            if tracer(pid):
                l.success()
            elif debugger_pid == 0:
                l.failure("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
            else:
                l.failure('Debugger did not attach to pid %d within 15 seconds', pid)
    

    绕了半天,最后终于发现了死循环的原因。

    解决方案

    那么稍微修改一下源码即可。即找到了tracer就不用在while了,直接退出循环就完事了~
    vim /usr/local/lib/python3.6/dist-packages/pwnlib/util/proc.py,然后把wait_for_debugger函数换成下面这个。(其实只改动了一小点,所以手动修改两三行就行)

    #/usr/local/lib/python3.6/dist-packages/pwnlib/util/proc.py
    
    def wait_for_debugger(pid, debugger_pid=None):
        """wait_for_debugger(pid, debugger_pid=None) -> None
    
        Sleeps until the process with PID `pid` is being traced.
        If debugger_pid is set and debugger exits, raises an error.
    
        Arguments:
            pid (int): PID of the process.
    
        Returns:
            None
        """
        t = Timeout()
        with t.countdown(timeout=15):
            with log.waitfor('Waiting for debugger') as l:
                while debugger_pid:
                    debugger = psutil.Process(debugger_pid)
                    #while t.timeout and tracer(pid) is None:
                    if tracer(pid) is not None: break
                    try:
                        debugger.wait(0.01)
                    except psutil.TimeoutExpired:
                        pass
                    else:
                        debugger_pid = 0
                else:
                    while t.timeout and tracer(pid) is None:
                        time.sleep(0.01)
    
            if tracer(pid):
                l.success()
            elif debugger_pid == 0:
                l.failure("debugger exited! (maybe check /proc/sys/kernel/yama/ptrace_scope)")
            else:
                l.failure('Debugger did not attach to pid %d within 15 seconds', pid)
    

    最终效果

    如下图,waiting for debugger的死循环解决了!
    在这里插入图片描述

    总结

    dl们写的代码也会存在问题……不要盲目搞崇拜呀。本来打算提交一个issue,但是发现github上不了了,就改天再说吧。

    2021-7-19日补充:今天上了github,发现这个bug四天前被修复过了……好吧T_T

    展开全文
  • Java 线程状态---WAITING(部分转载)

    万次阅读 2019-08-01 20:16:02
    看到一篇关于写线程waiting状态的文章,感觉很生动有趣,转过来保存下。 定义 一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态。 A thread that is waiting indefinitely for another ...

    看到一篇关于写线程waiting状态的文章,感觉很生动有趣,转过来保存下。

    总结:

    waiting这个状态,就是等待,明确了等待,就不会抢资源了。

    一个线程A在拿到锁但不满足执行条件的时候,需要另一个线程B去满足这个条件,
    那么线程A就会释放锁并处于waiting的状态,等线程B执行完再执行。
    waiting状态的好处是:此状态的线程不再活动,不再参与调度,因此不会浪费 CPU 资源,也不会去竞争锁了,相比暴力的blocking状态,要优雅很多。

    进入waiting的方法:

    调用wait方法,就能让线程进入waiting状态,notify/notify能让线程结束waiting状态。
    join也能让线程进入waiting状态,但也算是特殊的wait

    还有个Time_waiting的状态

    就是wait方法加了个时间的入参,代表等多久就不等了,避免永远等下去。

    blocking和waiting

    blocking状态,硬说也算waiting的特殊情况
    如果waiting状态的条件是有没有锁,那就可以勉强理解为blocking状态吧。
    blocking是线程被动阻塞,waiting是线程主动阻塞,本质上其实是一样的。

    下文这个火车上抢厕所的例子,很形象。

    以下是转载正文(末尾附原文链接):

    image

    定义

    一个正在无限期等待另一个线程执行一个特别的动作的线程处于这一状态。

    A thread that is waiting indefinitely for another thread to perform a particular action is in this state.

    然而这里并没有详细说明这个“特别的动作”到底是什么,详细定义还是看 javadoc(jdk8):

    一个线程进入 WAITING 状态是因为调用了以下方法:

    • 不带时限的 Object.wait 方法
    • 不带时限的 Thread.join 方法
    • LockSupport.park

    然后会等其它线程执行一个特别的动作,比如:

    • 一个调用了某个对象的 Object.wait 方法的线程会等待另一个线程调用此对象的 Object.notify() 或 Object.notifyAll()。
    • 一个调用了 Thread.join 方法的线程会等待指定的线程结束。

       

    对应的英文原文如下:

    A thread is in the waiting state due to calling one of the following methods:

    • Object.wait with no timeout
    • Thread.join with no timeout
    • LockSupport.park

    A thread in the waiting state is waiting for another thread to perform a particular action. For example, a thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. A thread that has called Thread.join() is waiting for a specified thread to terminate.

    线程间的协作(cooperate)机制

    显然,WAITING 状态所涉及的不是一个线程的独角戏,相反,它涉及多个线程,具体地讲,这是多个线程间的一种协作机制。谈到线程我们经常想到的是线程间的竞争(race),比如去争夺锁,但这并不是故事的全部,线程间也会有协作机制。

    就好比在公司里你和你的同事们,你们可能存在在晋升时的竞争,但更多时候你们更多是一起合作以完成某些任务。

    wait/notify 就是线程间的一种协作机制,那么首先,为什么 wait?什么时候 wait?它为什么要等其它线程执行“特别的动作”?它到底解决了什么问题?

    wait 的场景

    首先,为什么要 wait 呢?简单讲,是因为条件(condition)不满足。那么什么是条件呢?为方便理解,我们设想一个场景:

    有一节列车车厢,有很多乘客,每个乘客相当于一个线程;里面有个厕所,这是一个公共资源,且一次只允许一个线程进去访问(毕竟没人希望在上厕所期间还与他人共享~)。

    image

    竞争关系

    假如有多个乘客想同时上厕所,那么这里首先存在的是竞争的关系。

    如果将厕所视为一个对象,它有一把锁,想上厕所的乘客线程需要先获取到锁,然后才能进入厕所。

    image

    Java 在语言级直接提供了同步的机制,也即是 synchronized 关键字:

    synchronized(expression) {……}

    它的机制是这样的:对表达式(expresssion)求值(值的类型须是引用类型(reference type)),获取它所代表的对象,然后尝试获取这个对象的锁:

    • 如果能获取锁,则进入同步块执行,执行完后退出同步块,并归还对象的锁(异常退出也会归还);
    • 如果不能获取锁,则阻塞在这里,直到能够获取锁。

    在一个线程还在厕所期间,其它同时想上厕所的线程被阻塞,处在该厕所对象的 entry set 中,处于 BLOCKED 状态。

    image

    完事之后,退出厕所,归还锁。

    image

    之后,系统再在 entry set 中挑选一个线程,将锁给到它。

    image

    对于以上过程,以下为一个 gif 动图演示:

    当然,这就是我们所熟悉的锁的竞争过程。以下为演示的代码:

    @Test
    public void testBlockedState() throws Exception {
    	class Toilet { // 厕所类
    		public void pee() { // 尿尿方法
    			try {
    				Thread.sleep(21000);// 研究表明,动物无论大小尿尿时间都在21秒左右
    			} catch (InterruptedException e) {
    				Thread.currentThread().interrupt();
    			}
    		}
    	}
    
    Toilet toilet = <span class="hljs-keyword">new</span> Toilet();
    
    Thread passenger1 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
    	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
    		<span class="hljs-keyword">synchronized</span> (toilet) {
    			toilet.pee();
    		}
    	}
    });
    
    Thread passenger2 = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
    	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
    		<span class="hljs-keyword">synchronized</span> (toilet) {
    			toilet.pee();
    		}
    	}
    });
    
    passenger1.start();
    
    <span class="hljs-comment">// 确保乘客1先启动</span>
    Thread.sleep(<span class="hljs-number">100</span>);
    
    passenger2.start();
    
    <span class="hljs-comment">// 确保已经执行了 run 方法</span>
    Thread.sleep(<span class="hljs-number">100</span>);
    
    <span class="hljs-comment">// 在乘客1在厕所期间,乘客2处于 BLOCKED 状态</span>
    assertThat(passenger2.getState()).isEqualTo(Thread.State.BLOCKED);
    

    }

    条件

    现在,假设有个女乘客,她抢到了锁,进去之后裤子脱了一半,发现马桶的垫圈纸没了,于是拒绝尿。

    或许是因为她比较讲究卫生,怕直接坐上去会弄脏她白花花的屁股~

    现在,条件出现了:有纸没纸,这就是某种条件。

    image

    那么,现在条件不满足,这位女线程改怎么办呢?如果只是在里面干等,显然是不行的。

    这不就是人民群众所深恶痛绝的“占着茅坑不拉尿”吗?

    • 一方面,外面 entry set 中可能好多群众还嗷嗷待尿呢(其中可能有很多大老爷线程,他们才不在乎有没有马桶垫圈纸~)
    • 另一方面,假定外面同时有“乘务员线程”,准备进去增加垫圈纸,可你在里面霸占着不出来,别人也没法进去,也就没法加纸。

    所以,当条件不满足时,需要出来,要把锁还回去,以使得诸如“乘务员线程”的能进去增加纸张。

    等待是必要的吗?

    那么出来之后是否一定需要等待呢?当然也未必。

    这里所谓“等待”,指的是使线程处于不再活动的状态,即是从调度队列中剔除。

    如果不等待,只是简单归还锁,用一个反复的循环来判断条件是否满足,那么还是可以再次回到调度队列,然后期待在下一次被调度到的时候,可能条件已经发生变化:

    比如某个“乘务员线程”已经在之前被调度并增加了里面的垫圈纸。自然,也可能再次调度到的时候,条件依旧是不满足的。

    现在让我们考虑一种比较极端的情况:厕所外一大堆的“女乘客线程”想进去方便,同时还有一个焦急的“乘务员线程”想进去增加厕纸。

    image

    如果线程都不等待,而厕所又是一个公共资源,无法并发访问。调度器每次挑一个线程进去,挑中“乘务员线程”的几率反而降低了,entry set 中很可能越聚越多无法完成方便的“女乘客线程”,“乘务员线程”被选中执行的几率越发下降。

    当然,同步机制会防止产生所谓的“饥饿(starvation)”现象,“乘务员线程”最终还是有机会执行的,只是系统运行的效率下降了。

    所以,这会干扰正常工作的线程,挤占了资源,反而影响了自身条件的满足。另外,“乘务员线程”可能这段时间根本没有启动,此时,不愿等待的“女乘客线程”不过是徒劳地进进出出,占用了 CPU 资源却没有办成正事。

    效果上还是在这种没有进展的进进出出中等待,这种情形类似于所谓的忙等待 (busy waiting)

    协作关系

    综上,等待还是有必要的,我们需要一种更高效的机制,也即是 wait/notify 的协作机制。

    当条件不满足时,应该调用 wait()方法,这时线程释放锁,并进入所谓的 wait set 中,具体的讲,是进入这个厕所对象的 wait set 中:

    image

    这时,线程不再活动,不再参与调度,因此不会浪费 CPU 资源,也不会去竞争锁了,这时的线程状态即是 WAITING。

    现在的问题是:她们什么时候才能再次活动呢?显然,最佳的时机是当条件满足的时候。

    之后,“乘务员线程”进去增加厕纸,当然,此时,它也不能只是简单加完厕纸就完了,它还要执行一个特别的动作,也即是“通知(notify)”在这个对象上等待的女乘客线程:

    大概就是向她们喊一声:“有纸啦!赶紧去尿吧!”显然,如果只是“女乘客线程”方面一厢情愿地等待,她们将没有机会再执行。

    所谓“通知”,也即是把她们从 wait set 中释放出来,重新进入到调度队列(ready queue)中。

    • 如果是 notify,则选取所通知对象的 wait set 中的一个线程释放;
    • 如果是 notifyAll,则释放所通知对象的 wait set 上的全部线程。

    整个过程如下图所示:

    image

    对于上述过程,我们也给出以下 gif 动图演示:

    注意:哪怕只通知了一个等待的线程,被通知线程也不能立即恢复执行,因为她当初中断的地方是在同步块内,而此刻她已经不持有锁,所以她需要再次尝试去获取锁(很可能面临其它线程的竞争),成功后才能在当初调用 wait 方法之后的地方恢复执行。(这也即是所谓的 “reenter after calling Object.wait”,在上一个篇章中也曾详细的讨论了这一过程。)

    • 如果能获取锁,线程就从 WAITING 状态变成 RUNNABLE 状态;
    • 否则,从 wait set 出来,又进入 entry set,线程就从 WAITING 状态又变成 BLOCKED 状态。

    综上,这是一个协作机制,“女乘客线程”和“乘务员线程”间存在一个协作关系。显然,这种协作关系的存在,“女乘客线程”可以避免在条件不满足时的盲目尝试,也为“乘务员线程”的顺利执行腾出了资源;同时,在条件满足时,又能及时得到通知。协作关系的存在使得彼此都能受益。

    生产者与消费者问题

    不难发现,以上实质上也就是经典的“生产者与消费者”的问题:

    乘务员线程生产厕纸,女乘客线程消费厕纸。当厕纸没有时(条件不满足),女乘客线程等待,乘务员线程添加厕纸(使条件满足),并通知女乘客线程(解除她们的等待状态)。接下来,女乘客线程能否进一步执行则取决于锁的获取情况。

    代码的演示:

    在以下代码中,演示了上述的 wait/notify 的过程:

    @Test
    public void testWaitingState() throws Exception {
    
    <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Toilet</span> </span>{ <span class="hljs-comment">// 厕所类</span>
    	<span class="hljs-keyword">int</span> paperCount = <span class="hljs-number">0</span>; <span class="hljs-comment">// 纸张</span>
    
    	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pee</span><span class="hljs-params">()</span> </span>{ <span class="hljs-comment">// 尿尿方法</span>
    		<span class="hljs-keyword">try</span> {
    			Thread.sleep(<span class="hljs-number">21000</span>);<span class="hljs-comment">// 研究表明,动物无论大小尿尿时间都在21秒左右</span>
    		} <span class="hljs-keyword">catch</span> (InterruptedException e) {
    			Thread.currentThread().interrupt();
    		}
    	}
    }
    
    Toilet toilet = <span class="hljs-keyword">new</span> Toilet();
    
    <span class="hljs-comment">// 两乘客线程</span>
    Thread[] passengers = <span class="hljs-keyword">new</span> Thread[<span class="hljs-number">2</span>];
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; passengers.length; i++) {
    	passengers[i] = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
    		<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
    			<span class="hljs-keyword">synchronized</span> (toilet) {
    				<span class="hljs-keyword">while</span> (toilet.paperCount &lt; <span class="hljs-number">1</span>) {
    					<span class="hljs-keyword">try</span> {
    						toilet.wait(); <span class="hljs-comment">// 条件不满足,等待</span>
    					} <span class="hljs-keyword">catch</span> (InterruptedException e) {
    						Thread.currentThread().interrupt();
    					}
    				}
    				toilet.paperCount--; <span class="hljs-comment">// 使用一张纸</span>
    				toilet.pee();
    			}
    		}
    	});
    }
    
    <span class="hljs-comment">// 乘务员线程</span>
    Thread steward = <span class="hljs-keyword">new</span> Thread(<span class="hljs-keyword">new</span> Runnable() {
    	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{
    		<span class="hljs-keyword">synchronized</span> (toilet) {
    			toilet.paperCount += <span class="hljs-number">10</span>;<span class="hljs-comment">// 增加十张纸</span>
    			toilet.notifyAll();<span class="hljs-comment">// 通知所有在此对象上等待的线程</span>
    		}
    	}
    });
    
    passengers[<span class="hljs-number">0</span>].start();
    passengers[<span class="hljs-number">1</span>].start();
    
    <span class="hljs-comment">// 确保已经执行了 run 方法</span>
    Thread.sleep(<span class="hljs-number">100</span>);
    
    <span class="hljs-comment">// 没有纸,两线程均进入等待状态</span>
    assertThat(passengers[<span class="hljs-number">0</span>].getState()).isEqualTo(Thread.State.WAITING);
    assertThat(passengers[<span class="hljs-number">1</span>].getState()).isEqualTo(Thread.State.WAITING);
    
    <span class="hljs-comment">// 乘务员线程启动,救星来了</span>
    steward.start();
    
    <span class="hljs-comment">// 确保已经增加纸张并已通知</span>
    Thread.sleep(<span class="hljs-number">100</span>);
    
    <span class="hljs-comment">// 其中之一会得到锁,并执行 pee,但无法确定是哪个,所以用 "或 ||"</span>
    <span class="hljs-comment">// 注:因为 pee 方法中实际调用是 sleep, 所以很快就从 RUNNABLE 转入 TIMED_WAITING(sleep 时对应的状态)</span>
    assertTrue(Thread.State.TIMED_WAITING.equals(passengers[<span class="hljs-number">0</span>].getState())
    		|| Thread.State.TIMED_WAITING.equals(passengers[<span class="hljs-number">1</span>].getState()));
    
    <span class="hljs-comment">// 其中之一则被阻塞,但无法确定是哪个,所以用 "或 ||"</span>
    assertTrue(
    		Thread.State.BLOCKED.equals(passengers[<span class="hljs-number">0</span>].getState()) || Thread.State.BLOCKED.equals(passengers[<span class="hljs-number">1</span>].getState()));
    

    }

    join 的场景及其它

    从定义中可知,除了 wait/notify 外,调用 join 方法也会让线程处于 WAITING 状态。

    join 的机制中并没有显式的 wait/notify 的调用,但可以视作是一种特殊的,隐式的 wait/notify 机制。

    假如有 a,b 两个线程,在 a 线程中执行 b.join(),相当于让 a 去等待 b,此时 a 停止执行,等 b 执行完了,系统内部会隐式地通知 a,使 a 解除等待状态,恢复执行。

    换言之,a 等待的条件是 “b 执行完毕”,b 完成后,系统会自动通知 a。

    关于 LockSupport.park 的情况则由读者自行分析。

    与传统 waiting 状态的关系

    Thread.State.WAITING 状态与传统的 waiting 状态类似:

    image

    本文后面部分转载自:https://my.oschina.net/goldenshaw/blog/802620

    如有侵权,请告知删除

    展开全文
  • BLOCKED,WAITING,TIMED_WAITING有什么区别?

    千次阅读 2019-10-10 20:31:02
    BLOCKED,WAITING和TIMED_WAITING是很重要的线程状态,但是经常对我们造成困扰。如果需要分析线程dump必须要对其有一定的理解。使用生活的例子,本文将每个状态变成了简单的例子。 与正式的Java文档定义相比,任何让...

    原文地址:
    https://dzone.com/articles/di...

    BLOCKED,WAITING和TIMED_WAITING是很重要的线程状态,但是经常对我们造成困扰。如果需要分析线程dump必须要对其有一定的理解。使用生活的例子,本文将每个状态变成了简单的例子。

    与正式的Java文档定义相比,任何让人费解的概念都可以用简单的例子来理解。如果用真实生活中的例子,就更好理解了。我想分享一些真实生活的例子来帮助理解这些线程状态。

    http://fastthread.io/assets/i...
    clipboard.png

    图: 采用http://fastthread.io/生成的状态图展示了哪些线程被哪些线程阻塞

    BLOCKED

    Java文档官方定义BLOCKED状态是:“这种状态是指一个阻塞线程在等待monitor锁。”

    真实生活例子:今天你要去面试。这是你梦想的工作,你已经盯着它多年了。你早上起来,准备好,穿上你最好的外衣,对着镜子打理好。当你走进车库发现你的老婆已经把车开走了。在这个场景,你只有一辆车,所以怎么办?在真实生活中,可能会打架:-)。 现在因为你老爸把车开走了你被BLOCKED了。你不能去参加面试。

    这就是BLOCKED状态。用技术术语讲,你是线程T1,你老婆是线程T2而锁是车。T1BLOCKED在锁(例子里的车)上,因为T2已经获取了这个锁。

    小贴士:当线程调用Object#wait()方法进入一个synchronized块/方法或重进入一个synchronized锁/方法时会等待获取monitor锁。

    WAITING

    Java文档官方定义WAITING状态是:“一个线程在等待另一个线程执行一个动作时在这个状态”

    真实生活例子:再看下几分钟后你的老婆开车回家了。现在你意识到快到面试时间了,而开车过去很远。所以你拼命地踩油门。限速120KM/H而你以160KM/H的速度在开。很不幸,一个交警发现你超速了,让你停到路边。现在你进入了WAITING状态。你听下车坐在那等着交警过来检查你并放行。基本上,只有等他让你走,你被卡在WAITING状态了。

    用技术术语来讲,你是线程T1而交警是线程T2。你释放你的锁(例子中你停下了车),并进入WAITING状态,直到警察(例子中T2)让你走,你陷入了WAITING状态。

    小贴士:当线程调用以下方法时会进入WAITING状态:

    1. Object#wait() 而且不加超时参数

    2. Thread#join() 而且不加超时参数

    3. LockSupport#park()

    在对象上的线程调用了Object.wait()会进入WAITING状态,直到另一个线程在这个对象上调用了Object.notify()或Object.notifyAll()方法才能恢复。一个调用了Thread.join()的线程会进入WAITING状态直到一个特定的线程来结束。

    TIMED_WAITING

    Java文档官方定义TIMED_WAITING状态为:“一个线程在一个特定的等待时间内等待另一个线程完成一个动作会在这个状态”

    真实生活例子:尽管充满戏剧性,你在面试中做的非常好,惊艳了所有人并获得了高薪工作。(祝贺你!)你回家告诉你的邻居你的新工作并表达你激动的心情。你的朋友告诉你他也在同一个办公楼里工作。他建议你坐他的车去上班。你想这不错。所以第一天,你走到他的房子。在他的房子前停好你的车。你等了10分钟,但你的邻居没有出现。你继续开自己的车去上班,这样你不会在第一天就迟到。这就是TIMED_WAITING.

    用技术术语来解释,你是线程T1而你的邻居是线程T2。你释放了锁(这里是停止开车)并等了足足10分钟。如果你的邻居T2没有来,你继续开车。

    小贴士:调用了以下方法的线程会进入TIMED_WAITING

    1. Thread#sleep()

    2. Object#wait() 并加了超时参数

    3. Thread#join() 并加了超时参数

    4. LockSupport#parkNanos()

    5. LockSupport#parkUntil()

    结论

    当人们分析thread dump时,理解这些不同的线程状态很关键。
    有多少线程在RUNNABLE,BLOCKED,WAITING和TIMED_WAITING状态?哪一个线程被阻塞了?谁在阻塞别人?哪一个对象被锁了?这些都是很重要的度量分析线程状态的东西。这些线程分析的细节都可以很容易地用线上分析工具http://fastthread.io/完成。

    展开全文
  • Java 线程状态之 TIMED_WAITING

    万次阅读 2019-07-05 16:19:46
    A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state. 更详细的定义还是看 javadoc(jdk8): 带指定的等待时间的等待线程所处的...
  • mysql大量的waiting for table level lock怎么办

    千次阅读 热门讨论 2019-08-03 09:18:02
    row *************************** object_schema: test object_name: t1 waiting_thread_id: 1894 waiting_pid: 1855 waiting_account: root@localhost waiting_lock_type: SHARED_READ waiting_lock_duration: ...
  • centOS安装问题started cancel waiting for multipath siblings of sda。
  • Error: timed out waiting for the condition 查看 kubectl get pod --all-namespaces lynx@n149-136-019:/root$ kubectl get pod --all-namespaces NAMESPACE NAME READY STATUS RESTARTS AGE jhub hook-image-...
  • 转载自面试官让我讲下线程的WAITING状态,我笑了 面试官Q:你讲下线程状态中的WAITING状态,什么时候会处于这个状态?什么时候离开这个状态? 小菜J 会心一笑... 一个正在无限期等待另一个线程执行一个特别的动作...
  • java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000001da495e58> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util...
  • vue源码, npm install, npm run dev, 就这样一直卡着不动了 ``` PS E:\vue\vue-code\vue> npm run dev ...> vue@2.6.10 dev E:\vue\vue-code\vue ...[2019-10-24 16:07:03] waiting for changes... ```
  • 文章目录BLOCKED WAITING TIMED_WAITING代码演示 BLOCKED WAITING TIMED_WAITING代码演示 如下的代码为 演示打印出 BLOCKED WAITING TIMED_WAITING 的示例 . 创建出两个线程. 同时去竞争private synchronized void ...
  • waiting for headers

    千次阅读 2019-07-30 11:32:15
    Debian\Unbutu卡在“waiting for headers”怎么办? 在安装软件的过程中,出现[waiting for headers] ,并且卡住一直没反应。这可能是源的问题,也可能是上一次缓存不完全导致的,以下提供了一些措施来帮你缓解。 ...
  • | system user | Waiting for commit lock | 1 | +--------------+----------------------------------------+----------+ 8 rows in set (0.01 sec) mysql> 有许多类似下面的线程: | 516544 | cacti_user | ...
  • waiting animation

    2012-07-17 02:34:24
    该程序显示动画,类似VISTA鼠标旋转。可以借鉴学习使用
  • jstack dump的线程有一半的线程都在waiting on condition,而且都是一些tomcat的线程池之类的问题线程栈如下: ![图片说明](https://img-ask.csdn.net/upload/201904/19/1555638646_167074.png) 53%的线程都是...
  • 线程的BLOCK、WAITING、TIMED_WAITING状态

    千次阅读 2020-01-13 15:12:29
    提到WAITING状态,顺便提一下TIMED_WAITING状态的场景。 TIMED_WAITING状态 线程处于TIMED_WAITING状态的场景。 调用Thread.sleep方法。 调用Object对象的wait方法,指定超时值。 调用Thread对象的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 286,822
精华内容 114,728
关键字:

waiting