深入剖析Sleep、wait、notify、notifyAll、yield、join、Interrupt

sleep原理

sleep是Thread类中的静态方法,首先看下sleep的源码

/**
     * Causes the currently executing thread to sleep (temporarily cease
     * execution) for the specified number of milliseconds, subject to
     * the precision and accuracy of system timers and schedulers. The thread
     * does not lose ownership of any monitors.
     *
     * @param  millis
     *         the length of time to sleep in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public static native void sleep(long millis) throws InterruptedException;
native方法,那么我们应该结合操作系统的原理看下

Unix

Unix使用的是时间片算法, 操作系统按照他们的顺序,给每个进程分配一段时间,即该进程允许运行的时间。如果在 时间片结束时进程还在运行,则CPU将被剥夺并分配给另一个进程。如果进程在时间片结束前阻塞或结束,则CPU当即进行切换。调度程 序所要做的就是维护一张就绪进程列表,当进程用完它的时间片后,它被移到队列的末尾。就这样循环排队出对入队。

windows

windows属于抢占式, 就是说如果一个进程得到了 CPU 时间,除非它自己放弃使用 CPU ,否则将完全霸占 CPU 。因此可以看出,在抢 占式操作系统中,操作系统假设所有的进程都是“人品很好”的,会主动退出 CPU
在抢占式操作系统中,假设有若干进程,操作系统会根据他们的优先级、饥饿时间(已经多长时间没有使用过 CPU 了),给他们算出一 个总的优先级来。操作系统就会把 CPU 交给总优先级最高的这个进程。当进程执行完毕或者自己主动挂起后,操作系统就会重新计算一 次所有进程的总优先级,然后再挑一个优先级最高的把 CPU 控制权交给他
我们用分蛋糕的场景来描述这两种算法。假设有源源不断的蛋糕(源源不断的时间),一副刀叉(一个CPU),10个等待吃蛋糕的人(10 个进程)。
如果是 Unix操作系统来负责分蛋糕,那么他会这样定规矩:每个人上来吃 1 分钟,时间到了换下一个。最后一个人吃完了就再从头开始。于是,不管这10个人是不是优先级不同、饥饿程度不同、饭量不同,每个人上来的时候都可以吃 1 分钟。当然,如果有人本来不太饿,或者饭量小,吃了30秒钟之后就吃饱了,那么他可以跟操作系统说:我已经吃饱了(挂起)。于是操作系统就会让下一个人接着来。
如果是 Windows 操作系统来负责分蛋糕的,那么场面就很有意思了。他会这样定规矩:我会根据你们的优先级、饥饿程度去给你们每个人计算一个优先级。优先级最高的那个人,可以上来吃蛋糕——吃到你不想吃为止。等这个人吃完了,我再重新根据优先级、饥饿程度来计算每个人的优先级,然后再分给优先级最高的那个人。
这样看来,这个场面就有意思了——可能有些人是PPMM,因此具有高优先级,于是她就可以经常来吃蛋糕。可能另外一个人是个丑男,而去很ws,所以优先级特别低,于是好半天了才轮到他一次(因为随着时间的推移,他会越来越饥饿,因此算出来的总优先级就会越来越高,因此总有一天会轮到他的)。而且,如果一不小心让一个大胖子得到了刀叉,因为他饭量大,可能他会霸占着蛋糕连续吃很久很久,导致旁边的人在那里咽口水。。。 而且,还可能会有这种情况出现:操作系统现在计算出来的结果,5号PPMM总优先级最高,而且高出别人一大截。因此就叫5号来吃蛋糕。5号吃了一小会儿,觉得没那么饿了,于是说“我不吃了”(挂起)。因此操作系统就会重新计算所有人的优先级。因为5号刚刚吃过,因此她的饥饿程度变小了,于是总优先级变小了;而其他人因为多等了一会儿,饥饿程度都变大了,所以总优先级也变大了。不过这时候仍然有可能5号的优先级比别的都高,只不过现在只比其他的高一点点——但她仍然是总优先级最高的啊。因此操作系统就会说:5号mm上来吃蛋糕……(5号mm心里郁闷,这不刚吃过嘛……人家要减肥……谁叫你长那么漂亮,获得了那么高的优先级)。
那么,Thread.Sleep 函数是干吗的呢?还用刚才的分蛋糕的场景来描述。上面的场景里面,5号MM在吃了一次蛋糕之后,觉得已经有8分饱了,她觉得在未来的半个小时之内都不想再来吃蛋糕了,那么她就会跟操作系统说:在未来的半个小时之内不要再叫我上来吃蛋糕了。这样,操作系统在随后的半个小时里面重新计算所有人总优先级的时候,就会忽略5号mm。Sleep函数就是干这事的,他告诉操作系统“在未来的多少毫秒内我不参与CPU竞争”。
下面我们来考虑下两个问题
1、Thread.sleep(1000),1秒之后线程一定会被唤醒吗?
答案是不一定,因为你只是告诉操作系统:在未来的1000毫秒内我不想再参与到CPU竞争。那么1000毫秒过去之后,这时候也许另外一个线程正在使用CPU,那么这时候操作系统是不会重新分配CPU的,直到那个线程挂起或结束;况且,即使这个时候恰巧轮到操作系统进行CPU 分配,那么当前线程也不一定就是总优先级最高的那个,CPU还是可能被其他线程抢占去。
与此相似的,Thread有个Resume函数,是用来唤醒挂起的线程的。好像上面所说的一样,这个函数只是“告诉操作系统我从现在起开始参与CPU竞争了”,这个函数的调用并不能马上使得这个线程获得CPU控制权。
2、Thread.sleep(0)和不写有区别吗?
答案是有区别,假设我们刚才的分蛋糕场景里面,有另外一个PPMM 7号,她的优先级也非常非常高(因为非常非常漂亮),所以操作系统总是会叫道她来吃蛋糕。而且,7号也非常喜欢吃蛋糕,而且饭量也很大。不过,7号人品很好,她很善良,她没吃几口就会想:如果现在有别人比我更需要吃蛋糕,那么我就让给他。因此,她可以每吃几口就跟操作系统说:我们来重新计算一下所有人的总优先级吧。不过,操作系统不接受这个建议——因为操作系统不提供这个接口。于是7号mm就换了个说法:“在未来的0毫秒之内不要再叫我上来吃蛋糕了”。这个指令操作系统是接受的,于是此时操作系统就会重新计算大家的总优先级——注意这个时候是连7号一起计算的,因为“0毫秒已经过去了”嘛。因此如果没有比7号更需要吃蛋糕的人出现,那么下一次7号还是会被叫上来吃蛋糕。
因此,Thread.Sleep(0)的作用,就是“触发操作系统立刻重新进行一次CPU竞争”。竞争的结果也许是当前线程仍然获得CPU控制权,也许会换成别的线程获得CPU控制权。这也是我们在大循环里面经常会写一句Thread.Sleep(0) ,因为这样就给了其他线程比如Paint线程获得CPU控制权的权力,这样界面就不会假死在那里。
虽然上面提到说“除非它自己放弃使用 CPU ,否则将完全霸占 CPU”,但这个行为仍然是受到制约的——操作系统会监控你霸占CPU的情况,如果发现某个线程长时间霸占CPU,会强制使这个线程挂起,因此在实际上不会出现“一个线程一直霸占着 CPU 不放”的情况。至于我们的大循环造成程序假死,并不是因为这个线程一直在霸占着CPU。实际上在这段时间操作系统已经进行过多次CPU竞争了,只不过其他线程在获得CPU控制权之后很短时间内马上就退出了,于是就又轮到了这个线程继续执行循环,于是就又用了很久才被操作系统强制挂起。。。因此反应到界面上,看起来就好像这个线程一直在霸占着CPU一样

wait和notify、notifyall原理

wait、notify、notifyAll都是Object类中的方法,下面我们先看源码

/**
     * Causes the current thread to wait until another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object.
     * In other words, this method behaves exactly as if it simply
     * performs the call {@code wait(0)}.
     * <p>
     * The current thread must own this object's monitor. The thread
     * releases ownership of this monitor and waits until another thread
     * notifies threads waiting on this object's monitor to wake up
     * either through a call to the {@code notify} method or the
     * {@code notifyAll} method. The thread then waits until it can
     * re-obtain ownership of the monitor and resumes execution.
     * <p>
     * As in the one argument version, interrupts and spurious wakeups are
     * possible, and this method should always be used in a loop:
     * <pre>
     *     synchronized (obj) {
     *         while (&lt;condition does not hold&gt;)
     *             obj.wait();
     *         ... // Perform action appropriate to condition
     *     }
     * </pre>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. See the {@code notify} method for a
     * description of the ways in which a thread can become the owner of
     * a monitor.
     *
     * @exception  IllegalMonitorStateException  if the current thread is not
     *               the owner of the object's monitor.
     * @exception  InterruptedException if any thread interrupted the
     *             current thread before or while the current thread
     *             was waiting for a notification.  The <i>interrupted
     *             status</i> of the current thread is cleared when
     *             this exception is thrown.
     * @see        java.lang.Object#notify()
     * @see        java.lang.Object#notifyAll()
     */
    public final void wait() throws InterruptedException {
        wait(0);
    }

