Thread实现及方法

2021-10-19 10:04:00 浏览数 (1)

Thread方法

构造方法

代码语言:javascript复制
源码:

public Thread()
public Thread(String name)
public Thread(Runnable target)
public Thread(Runnable target, String name)
public Thread(Runnable target, AccessControlContext acc)
public Thread(ThreadGroup group, String name)
public Thread(ThreadGroup group, Runnable target)
public Thread(ThreadGroup group, Runnable target, String name)
public Thread(ThreadGroup group, Runnable target, String name, long stackSize)

注意:
所有的构造方法都会去调用一个静态的init方法。
stacksize越大则代表着正在线程内方法调用递归的深度就越深,stacksize越小则代表着创建的线程数量越多。默认是0

如果在构造Thread的时候没有显示地指定一个ThreadGroup,那么子线程将会被加入到父线程所在的线程组里边。

代码语言:javascript复制
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc, boolean inheritThreadLocals) {
    if (name == null) {
        throw new NullPointerException("name cannot be null");
    }

    this.name = name;

    Thread parent = currentThread();//获取当前线程(父线程)
    SecurityManager security = System.getSecurityManager();
    if (g == null) {
        /* Determine if it's an applet or not */

    /* If there is a security manager, ask the security manager
       what to do. */
        if (security != null) {
            g = security.getThreadGroup();
        }

    /* If the security doesn't have a strong opinion of the matter
       use the parent thread group. */
        if (g == null) {
            g = parent.getThreadGroup();
        }
    }

/* checkAccess regardless of whether or not threadgroup is
   explicitly passed in. */
    g.checkAccess();

    /*
     * Do we have the required permissions?
     */
    if (security != null) {
        if (isCCLOverridden(getClass())) {
            security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    }

    g.addUnstarted();

    this.group = g;
    this.daemon = parent.isDaemon();
    this.priority = parent.getPriority();
    if (security == null || isCCLOverridden(parent.getClass()))
        this.contextClassLoader = parent.getContextClassLoader();
    else
        this.contextClassLoader = parent.contextClassLoader;
    this.inheritedAccessControlContext =
            acc != null ? acc : AccessController.getContext();
    this.target = target;
    setPriority(priority);
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    /* Stash the specified stack size in case the VM cares */
    this.stackSize = stackSize;

    /* Set thread ID */
    tid = nextThreadID();
}

通过的Thread的源码解析可以得出以下结论:

  • main线程所在的ThreadGroup称为main
  • 构造一个线程的时候如果没有显示的指定ThreadGroup,那么它将会和父线程同属一个ThreadGroup

currentThread

代码语言:javascript复制
Thread.currentThread().getName()

isAlive

描述:测试当前线程是否处于活动状态(已经启动且没有运行结束)。

代码语言:javascript复制
public class ThreadTest7 {

    public static void main(String[] args) {

        Thread thread = new MyThread7();
        System.out.println("准备启动线程"   thread.isAlive());
        thread.start(); // 启动线程
        System.out.println("已经启动线程"   thread.isAlive());
    }
}

class MyThread7 extends Thread{

    @Override
    public void run() {
        System.out.println("run方法"   Thread.currentThread().getName());
    }
}

sleep

描述:作用是在指定的毫秒数内让当前正在执行的线程暂停执行。

代码语言:javascript复制
sleep是一个静态方法,有两个重载方法,一个是需要传毫秒数,另一个即需要传毫秒数也需要传纳秒数。

public static native void sleep(long millis)
public static void sleep(long millis, int nanos)

sleep方法会使当前线程进入指定毫秒数的休眠,暂停执行。虽然给定了一个休眠的时间,但是最终还是以系统的定时器和调度器的精度为准,休眠有一个非常重要的特性,那就是其不会放弃monitor锁的所有权。

在JDK1.5以后,JDK引入了一个枚举TimeUtil,其对sleep方法提供了很好的封装。在使用Thread.sleep的地方完全可以使用TimeUtil来替代,因为sleep可以做的事,TimeUtil全部都能完成,并且功能更加强大。

sleep会使线程短暂进入BLOCKED,会在指定时间内释放CPU资源,但不会释放"锁标志",也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。

sleep方法给其他线程运行机会时不考虑线程的优先级,因此会给低优先级的线程以运行的机会。

代码语言:javascript复制
sleep和wait区别:
1.来自不同的类
2.sleep不会释放锁,wait会释放锁
3.使用范围不同:wait必须在同步代码块中使用,sleep没有限制
4.是否需要捕获异常:sleep需要捕获异常,wait不需要捕获异常

getId

代码语言:javascript复制
源码:
public long getId() {
   return tid;
}

备注:
线程的ID在整个JVM进程中都是唯一的,并且都是从0开始逐次递增。

如果你发现你在main线程中创建的一个唯一的线程,并且调用了getId后发现其并不等于0,这是因为在JVM启动的时候,实际上已经派生出了很多个线程,自增序列已经有了一定的消耗,因此我们自己创建的线程绝非是从0开始的。

停止线程

1. 使用退出标志

代码语言:javascript复制
public class ThreadTest8 {

    public static void main(String[] args) throws InterruptedException{
        Thread thread = new MyThread8();
        thread.start();
        Thread.sleep(2000);
        ((MyThread8) thread).stopThread();
    }
}

class MyThread8 extends Thread{

    private boolean flag = true;

    @Override
    public void run() {

        try {
            while (flag) {
                System.out.println("time:"   System.currentTimeMillis());
                Thread.sleep(1000);
            }
            System.out.println("线程执行结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void stopThread() {
        flag = false;
    }
}

执行结果:
time:1630930825021
time:1630930826025
线程执行结束

2. stop方法停止

代码语言:javascript复制
public class ThreadTest9 {

    public static void main(String[] args) throws InterruptedException{
        Thread thread = new MyThread9();
        thread.start();
        Thread.sleep(2000);
        thread.stop();
    }
}

class MyThread9 extends Thread{

    @Override
    public void run() {

        try {
            while (true) {
                System.out.println("time:"   System.currentTimeMillis());
                Thread.sleep(1000);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ThreadDeath death) {
            death.printStackTrace();
        }
    }
}

stop方法已经被作废,如果强制让线程停止有可能使一些清理性工作得不到完成,另一个原因是对锁定的对象进行“解锁”,导致数据得不到同步处理,出现数据不一致的问题。

代码语言:javascript复制
public class ThreadTest10 {

    public static void main(String[] args) throws InterruptedException{

        User10 user10 = new User10();
        Thread thread = new MyThread10(user10);
        thread.start();
        Thread.sleep(500);
        thread.stop();
        System.out.println("username: "   user10.getUsername()   " --- password: "   user10.getPassword());
    }

}

class MyThread10 extends Thread{

    private User10 user10;

    public MyThread10(User10 user10) {
        this.user10 = user10;
    }

    @Override
    public void run() {
        user10.update("tom", "admin");
    }
}

@Getter
@Setter
class User10{

    private String username = "no";
    private String password = "no";

    synchronized public void update(String username, String password) {

        try {
            this.username = username;
            Thread.sleep(1000);
            this.password = password;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果:username被修改了,但是password没有被修改
username: tom --- password: no

3. interrupt方法停止

描述:调用interrupt方法不会真正结束线程,只是在当前线程上打上一个停止的标记。

代码语言:javascript复制
public class ThreadTest11 {

    public static void main(String[] args) throws InterruptedException{

       Thread thread = new MyThread11();
       thread.start();
       thread.interrupt();
    }
}

class MyThread11 extends Thread{

    @Override
    public void run() {

        for (int i = 0; i < 10; i  ) {
            System.out.println("i="   i);
        }
    }
}

执行结果:
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10

描述:Thread类提供了interrupted方法测试当前线程是否中断,isinterrupted方法测试线程是否已经中断。

代码语言:javascript复制
public class ThreadTest11 {

    public static void main(String[] args) throws InterruptedException{

       Thread thread = new MyThread11();
       thread.start();
       thread.interrupt();
       System.out.println("是否停止1:"   thread.isInterrupted());
       System.out.println("是否停止1:"   Thread.interrupted());
    }
}


class MyThread11 extends Thread{

    @Override
    public void run() {

        for (int i = 0; i < 10; i  ) {
            System.out.println("i="   i);
        }
    }
}

执行结果:
是否停止1:true
i=0
是否停止1:false
i=1
i=2
i=3
i=4
i=5
i=6
i=7
i=8
i=9
i=10

thread.isInterrupted()方法是判断MyThread11线程是否被打上停止标记,Thread.interrupted()方法是检查主线程是否被打上停止标记。

代码语言:javascript复制
public class ThreadTest12 {

    public static void main(String[] args) {

        Thread.currentThread().interrupt();
        System.out.println("是否停止1:"   Thread.interrupted());
        System.out.println("是否停止2:"   Thread.interrupted());
    }
}

执行结果:
是否停止1:true
是否停止2:false

interrupted方法会检测当前运行线程是否已经中断,此方法会清除中断状态,也就是说,假设当前线程中断状态为true,第一次调此方法,将返回true,表明的确已经中断了,但是第二次调用后,将会返回false,因为第一次调用的操作已将中断状态重新置为false了。

代码语言:javascript复制
isinterruptd方法的源码中该参数为false,表示用不擦除
public boolean isInterrupted() {
   return isInterrupted(false);
}

interrupted静态方法中该参数为true,表示想要擦除
public static boolean interrupted() {
   return currentThread().isInterrupted(true);
}

private native boolean isInterrupted(boolean ClearInterrupted);
参数ClearInterrupted主要用来控制立即擦除线程interrupt标识
  • interrupt:将线程中断状态设置为true,表明此线程目前是中断状态。此时如果调用isInterrupted()方法,将会得到true的结果。
  • isInterrupted:检测当前线程是否已经中断,此方法与下一方法的区别在于此方法不会清除中断状态。
  • interrupted:检测当前运行线程是否中断,此方法会清除中断状态,也就是说,假设当前线程中断状态为true,第一次调此方法,将返回true,表明的确已经中断了,但是第二次调用后,将会返回false,因为第一次调用的操作已将中断状态重新置为false了。

结论:interrupt方法本质上不会进行线程的终止操作的,它不过是改变了线程的中断状态,将线程中断状态设置为true。而改变了此状态带来的影响是,部分可中断的线程方法(比如Object.wait, Thread.sleep, Object.join)会定期执行isInterrupted方法,检测到此变化,随后会停止阻塞并抛出InterruptedException异常,并且在抛出异常后立即将线程的中断标示位清除,即重新设置为false。

总结:在捕获InterruptedException异常之后如果只是想记录一条日志,那么就是不负责任的做法,因为在捕获InterruptedException异常的时候会将线程的中断标志置由trrue擦除掉改为false,这样就会导致方法其他链路获取该线程的中断状态有误,不能做出正确的响应。所以至少在捕获了InterruptedException异常之后,如果你什么也不想做,那么就将标志重新置为true,以便栈中更高层的代码能知道中断,并且对中断作出响应。

suspend&resume

描述:suspend方法暂停线程进入block状态,重启暂停的线程使用resume方法。

代码语言:javascript复制
public class ThreadTest14 {

    public static void main(String[] args) throws InterruptedException{

        Thread thread = new MyThreasd14();
        thread.start();
        Thread.sleep(1000);

        thread.suspend(); // 暂停线程
        System.out.println("A="   System.currentTimeMillis()   " i="   ((MyThreasd14) thread).getI());
        Thread.sleep(1000);
        System.out.println("A="   System.currentTimeMillis()   " i="   ((MyThreasd14) thread).getI());

        thread.resume(); // 恢复暂停线程

        Thread.sleep(1000);

        thread.suspend();
        System.out.println("B="   System.currentTimeMillis()   " i="   ((MyThreasd14) thread).getI());
        Thread.sleep(1000);
        System.out.println("B="   System.currentTimeMillis()   " i="   ((MyThreasd14) thread).getI());
    }
}

@Getter
@Setter
class MyThreasd14 extends Thread {

    private long i = 0;

    @Override
    public void run() {
        while (true) {
            i  ;
        }
    }
}

执行结果:
A=1631098898278 i=683298527
A=1631098899283 i=683298527
B=1631098900284 i=1405066957
B=1631098901288 i=1405066957

suspend方法如果独占公共的同步对象(不会释放同步锁),那么其它线程无法访问公共的同步对象。

代码语言:javascript复制
public class ThreadTest15 {

    public static void main(String[] args) throws InterruptedException{

        MyThreadService myThreadService = new MyThreadService();

        Thread thread = new Thread(() -> {
           myThreadService.printInfo();
        });
        thread.setName("A");
        thread.start();

       Thread.sleep(1000);

        Thread thread2 = new Thread(() -> {
            myThreadService.printInfo();
        });
        thread2.start();
    }
}

模式一:不加锁
class MyThreadService {

    public void printInfo() {
        System.out.println("线程开始执行");
        if ("A".equals(Thread.currentThread().getName())) {
            System.out.println("A线程永远Suspend");
            Thread.currentThread().suspend();
        }
        System.out.println("线程执行结束");
    }
}
执行结果:
线程开始执行
A线程永远Suspend
线程开始执行
线程执行结束

模式二:加锁
class MyThreadService {

    synchronized public void printInfo() {
        System.out.println("线程开始执行");
        if ("A".equals(Thread.currentThread().getName())) {
            System.out.println("A线程永远Suspend");
            Thread.currentThread().suspend();
        }
        System.out.println("线程执行结束");
    }
}
执行结果:
线程开始执行
A线程永远Suspend

suspend之后,只是让线程停止执行,并一直持有锁,不会退出。

代码语言:javascript复制
public class ThreadTest16 {

    public static void main(String[] args) throws InterruptedException{

        Thread thread = new MyThread16();
        thread.start();

        Thread.sleep(10);

        thread.suspend(); // 让线程暂停

        System.out.println("main线程执行结束");
    }
}

class MyThread16 extends Thread{

    private long i = 0;

    @Override
    public void run() {

        while (true) {
            i  ;
            System.out.println("i="   i);
        }
    }
}

执行结果:
i=1
i=2
i=3
i=4
.....
i=508
i=509
i=510
i=511
main线程执行结束

yield

描述:yield方法的作用是放弃当前CPU资源,将资源让给其它的任务去占用CPU执行,但是放弃时间不确定,有可能刚刚放弃,马上有获取了CPU时间片。

代码语言:javascript复制
public class ThreadTest17 {

    public static void main(String[] args) {

        Thread thread = new MyThread17();
        thread.start();
    }
}

class MyThread17 extends Thread {

    @Override
    public void run() {
        int count = 0;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 500000; i  ) {
            Thread.yield();
            count  = i   1;
        }
        long endTime = System.currentTimeMillis();
        System.out.println("耗时:"   (endTime - startTime)   "毫秒");
    }
}

执行结果:
耗时:174毫秒

yield会使线程进入RUNABLE就绪状态,但不会释放"锁标志",也就是说如果有 synchronized 同步块,其他线程仍然不能访问共享数据。yield方法只会给相同优先级或更高优先级的线程以运行的机会。yield只是对CPU调度器的一个提示,它会导致线程上线文的切换。

start

代码语言:javascript复制
源码:

public synchronized void start() {
 
    if (threadStatus != 0)
        throw new IllegalThreadStateException();

    group.add(this);

    boolean started = false;
    try {
        start0();
        started = true;
    } finally {
        try {
            if (!started) {
                group.threadStartFailed(this);
            }
        } catch (Throwable ignore) {
            /* do nothing. If start0 threw a Throwable then
              it will be passed up the call stack */
        }
    }
}

注意:start方法是线程安全的
  • Thread被构造后的new状态,事实上threadStatus这个内部状态是0;
  • 不能两次启动Thread,否则就会出现IllegalThreadStateException异常;
  • 线程启动后将会被加到一个ThreadGroup中;
  • 一个线程生命周期结束后,也就是到了TERMINATED状态,再次调用start方法是不允许的,也就是说TERMINATED状态是没有办法回到RUNNABLE/RUNNING状态的;

join

描述:在线程B中执行(join方法也是可被中断的)某个线程A的join方法,会使当前线程B进入等待,直至线程A结束生命周期,或者到达指定时间,那么在这期间B线程都是处于BLOCKED的。

代码语言:javascript复制
t.join();      //调用join方法,等待线程t执行完毕
t.join(1000);  //等待 t 线程,等待时间是1000毫秒。

thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。

比如在线程B中调用了线程A的Join()方法,直到线程A执行完毕后,才会继续执行线程B。

总结:

  • join和synchronized区别:join的内部使用wait方法进行等待,而synchronized关键字使用的是对象锁进行同步。
  • join(long)是在内部使用wait(long)来实现的,所以join(long)方法具有释放同步锁的特点。
  • join与interrupt方法如果相遇,则会出现异常,但进程并不会结束。原因是线程A还在继续运行,线程A并没有出现异常,是正常状态下继续执行。

优先级方法

描述:如果CPU比较忙,设置优先级可能会获得更多的CPU时间片,但是闲时优先级的高低几乎不会有任何作用。

代码语言:javascript复制
public final void setPriority(int newPriority)

不要再业务中企图使用优先级绑定某些特定的业务,或者让业务严重依赖线程优先级,这可能会让你大失所望。

代码语言:javascript复制
源码:
public final void setPriority(int newPriority) {
    ThreadGroup g;
    checkAccess();
    if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
        throw new IllegalArgumentException();
    }
    if((g = getThreadGroup()) != null) {
        if (newPriority > g.getMaxPriority()) {
            newPriority = g.getMaxPriority();
        }
        setPriority0(priority = newPriority);
    }
}

备注:
线程的优先级不能小于1也不能大于10,如果指定的线程的优先级大于线程所在的线程组的优先级,那么指定的优先级将会失效,取而代之的是线程组的最大优先级。

一般情况下不会对线程的优先级进行设定,更不会对某些业务严重依赖线程的优先级,一般设定线程的优先级使用默认的就行。线程默认的优先级和它的父类保持一致,一般情况下都是5,因为main线程的优先级是5,所以由main线程所派生出来的线程的优先级默认都是5。

上下文类加载器

getContextClassLoader()获取线程上下文的类加载器,简单来说就是这个线程是由哪个类加载器加载的,如果是在没有上下文类加载器的情况下,则保持通父线程同样的类加载器。

代码语言:javascript复制
源码:

@CallerSensitivepublic ClassLoader getContextClassLoader() {
    if (contextClassLoader == null)
        return null;
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        ClassLoader.checkClassLoaderPermission(contextClassLoader,
                Reflection.getCallerClass());
    }
    return contextClassLoader;
}

setContextClassLoader(ClassLoader cl)设置该线程的类加载器,这个方法可以打破JAVA类加载器的父委托机制,有时候该方法也被称为JAVA类加载器的后门。

代码语言:javascript复制
public void setContextClassLoader(ClassLoader cl) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission("setContextClassLoader"));
    }
    contextClassLoader = cl;
}

wait&notify&notifyAll

wait方法的作用是使当前正在执行的线程进入等待状态,wait方法是Object类的方法,该方法用来将当前线程放入到“预执行队列”中,并且在wait所在的代码行进行停止执行,直到接到通知或被中断为止。在调用wait方法之前,线程必须获得该对象的对象锁,也就是说只能在同步方法或同步代码块中调用wait方法。在执行wait方法后,当前线程锁会自动释放,当wait方法返回该线程与其他线程重新竞争获取锁。

代码语言:javascript复制
public class ThreadTest27 {

    public static void main(String[] args) throws InterruptedException{

        String str = new String();
        System.out.println("准备进入同步代码块");
        synchronized (str) {
            System.out.println("同步代码块第一行");
            str.wait();
            System.out.println("同步代码块第二行");
        }
    }
}

执行结果:
准备进入同步代码块
同步代码块第一行

notify方法也是要在同步方法或者同步代码块中使用,在调用前必须获得对象的对象锁。这个方式是用来通知那些可能等待锁对象的其他线程,如果有多个线程等待,由线程调试器随机选一个wait状态的线程,向其发出通知,并使等待获取该对象的对象锁。

在执行notify方法后,当前线程不会马上释放该对象的对象锁,wait状态的线程也不能马上获取该对象的对象锁,要等到执行notify方法的线程将任务执行完成后,也就是退出synchronized代码块后,当前线程才会释放锁,wait状态的线程才可以获得锁。

当第一个获得该对象锁的wait线程运行完成后,它会释放掉该对象锁,如果该对象没有再次使用nofity语句,则对象处于空闲状态,其它wait状态的线程由于没有得到通知,还会继续处理阻塞的wait状态,直到这个对象发现次发出通知。

代码语言:javascript复制
public class ThreadTest28 {

    public static void main(String[] args) throws InterruptedException{

        Object lock = new Object();

        Thread thread = new ThreadUser28A(lock);
        thread.start();

        Thread.sleep(20);

        Thread thread2 = new ThreadUser28B(lock);
        thread2.start();
    }
}

class ThreadUser28A extends Thread{

    private Object lock;

    public ThreadUser28A(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("线程A开始等待在"   System.currentTimeMillis());
                lock.wait();
                System.out.println("线程A结束等待在"   System.currentTimeMillis());
            }
        } catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}

