Java多线程编程核心技术——第三章的学习笔记

等待/通知模式

  • wait()方法将当前线程置入"预执行队列"中,释放锁,直到收到通知或者到达规定时间,才会继续参与对锁的争夺,得到锁后,从wait()代码位置开始继续执行,只有在同步方法或同步块中使用wait(),否则抛出IllegalMonitorStateException(非受查异常,不需要catch)
  • notify()/notyfyAll()也要在同步方法或同步块中调用,否则抛出IllegalMonitorStateException;用来通知wait的线程来争夺锁,但不释放锁,直到同步代码块执行完毕
  • wait()notify()/notifyAll()是针对同一把锁来说的
  • 通知过早,导致wait()的线程再也不被唤醒

生产者/消费者模式

  • 假死:全部线程进入waitting状态
  • 生产者持续生产,直到缓冲区满,阻塞;缓冲区不满后,继续生产
  • 消费者持续消费,直到缓冲区空,阻塞;缓冲区不空后,继续消费
  • 生产者可以有多个,消费者也可以有多个

三种实现

 - BlockingQueue
 - wait && notify
 - Lock && Condition

判断模型的正确性

  • 同一产品的消费行为一定发生在生产行为之后
  • 任意时刻,缓冲区大小不小于0,不大于限制容量

通过管道进行线程间通信:字节、字符流

PipeInputStream和PipeOutputStream
PipedReader和PipeWriter
outputStream.connect()来连接两个管道
阻塞,没有数据可读的时候会阻塞

join的使用

  • x.join(),x正常执行,而当前线程则被阻塞,直到x被销毁后,继续从join处执行当前线程的代码
  • join(long)设定等待的时间,内部用wait()来实现,所以会释放锁
  • join()遇上interrupt()会出现异常
public class ThreadB extends Thread{
    @Override

    synchronized public void run() {//synchronized的有无影响结果
     try {
         System.out.println("begin B ThreadName = " + Thread.currentThread().getName() + "  " + System.currentTimeMillis());
         Thread.sleep(2000);
         System.out.println("begin B ThreadName = " + Thread.currentThread().getName() + "  " + System.currentTimeMillis());
     }
     catch (Exception e){
         e.printStackTrace();
     }
    }
}
 public static void main(String[] args) throws InterruptedException {
	// write your code here
        ThreadB b = new ThreadB();
       // ThreadA a = new ThreadA(b);


       // a.start();
        b.start();
        b.join(1000);
        System.out.println("main end "+ System.currentTimeMillis());
    }

如果无synchronized关键字

begin B ThreadName = Thread-0  1556087741789
main end 1556087742789
begin B ThreadName = Thread-0  1556087743816

如果有synchronized关键字

begin B ThreadName = Thread-0  1556087786059
begin B ThreadName = Thread-0  1556087788090
main end 1556087788090

可以发现join(1000)方法并没有在1秒后恢复主线程的执行,这是因为synchronized关键字导致线程b一直占用了这个锁,join只有在线程b结束后才能得到这个锁

如果加上ThreadA,取消掉main中关于a的注释

public class ThreadA extends Thread {
    private ThreadB b;
    public ThreadA(ThreadB b) {
        this.b = b;
    }

    @Override
    public void run() {
        try{
            synchronized (b){
                System.out.println("begin A ThreadName = "+ Thread.currentThread().getName()+"  "+System.currentTimeMillis());
                Thread.sleep(2000);
                System.out.println("end A ThreadName = "+ Thread.currentThread().getName()+"  "+System.currentTimeMillis());

            }
        }
        catch (Exception e){
            e.printStackTrace();
        }
    }
}

结果可能有很多种,在我的机器上是这样的

begin A ThreadName = Thread-1  1556088325106
end A ThreadName = Thread-1  1556088327136
begin B ThreadName = Thread-0  1556088327137
main end 1556088327136
begin B ThreadName = Thread-0  1556088329139

运行结果的解释:

  1. b.join()方法先抢到B锁,然后将B锁释放
  2. ThreadA抢到锁,打印内容,sleep,打印内容
  3. b.join()和ThreadB来争抢锁,join抢到,发现时间已过,释放锁
  4. ThreadB得到锁,打印
  5. 主线程继续往下执行,打印
  6. ThreadB同时sleep,打印

b.join(1000)方法的执行需要得到,ThreadB这把锁,只有得到锁后,才会进入join方法;
如果我们把上述代码的A,B的锁改为其他的,就会得到如下结果

begin A ThreadName = Thread-1  1556088940831
main end 1556088941832//没人和join争抢锁,超出等待时间后便结束了
end A ThreadName = Thread-1  1556088942859
begin B ThreadName = Thread-0  1556088942859
begin B ThreadName = Thread-0  1556088944861

猜你喜欢

转载自blog.csdn.net/qq_41374768/article/details/89493277