注释说的很清楚,让当前线程等待直到其他线程调用notify或者notifyAll方法,当前线程必须拥有monitor。当前线程释放此监视器所有权并等待到另一个线程通知等待此对象监视器上的线程唤醒通过调用notify方法

/**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the {@code wait} methods.
     * <p>
     * The awakened thread will not be able to proceed until the current
     * thread relinquishes the lock on this object. The awakened thread will
     * compete in the usual manner with any other threads that might be
     * actively competing to synchronize on this object; for example, the
     * awakened thread enjoys no reliable privilege or disadvantage in being
     * the next thread to lock this object.
     * <p>
     * This method should only be called by a thread that is the owner
     * of this object's monitor. A thread becomes the owner of the
     * object's monitor in one of three ways:
     * <ul>
     * <li>By executing a synchronized instance method of that object.
     * <li>By executing the body of a {@code synchronized} statement
     *     that synchronizes on the object.
     * <li>For objects of type {@code Class,} by executing a
     *     synchronized static method of that class.
     * </ul>
     * <p>
     * Only one thread at a time can own an object's monitor.
     *
     * @exception  IllegalMonitorStateException  if the current thread is not
     *               the owner of this object's monitor.
     * @see        java.lang.Object#notifyAll()
     * @see        java.lang.Object#wait()
     */
    public final native void notify();