class ThreadUser28B extends Thread{

    private Object lock;

    public ThreadUser28B(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock) {
                System.out.println("线程B准备发出通知"   System.currentTimeMillis());
                lock.notify();
                System.out.println("线程B结束发出通知"   System.currentTimeMillis());

                Thread.sleep(1000);
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

执行结果:
线程A开始等待在1632485745470
线程B准备发出通知1632485745493
线程B结束发出通知1632485745493
线程A结束等待在1632485746498

wait就是使线程停止运行,notify是使停止的线程继续运行。

总结: 1. 在执行同步代码块的过程中,遇到异常而导致线程终止,锁也不会被释放; 2. 在执行同步代码块的过程中,执行了锁所属的对象的wait方法,这个线程会释放对象锁,而此线程对象会进入线程等待池中等待被唤醒。notify方法必须执行完同步代码后才会释放锁;

代码语言:javascript复制
public class ThreadTest29 {

    public static void main(String[] args) throws InterruptedException{

        Object lock = new Object();

        Thread thread = new ThreadUser29A(lock);
        thread.setName("A");
        thread.start();

        Thread thread2 = new ThreadUser29A(lock);
        thread2.setName("B");
        thread2.start();

        Thread thread3 = new ThreadUser29A(lock);
        thread3.setName("C");
        thread3.start();

        Thread.sleep(1000);

        Thread thread4 = new ThreadUser29B(lock);
        thread4.setName("D");
        thread4.start();
    }
}

class ThreadUser29A extends Thread{

    private Object lock;

    public ThreadUser29A(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        ThreadService29 threadService29 = new ThreadService29();
        threadService29.foo(lock);
    }
}

class ThreadUser29B extends Thread{

    private Object lock;

    public ThreadUser29B(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized (lock) {
            lock.notify(); // 随机选一个进行唤醒

            // 如果要唤醒多个需要写多个notify,或者一次性全部唤醒使用notifyAll
        }
    }
}

class ThreadService29{

    public void foo(Object lock) {
        try {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()   "进入了fool,准备执行wait方法");
                lock.wait();
                System.out.println(Thread.currentThread().getName()   "结束了fool执行");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

执行结果:
B进入了fool,准备执行wait方法
C进入了fool,准备执行wait方法
A进入了fool,准备执行wait方法
B结束了fool执行

wait(long)和sleep(long)方法给常相似,都是在指定时间后线程会自动唤醒,区别在于sleep不会释放对象锁,而wait可以释放对象锁。

0 人点赞