Java线程阻塞和唤醒的几种方式

Object类自带的方法

使用wait()方法来阻塞线程,使用notify()和notifyAll()方法来唤醒线程。
调用wait()方法后,线程将被阻塞,wait()方法将会释放当前持有的监视器锁(monitor),直到有线程调用notify/notifyAll()方法后方能继续执行。
notify/notifyAll()方法只是解除了等待线程的阻塞,并不会马上释放监视器锁,而是在相应的被synchronized关键字修饰的同步方法或同步代码块执行结束后才自动释放锁。
默认使用非公平锁,无法修改。
缺点:

  • 使用几个方法时,必须处于被synchronized关键字修饰的同步方法或同步代码块中,否则程序运行时,会抛出IllegalMonitorStateException异常。
  • 线程的唤醒必须在线程阻塞之后,否则,当前线程被阻塞之后,一直没有唤醒,线程将会一直等待下去(对比LockSupport)
public class SynchronizedDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) {

        Print print = new Print();

        new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A").start();
        new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B").start();
        new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C").start();
    }
}

class Print {

    Object object = new Object();
    int num = 1;

    public void printA() {
        synchronized (object) {
            try {
                while (num != 1) {
                    object.wait();
                }
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>A");
                }
                num = 2;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void printB() {
        synchronized (object) {
            try {
                while (num != 2) {
                    object.wait();
                }
                for (int i = 0; i < 10; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>B");
                }
                num = 3;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void printC() {
        synchronized (object) {
            try {
                while (num != 3) {
                    object.wait();
                }
                for (int i = 0; i < 15; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>C");
                }
                num = 1;
                object.notifyAll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Condition接口

使用await()方法来阻塞线程,signal()/singnalAll()方法来唤醒线程。
需要使用lock对象的newCondition()方法获得Condition条件对象(可有多个)。
可实现公平锁,默认是非公平锁
缺点:

  • 必须被Lock包裹,否则会在运行时抛出IllegalMonitorStateException异常。
  • 线程的唤醒必须在线程阻塞之后
  • Lock的实现是基于AQS,效率稍高于synchronized
public class ConditionDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) {
        Print print = new Print();
        new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A").start();

        new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B").start();

        new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C").start();
    }
}

class Print {

    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int num = 1;

    public void printA() {

        lock.lock();
        try {
            while (num != 1) {
                condition1.await();
            }
            for (int i = 0; i < 5; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>A");
            }
            num = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printB() {

        lock.lock();
        try {
            while (num != 2) {
                condition2.await();
            }
            for (int i = 0; i < 10; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>B");
            }
            num = 3;
            condition3.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void printC() {

        lock.lock();
        try {
            while (num != 3) {
                condition3.await();
            }
            for (int i = 0; i < 15; ++i) {
                System.out.println(Thread.currentThread().getName() + "==>C");
            }
            num = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

LockSupport

使用park()来阻塞线程,用unpark()方法来唤醒线程。
这里有一个许可证的概念,许可不能累积,并且最多只能有一个许可,只有1和0的区别。
特点:

  • 使用灵活,可以直接使用
  • 线程唤醒可在线程阻塞之前,因为调用unpark()方法后,线程已经获得了一个许可证(但也只能有一个许可证),之后阻塞时,可以直接使用这个许可证来通行。
  • 效率高
public class LockSupportDemo {

// 三个线程交替打印ABC
    public static void main(String[] args) throws Exception {

        Print print = new Print();

        Thread threadA = new Thread(() -> {
            while (true) {
                print.printA();
            }
        }, "A");

        Thread threadB = new Thread(() -> {
            while (true) {
                print.printB();
            }
        }, "B");

        Thread threadC = new Thread(() -> {
            while (true) {
                print.printC();
            }
        }, "C");

        threadA.start();
        threadB.start();
        threadC.start();

        while (true) {
            LockSupport.unpark(threadA);
            LockSupport.unpark(threadB);
            LockSupport.unpark(threadC);
        }

    }
}

class Print {

    private int num = 1;

    public void printA() {
        while (num != 1) {
            LockSupport.park();
        }
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "==>A");
        }
        num = 2;
    }

    public void printB() {
        while (num != 2) {
            LockSupport.park();
        }
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "==>B");
        }
        num = 3;
    }

    public void printC() {
        while (num != 3) {
            LockSupport.park();
        }
        for (int i = 0; i < 15; i++) {
            System.out.println(Thread.currentThread().getName() + "==>C");
        }
        num = 1;
    }
}

  • 3
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值