wait方法在其他线程调用对象的notify或notifyAll方法前,导致当前线程等待。线程会释放掉它所占有的“锁标志”,从而使别的线程有机会抢占该锁。当前线程必须拥有当前对象锁。如果当前线程不是此锁的拥有者,会抛出IllegalMonitorStateException异常。
唤醒当前对象锁的等待线程使用notify或notifyAll方法,也必须拥有相同的对象锁,否则也会抛出IllegalMonitorStateException异常。waite() 和notify()必须在synchronized函数或synchronized block中进行调用。如果在non-synchronized函数或non-synchronized block中进行调用,虽然能编译通过,但在运行时会发生 IllegalMonitorStateException的异常。
下面我们来看个例子:

public class SynchronizedThis {

    public void methodA(String name){
        synchronized (this) {
            System.out.println("this "+name+" start:"+Thread.currentThread().getName());
            try {
                System.out.println(name+" wait...");
                this.wait();
                System.out.println(name+" notifyed...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("this "+name+" end:"+Thread.currentThread().getName());
        }
    }

    public void methodB(String name){
        synchronized (this) {
            System.out.println("this "+name+" start:"+Thread.currentThread().getName());
            try {
                this.notify();
                System.out.println(name+" notify...");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("this "+name+" end:"+Thread.currentThread().getName());
        }
    }
}

两个线程类和线程启动类

public class MyThreadA implements Runnable {

    private SynchronizedThis sthis;

    public MyThreadA(SynchronizedThis sthis){
        this.sthis = sthis;
    }

    @Override
    public void run() {
        sthis.methodA("sunwukong");
    }
}
public class MyThreadB implements Runnable {
private SynchronizedThis sthis;

    public MyThreadB(SynchronizedThis sthis){
        this.sthis = sthis;
    }

    @Override
    public void run() {
        sthis.methodB("zhubajie");
    }
}
public class MainThread {
    public static void main(String[] args) {
        SynchronizedThis sthis = new SynchronizedThis();
        MyThreadA threadA = new MyThreadA(sthis);
        Thread TA = new Thread(threadA,"A");
        TA.start();
        MyThreadB threadB = new MyThreadB(sthis);
        Thread TB = new Thread(threadB,"B");
        TB.start();
    }
}

运行结果:

this sunwukong start:A
sunwukong wait...
this zhubajie start:B
zhubajie notify...
this zhubajie end:B
sunwukong notifyed...
this sunwukong end:A

sunwukong线程启动,sunwukong调用wait之后释放锁,zhubajie线程启动,zhubajie唤醒当前对象之后,sunwukong被唤醒开始执行。调用wait方法只会线程会放到wait set中,notify是从wait set中唤醒线程。
notifyAll和notify的区别是他是唤醒所有线程。那我们该用哪个呢?选择notify因为唤醒的线程少程序处理速度相对快些。但是notify如果处理不好程序可能会被挂掉。一般来说notifyAll写出来的程序代码会比选在notify更可靠。

yield

Thread类中的静态方法
暂停当前正在执行的线程对象。yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
yield()只能使同优先级或更高优先级的线程有执行的机会。yield不会释放资源所,当暂停后,等待调度程序再次调用,走完同步区域代码后才释放锁

public class SynchronizedThis {

    public void methodA(String name){
        synchronized (this) {
            System.out.println("this "+name+" start:"+Thread.currentThread().getName());
            try {
                for(int i=0;i<10;i++){
                    if(i == 5){
                        System.out.println("yield");
                        Thread.yield();
                    }
                    System.out.println(name+" start "+Thread.currentThread().getName()+"--"+i);
                }
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("this "+name+" end:"+Thread.currentThread().getName());
        }
    }
|

运行结果
this sunwukong start:A
sunwukong start A–0
sunwukong start A–1
sunwukong start A–2
sunwukong start A–3
sunwukong start A–4
yield
sunwukong start A–5
sunwukong start A–6
sunwukong start A–7
sunwukong start A–8
sunwukong start A–9
this sunwukong end:A
this zhubajie start:B
zhubajie start B–0
zhubajie start B–1
zhubajie start B–2
zhubajie start B–3
zhubajie start B–4
yield
zhubajie start B–5
zhubajie start B–6
zhubajie start B–7
zhubajie start B–8
zhubajie start B–9
this zhubajie end:B

join

Thread类中的非静态方法
让一个线程B“加入”到另外一个线程A的尾部。在A执行完毕之前,B不能工作
首先看下源码

/**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }
        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

调用join的线程会判断上个线程是否死亡,如果没有就等待
下面我看个例子:

public class SynchronizedThis {

    public void methodA(String name){
        System.out.println("this "+name+" start:"+Thread.currentThread().getName());
        try {
            for(int i=0;i<10;i++){
                System.out.println(name+" start "+Thread.currentThread().getName()+"--"+i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("this "+name+" end:"+Thread.currentThread().getName());
    }

    public void methodB(String name){
        System.out.println("this "+name+" start:"+Thread.currentThread().getName());
        try {
            for(int i=0;i<10;i++){
                System.out.println(name+" start "+Thread.currentThread().getName()+"--"+i);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("this "+name+" end:"+Thread.currentThread().getName());
    }
}
public class MyThreadA implements Runnable {

    private SynchronizedThis sthis;

    public MyThreadA(SynchronizedThis sthis){
        this.sthis = sthis;
    }

    @Override
    public void run() {
        sthis.methodA("sunwukong");
    }
}
public class MyThreadB implements Runnable {
private SynchronizedThis sthis;

    public MyThreadB(SynchronizedThis sthis){
        this.sthis = sthis;
    }

    @Override
    public void run() {
        sthis.methodB("zhubajie");
    }
}
public class MainThread {
    public static void main(String[] args) {
        try {
            SynchronizedThis sthis = new SynchronizedThis();
            MyThreadA threadA = new MyThreadA(sthis);
            Thread TA = new Thread(threadA,"A");
            TA.start();
            MyThreadB threadB = new MyThreadB(sthis);
            Thread TB = new Thread(threadB,"B");
            TB.start();
            TB.join();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

this sunwukong start:A
sunwukong start A--0
sunwukong start A--1
sunwukong start A--2
sunwukong start A--3
sunwukong start A--4
sunwukong start A--5
sunwukong start A--6
sunwukong start A--7
sunwukong start A--8
sunwukong start A--9
this sunwukong end:A
this zhubajie start:B
zhubajie start B--0
zhubajie start B--1
zhubajie start B--2
zhubajie start B--3
zhubajie start B--4
zhubajie start B--5
zhubajie start B--6
zhubajie start B--7
zhubajie start B--8
zhubajie start B--9
this zhubajie end:B

sunwukong运行完成之后zhubajie才能上场

Interrupt

interrupt是Thread中非静态方法
说到Interrupt我们可能会想到Thread.stop方法,不过这个方法已经被废弃我们在这里就不讨论了
Interrupt并不会中断一个正在运行的线程,我们看个例子:

public class SynchronizedThis {

    private volatile boolean stop = false;
    public void methodA(String name){
        while(!stop){
            System.out.println("this "+name+" is running ...");
            long time = System.currentTimeMillis();
            while ((System.currentTimeMillis() - time < 1000)) {
            }
        }
        System.out.println("this "+name+" is end ...");
    }
}
private SynchronizedThis sthis;

    public MyThreadA(SynchronizedThis sthis){
        this.sthis = sthis;
    }

    @Override
    public void run() {
        sthis.methodA("sunwukong");
    }
}

线程启动类

public class MainThread {
    public static void main(String[] args) {
        try {
            SynchronizedThis sthis = new SynchronizedThis();
            MyThreadA threadA = new MyThreadA(sthis);
            Thread TA = new Thread(threadA,"A");
            TA.start();
            Thread.sleep( 3000 );  
            TA.interrupt();
            System.out.println(Thread.currentThread().getName()+" is interrupted ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
main is interrupted ...
this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...

虽然已经执行了interrrupt但是线程仍然在执行
中断线程最好的,最受推荐的方式是,使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务
例如:

public class SynchronizedThis {

    public static volatile boolean stop = false;
    public void methodA(String name){
        while(!stop){
            System.out.println("this "+name+" is running ...");
            long time = System.currentTimeMillis();
            while ((System.currentTimeMillis() - time < 1000)) {
            }
        }
        System.out.println("this "+name+" is end ...");
    }
}

线程启动类

public class MainThread {
    public static void main(String[] args) {
        try {
            SynchronizedThis sthis = new SynchronizedThis();
            MyThreadA threadA = new MyThreadA(sthis);
            Thread TA = new Thread(threadA,"A");
            TA.start();
            Thread.sleep( 3000 ); 
            SynchronizedThis.stop = true;
            System.out.println(Thread.currentThread().getName()+" is stoped ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
main is stoped ...
this sunwukong is end ...

运行了3秒,game over
但是,当线程等待某些事件发生而被阻塞,又会发生什么?当然,如果线程被阻塞,它便不能核查共享变量,也就不能停止。这在许多情况下会发生,例如调用Object.wait()、ServerSocket.accept()和DatagramSocket.receive()时,他们都可能永久的阻塞线程。即使发生超时,在超时期满之前持续等待也是不可行和不适当的,所以,要使用某种机制使得线程更早地退出被阻塞的状态。
这时候interrupt该上场了,在线程受到阻塞时抛出一个中断信号,这样线程调用interrupt就得以退出阻塞的状态,结束线程了。
例如:

public class SynchronizedThis {

    public static volatile boolean stop = false;
    public void methodA(String name){
        try {
            while(!stop){
                System.out.println("this "+name+" is running ...");
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            System.out.println("this "+name+" is interupted ...");
        }
        System.out.println("this "+name+" is end ...");
    }
}

线程启动类

public class MainThread {
    public static void main(String[] args) {
        try {
            SynchronizedThis sthis = new SynchronizedThis();
            MyThreadA threadA = new MyThreadA(sthis);
            Thread TA = new Thread(threadA,"A");
            TA.start();
            Thread.sleep( 3000 ); 
            TA.interrupt();
            System.out.println(Thread.currentThread().getName()+" is interrupt ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果

this sunwukong is running ...
this sunwukong is running ...
this sunwukong is running ...
main is interrupt ...
this sunwukong is interupted ...
this sunwukong is end ...

还需说明一种特殊情况,处于大数据IO读写中的线程停止,处于大数据IO读写中的线程实际上处于运行状态,而不是等待或阻塞状态,因此上面的interrupt机制不适用。线程处于IO读写中可以看成是线程运行中的一种特例。停止这样的线程的办法是强行close掉io输入输出流对象,使其抛出异常,进而使线程停止。
例如:

public class SynchronizedThis {
    public static volatile boolean stop = false;
    public static volatile ServerSocket socket;
    public void methodA(String name) {
        try {
            socket = new ServerSocket(7856);
        } catch (IOException e) {
            System.out.println("create socket failed...");
            return;
        }
        while (!stop) {
            System.out.println("Waiting for connection...");
            try {
                Socket sock = socket.accept();
            } catch (IOException e) {
                System.out.println("accept() failed or interrupted...");
            }
        }
        System.out.println("Thread exiting under request...");
    }
}
public class MainThread {
    public static void main(String[] args) {
        try {
            SynchronizedThis sthis = new SynchronizedThis();
            MyThreadA threadA = new MyThreadA(sthis);
            Thread TA = new Thread(threadA,"A");
            TA.start();
            Thread.sleep( 3000 ); 
            SynchronizedThis.stop = true;
            SynchronizedThis.socket.close();
            System.out.println(Thread.currentThread().getName()+" is interrupt ...");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果如下:

Waiting for connection...
main is interrupt ...
accept() failed or interrupted...
Thread exiting under request...
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值