1 2 3 4

synchronized 学习笔记

线程生命周期

java.lang.Thread.State中定义了6种不同的线程状态,在给定一个时刻、线程只能处于其中一个状态

  以下是各状态的说明、以及状态间的联系

  • 新建(new)- 尚未调用start方法的线程处于此状态、此状态以为着:创建的线程尚未启动

  • 就绪(runnable)-已经调用了start方法的线程处于此状态。此状态以为着:线程已经咱jvm种运行。但是在操作系统层面,他可能处于运行状态、也能处于等待资源调度,资源调度完成就进入运行状态、所以该状态的可运行是指可以被运行、具体有没有运行要看底层操作系统的资源调度

  • 阻塞(blocked) -此状态意味着:此线程处于被阻塞状态、。表示线程在等待synchronized的隐式锁(monitor lock)。synchronized修饰的方法、代码块同一时刻只允许一个线程执行、其他线程只能等待,即处于阻塞状态。当占用synchronized的隐式锁的线程释放锁、并且等待的线程获得synchronized隐式锁时、就会从blocked装换到runnable状态

  • 等待(waiting)-此状态以为着:线程无限期等待、知道被其他线程显示的唤醒、阻塞和等待的区别在于、阻塞是被动的、他是在等待获取synchronized的隐式锁。而等待是主动的、通过调用Object.wait()等发进入。

进入方法 退出方法
没有设置Timeout参数的Object.wait() Object.notify()/Object.notifyall()
没有设置Timeout参数的Thread.jion方法 被调用的线程执行完毕
LockSupport.pack方法(java并法包种的锁、都是基于它实现的) LockSupport.unpack
  • 定时等待(time waiting)-此状态意味着:无需要等待其他线程显示的唤醒、在一定时间后会自动被系统唤醒
进入方法 退出方法
Thread.sleep 方法 时间结束
获得synchronized 隐式锁的线程、调用设置了Timeout参数的Object.wait 方法 时间结束/Object.notify/Object.notifyall
设置了Timeout 参数的Thread.jion 方法 时间结束/被调用的线程执行完毕
LockSupport.packNanos方法 LockSupport.unpack
LockSupport.packUntil方法 LockSupport.unpack
  • 终止(terminated) -线程执行完run方法、或者因为异常退出lrun方法、此状态意味着:线程结束了生命周期。

线程通信

当多个线程可以一起工作去解决某个问题的时候、如果某些部分必须在其他部分之前完成、那么就需要对线程进行协调

 wait/notify/notifyall

  • wait-wait会自动释放当前线程占有的对象锁、并请求操作系统挂起当前线程、让线程从running 状态转入waiting 状态、等待notufy/notifyall来唤醒。如果没有释放锁、那么其他线程就无法进入对象同步的同步方法块中、那么就无法执行notify或notifyall来唤醒线程、造成死锁。

  • notify - 唤醒一个正在waiting 状态的线程、并让他拿到对象锁、具体唤醒那一个线程有jvm控制

  • notifyall - 唤醒当前对象锁中所有正在waiting 状态的线程、接下来他们需要竞争对象锁

注意:

  • wait、 notify 、 nitifyall 都是object 类中的方法、而非Thread
  • wait、 notify、notifyall 只能在synchronized 方法中或者synchronized代码块中使用、否则会在运行中抛出IllegalMonitorStateException

为什么wait、notify、notifyall不定义在Thread中?

首先需要了解几个基本的知识点

  • 每一个java对象都有一个与之对应的监视器(montier)
  • 每一个监视器里面都有一个对象锁、一个等待队列、一个同步队列(这个我会在之后的内容详细分析)

生产者、消费者模式是wait、notify、notifyall的一个经典案例

public class ThreadWaitNotifyDemo{

  private static final int QUEUE_SIZE = 10;
  private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

      public static void main(String[] args) {
        new Producer("生产者A").start();
        new Consumer("消费者A").start();
    }

  static class Consumer extends Thread{
      Consumer(String name){
          super(name);
      }
      @Overide
      private void run(){
          while (true){
                synchronized (queue){
                    while (queue.size() == 0 ){
                        try {
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }

                    queue.poll(); // 每次移走队首元素
                    queue.notify();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " 从队列取走一个元素、队列当前有:" + queue.size() + " 个元素");
                }
            }
      }
   }


  static class Producer extends Thread{

         Producer(String name){
            super(name);
        }

        @Override
        public void run() {
            while(true){
                synchronized (queue){
                    while (queue.size() == QUEUE_SIZE){
                        try {
                            System.out.println("队列满、等待有空余空间");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notify();
                        }
                    }

                    queue.offer(1);//每次插入一个元素
                    queue.notify();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println(Thread.currentThread().getName() + " 向队列中插入一个元素,队列当前有:" + queue.size() + " 个元素");
                }
            }
        }
  }

}

运行结果

