多线程 第五节 线程间通信

wait/notify 线程间通信

1、Java 的一种实现线程间通信的机制是:wait/notify 线程间通信。wait() 方法实现线程等待,使用 notify()/notifyAll() 方法唤醒线程
2、应用场景:去银行办业务,进门之后取票号,等叫到号码的时候会广播通知我们办业务
3、wait 方法:
     1)    在调用wait()之前,线程必须获得该对象级别锁(监视器),只能在同步方法或同步块中调用wait()方法。
     2)    作用是使当前执行代码的线程等待,释放其对象拥有的锁。该方法会将该线程放入“预执行队列”中,并且在 wait() 所在的代码处停止执行,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。或使用Thread.interrupt()中断它。
     3)    wait()是释放锁的,在执行到wait()方法之后,当前线程会释放锁,进入等待队列。当从wait()方法返回前,线程与其他线程竞争重新获得锁
     4)    当线程处于 wait() 状态时,调用线程对象的 interrupt() 方法会出现 InterruptedException 异常。 
4、thread.wait() 和 thread.wait(0) 是相同的,使用 thread.wait() 内部其实是调用的 thread.wait(0)
5、notify 方法:
     1)    随机唤醒一个处于wait状态的线程,而不是按优先级。对其发出通知 notify,并使它等待获取该对象的对象锁。
     2)    和wait()方法一样,notify()方法也要在同步块或同步方法中调用,在调用前,线程也必须获得该对象的对象级别锁。
     3)    执行 notify 方法之后,当前线程不会立即释放其拥有的该对象锁,而是等待 notify 方法执行完之后才会释放该对象锁。
6、notifyAll() 唤醒所有处于等待状态的线程,从等待状态退出,进入可运行状态,重新竞争获得对象锁。
7、wait()/notify() 方法总结:
     1)    wait()/notify() 要与 Synchronized 关键字一起使用,因为他们都需要首先获取该对象的对象锁,因此对这两个方法的调用需要放在 synchronized 方法或块当中。
     2)    wait 方法是释放锁,notify 方法是不释放锁的
     3)    notify 每次唤醒 wait 等待状态的线程都是随机的,且每次只唤醒一个
     4)    notifAll 每次唤醒 wait 等待状态的线程使之重新竞争获取对象锁,优先级最高的那个线程会最先执行。
     5)    注意wait()、notify()、notifyAll()是Object类的方法,而不是Thread类的方法。而且是 final 的,因此会被所有的 Java类所继承并且无法重写。并且每次调用wait()方法,都有相应的notify()、notifyAll()方法,且他们均作用于同一个对象
8、sleep() 和 wait() 的区别?
sleep():正在执行的线程主动让出 cpu给其他线程,在 sleep 指定的时间过后,cpu 才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep 方法并不会释放锁,其它被同步锁挡住了的线程也无法得到执行。
wait():是指在一个已经进入了同步锁的线程内,暂时让出同步锁,以便其它正在等待此锁的线程可以得到同步锁并运行,只有其它线程调用了 notify 方法,调用 wait 方法的线程就会解除 wait 状态使程序可以再次得到锁后继续向下运行。 

public class MyList {
    private static List list = new ArrayList<>();

    public static void add(int info){
        list.add(info);
    }

    public static int size(){
        return list.size();
    }
}

public class ThreadOne extends Thread {
    private Object lock;

    public ThreadOne(Object lock){
        super();
        this.lock = lock;
    }
    @Override
    public void run() {
        try {
            synchronized (lock){
                System.out.println("threadOne");
                System.out.println("MyList size" + MyList.size());
                if(MyList.size() != 5){
                    System.out.println("ThreadOne 开始等待 " + System.currentTimeMillis());
                    lock.wait();
                    System.out.println("ThreadOne 结束等待 " + System.currentTimeMillis());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class ThreadTwo extends Thread{
    private Object lock;

    public ThreadTwo(Object lock){
        super();
        this.lock = lock;
    }

    @Override
    public void run() {
        try {
            synchronized (lock){
                for (int i = 0; i < 10; i++) {
                    MyList.add(i);
                    if(MyList.size() == 5){
                        lock.notify();
                        System.out.println("线程ThreadOne已经通知唤醒");
                    }
                    System.out.println("添加了" + (i + 1) + "个元素!");
                    Thread.sleep(1000);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class WaitNotifyTest {
    public static void main(String[] args) {
        try {
            Object obj = new Object();
            ThreadOne threadOne = new ThreadOne(obj);
            threadOne.start();
            Thread.sleep(50);
            ThreadTwo threadTwo = new ThreadTwo(obj);
            threadTwo.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

线程阻塞

1、 阻塞状态:
       等待阻塞 执行了 wait() 方法。
       同步阻塞 获取对象的同步琐时,同步锁被别的线程占用。
       其他阻塞 执行了 sleep() 或 join() 方法。该线程正在等待I/O操作完成、调用了suspend()方法
2、 Thread.sleep():让当前正在执行的线程暂停一段时间并进入睡眠状态(阻塞状态)。是一个静态方法,调用此方法要捕捉InterruptedExceprion异常
3、 suspend():线程挂起,阻塞状态,不会释放锁
4、 interrupt():中断线程,它将接收到一个中断异常(InterruptedException),从而提早地终结被阻塞状态。
5、 isInterrupted():通过中断手段在运行中来判断线程是否中断来终止线程,但是必须重设中断标志位。
6、 resume():线程继续执行
7、 yeild():线程礼让。暂停当前正在执行的线程对象,并执行其他线程。它不会阻塞该线程,它只是将该线程转入就绪状态。当调用了yield方法后,只有优先级与当前线程相同,或者优先级比当前线程更高的就绪状态的线程才会获得执行的机会。
8、 join():线程加入。等待该线程终止。当某个程序执行流中调用其他线程的join()方法时,调用线程将被阻塞,直到被join方法加入的join线程完成为止。
 

死锁(deadlock)

1、    死锁:是指两个或两个以上的线程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象
2、    产生死锁的四个必要条件:
    1)    互斥条件:一个资源每次只能被一个线程使用
    2)    请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放
    3)    不剥夺条件:线程已获得的资源,在末使用完之前,不能强行剥夺
    4)    循环等待条件:若干线程之间形成一种头尾相接的循环等待资源关系
    这四个条件是死锁的必要条件,只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
3、    预防死锁的方法如下:
    1)    尽量使用 tryLock(long timeout, TimeUnit unit) 的方法 (ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁
    2)    尽量使用 Java. util. concurrent 并发类代替自己手写锁
    3)    尽量降低锁的使用粒度,尽量不要几个功能用同一把锁
    4)    尽量减少同步的代码块

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值