Java中 notify/notifyAll、run/start、wait/sleep、有啥区别

文章目录

  • 一、notify/notifyAll
    • 1. notify方法:
    • 2. notifyAll方法:
  • 二、wait/sleep
    • 1. wait()方法:
    • 2. sleep()方法:
  • 三、run()/start()
    • 1. run()方法:
    • 2. start()方法:

一、notify/notifyAll

在Java中,notify和notifyAll是用于线程间通信的方法,用于唤醒等待中的线程。

1. notify方法:

用于唤醒在对象上等待的单个线程。如果有多个线程在等待同一个对象的监视器锁,则只会唤醒其中一个线程,具体唤醒哪个线程是不确定的。

2. notifyAll方法:

用于唤醒在对象上等待的所有线程。如果有多个线程在等待同一个对象的监视器锁,则会唤醒所有等待的线程,并使它们竞争锁资源。

‍下面是一个使用notify和notifyAll的示例:‍

class Message {
    private boolean isProduced = false;

    public synchronized void produce() {
        if (isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Producing message");
        isProduced = true;
        notify(); // 唤醒等待中的单个线程
    }

    public synchronized void consume() {
        if (!isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Consuming message");
        isProduced = false;
        notifyAll(); // 唤醒等待中的所有线程
    }
}

class Producer implements Runnable {
    private Message message;

    public Producer(Message message) {
        this.message = message;
    }

    @Override
    public void run() {
        while (true) {
            message.produce();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    private Message message;

    public Consumer(Message message) {
        this.message = message;
    }

    @Override
    public void run() {
        while (true) {
            message.consume();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Message message = new Message();
        Thread producerThread = new Thread(new Producer(message));
        Thread consumerThread = new Thread(new Consumer(message));

        producerThread.start();
        consumerThread.start();
    }
}
  • 在上面的示例中,Message类表示一个消息对象,isProduced表示是否已经生产了消息。

  • Producer类和Consumer类分别表示生产者和消费者线程。

  • 在生产者线程中,调用message.produce()方法生产消息,并通过notify()方法唤醒等待中的消费者线程。

  • 在消费者线程中,调用message.consume()方法消费消息,并通过notifyAll()方法唤醒等待中的生产者和消费者线程。

  • 通过运行上述示例,可以观察到生产者和消费者线程之间的交替执行。当生产者线程生产消息时,消费者线程会被唤醒并消费消息,然后生产者线程会被唤醒并继续生产消息,如此循环。这个例子演示了notify和notifyAll的不同行为。

二、wait/sleep

Java中的wait()和sleep()方法都可以用于线程的控制,但是两者的作用和使用方式有所不同。

1. wait()方法:

wait()方法是Object类中定义的一个方法,它的作用是使当前线程进入等待状态,直到其他线程调用notify()或notifyAll()方法唤醒它。

wait()方法必须在synchronized块中调用,否则会抛出IllegalMonitorStateException异常。

wait()方法的使用流程如下:

  • 当一个线程调用某个对象的wait()方法时,它会释放该对象的锁,并进入等待状态。
  • 当其他线程调用该对象的notify()或notifyAll()方法时,处于等待状态的线程会被唤醒,并尝试重新获取锁。
  • 一旦获取到锁,线程会从wait()方法返回,继续执行。

✨示例:✨

public class WaitDemo {
    public static void main(String[] args) {
        final Object lock = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("Thread 1 is waiting");
                    lock.wait(); // 线程1进入等待状态
                    System.out.println("Thread 1 is awake");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("Thread 2 is notifying");
                lock.notify(); // 唤醒等待的线程1
            }
        });

        thread1.start();
        thread2.start();
    }
}

在上述示例中,线程1在synchronized块中调用了lock对象的wait()方法,进入等待状态。而线程2在synchronized块中调用了lock对象的notify()方法,唤醒了线程1。

输出结果如下:

Thread 1 is waiting
Thread 2 is notifying
Thread 1 is awake

2. sleep()方法:

sleep()方法是Thread类中定义的一个方法,它的作用是使当前线程暂停执行指定的时间。

sleep()方法不会释放锁,因此可以在任何地方调用。

sleep()方法的使用方式为:

Thread.sleep(时间);

其中,时间参数为毫秒值。

示例:

public class SleepDemo {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                System.out.println("Thread is sleeping");
                Thread.sleep(2000); // 线程暂停2秒
                System.out.println("Thread is awake");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        thread.start();
    }
}

在上述示例中,线程暂停了2秒后输出"Thread is awake"。

输出结果如下:

Thread is sleeping
Thread is awake

sleep和wait对比:

  • wait()方法适用于线程之间的通信,通过等待和唤醒机制,实现线程的同步和协作。
  • sleep()方法适用于在线程中暂停执行一段时间,用于实现定时任务或控制线程执行速度。
  • 在实际应用中,可以根据具体需求选择使用wait()或sleep()方法,合理利用线程的等待和暂停机制,提高系统的性能和效率。

三、run()/start()

在Java中,run()方法和start()方法均与线程相关。

1. run()方法:

  • run()方法是Thread类中定义的一个普通方法,用于定义线程的执行逻辑。

  • 在使用Thread类创建线程时,可以重写run()方法,将自定义的代码逻辑写在run()方法中。

  • 当线程被调度执行时,会自动调用run()方法中的代码逻辑。

  • 一般情况下,我们不直接调用run()方法,而是通过调用start()方法来启动线程。

2. start()方法:

  • start()方法是Thread类中定义的一个方法,用于启动线程。
  • 在调用start()方法后,系统会自动创建一个新的线程,并调用该线程的run()方法来执行线程的逻辑。
  • start()方法会立即返回,并不会阻塞当前线程。
  • 注意,如果直接调用run()方法,那么只会在当前线程中执行run()方法中的逻辑,不会创建新的线程。

下面是一个简单的示例代码:

public class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread " + Thread.currentThread().getId() + ": " + i);
        }
    }

    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        // 启动线程1
        thread1.start();
        
        // 直接调用run()方法,不会创建新的线程
        thread2.run();
    }
}

运行以上代码,会输出类似以下的结果:

Thread 9: 0
Thread 9: 1
Thread 9: 2
Thread 9: 3
Thread 9: 4
Thread 10: 0
Thread 10: 1
Thread 10: 2
Thread 10: 3
Thread 10: 4

start()/run()方法对比:

  • start()方法用于启动线程,会创建一个新的线程并调用run()方法。
  • run()方法用于定义线程的执行逻辑,可以重写该方法来实现自定义的线程行为。
  • 直接调用run()方法只会在当前线程中执行run()方法的逻辑,不会创建新的线程。
  • 在多线程编程中,一般使用start()方法来启动线程,而不是直接调用run()方法。这样可以实现多个线程并发执行。

你可能感兴趣的:(《并发编程》专栏,java)