生产者A 向队列中插入一个元素,队列当前有:1 个元素
生产者A 向队列中插入一个元素,队列当前有:2 个元素
生产者A 向队列中插入一个元素,队列当前有:3 个元素
生产者A 向队列中插入一个元素,队列当前有:4 个元素
生产者A 向队列中插入一个元素,队列当前有:5 个元素
生产者A 向队列中插入一个元素,队列当前有:6 个元素
生产者A 向队列中插入一个元素,队列当前有:7 个元素
生产者A 向队列中插入一个元素,队列当前有:8 个元素
生产者A 向队列中插入一个元素,队列当前有:9 个元素
消费者B 从队列取走一个元素、队列当前有:8 个元素
消费者B 从队列取走一个元素、队列当前有:7 个元素
消费者B 从队列取走一个元素、队列当前有:6 个元素
消费者B 从队列取走一个元素、队列当前有:5 个元素
消费者B 从队列取走一个元素、队列当前有:4 个元素
消费者B 从队列取走一个元素、队列当前有:3 个元素
消费者B 从队列取走一个元素、队列当前有:2 个元素
消费者B 从队列取走一个元素、队列当前有:1 个元素
消费者B 从队列取走一个元素、队列当前有:0 个元素
队列空,等待数据
队列空,等待数据
生产者B 向队列中插入一个元素,队列当前有:1 个元素
生产者B 向队列中插入一个元素,队列当前有:2 个元素
生产者B 向队列中插入一个元素,队列当前有:3 个元素
生产者B 向队列中插入一个元素,队列当前有:4 个元素
生产者B 向队列中插入一个元素,队列当前有:5 个元素
生产者B 向队列中插入一个元素,队列当前有:6 个元素
生产者B 向队列中插入一个元素,队列当前有:7 个元素
生产者B 向队列中插入一个元素,队列当前有:8 个元素
生产者B 向队列中插入一个元素,队列当前有:9 个元素
生产者B 向队列中插入一个元素,队列当前有:10 个元素
队列满、等待有空余空间
消费者B 从队列取走一个元素、队列当前有:9 个元素
消费者B 从队列取走一个元素、队列当前有:8 个元素

运行结果分析

生产者A首先获取queue对象的锁、进入run方法、其他线程进入阻塞状态(blocked)、等待生产者A释放queue对象的锁!生产者A判断队列长度未满、向队列中加入1个产品、然后唤醒当前在等待当前对象锁的其他线程(目前没有)、打印语句、进入二次循环、获取到了queue的对象锁
生产者A添加到9个的时候、消费者B获取到了对象锁、将队列数据消费完后进入了waiting状态、释放对象锁、打印 ‘队列为空语句’
然后消费者B消费者获取到了对象锁、发现队列为空、进入waiting状态、释放对象锁、打印‘队列为空语句’
生产者B获取到对象锁、向队列中加入数据、执行notifyall语句、唤醒当前在等待该对象锁的线程,之前waiting状态的线程转为Runnable状态、继续争抢对象锁
生产者B将队列加满后、打印'队列满'语句、进入waiting状态、释放对象锁
消费者B线程抢到、执行消费队列逻辑......


jion

在线程操作中、可以使用join方法让一个线程强制运行、线程强制运行期间、其他线程无法运行、必须等待此线程完成之后才可以继续执行


public class ThreadJoinDemo{

  public static void main(String args[]){
    MyThread mt = new MyThread();
    Thread t1 = new Thread(mt,"Mythread");
    Thread t2 = new Thread(mt,"MyThread2")
    t1.start();
    try{
        t1.join()
        }catch(InterrupteException e){
        e.printStackTrace();
        }
    t2.start(); 
    try{
        t2.join()
        }catch(InterrupteException e){
        e.printStackTrace();
        }
    
    for(int i =0;i<10;i++){
    System.out.ptintln("main线程运行————》"+i);
    }
  }

  static class MyThread implements Runnable{
    @Override
    public void run(){
      for(int i =0;i<10;i++){
        System.out.println(Thread.currentThread.getName()+" 运行,i=:"+i);
        }
      }
  }

 }

运行结果

可以看到、main线程在启动t1线程后、调用t1.join()方法、t1线程一直在运行(拿到了cpu资源)、直到t1运行结束后、main启动T2线程,调用t2.join,t2运行结束后,main线程才开始打印


管道

管道输入/输出流和文件输入/输出流或者网络输入/输出流不同之处在于,它主要用于线程之间的数据传输、而传输的媒介为内存。管道输入/输出流包括了如下4种具体实现:PipedOutputStreamPipedInputStreamPipedReaderPipedWriter,前两种面向字节、而后两种面向字符。

public class Piped {

    public static void main(String[] args) throws IOException {
        PipedWriter out = new PipedWriter();
        PipedReader in = new PipedReader();
        //将输入和输出流想连接、否则使用的时候会抛出IOException
        out.connect(in);

        Thread printThread = new Thread(new Print(in),"PrintThread");
        printThread.start();
        int receive = 0;
        try{
            //从控制台输入流、获取输入信息
            while ((receive=System.in.read()) != -1){
                //通过输出流、输出信息
                out.write(receive);
            }
        }finally {
                out.close();
        }

    }




    static class Print implements Runnable{
        private PipedReader in;
        Print(PipedReader in){
            this.in = in;
        }

        @Override
        public void run() {
            int receive = 0;
            while (true) {
                try {
                    // 由于输出、输入流建立了连接、输入流能拿到输出流的信息
                    if ((receive =in.read())!=-1){
                        //打印信息
                        System.out.println((char) receive);
                    };
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

运行结果

posted @ 2022-04-27 10:13  startscorpio  阅读(44)  评论(0编辑  收藏  举报