• Java并发—线程常用方法总结


    1.join()方法

    join —— 让一个线程等待另一个线程完成才继续执行。如A线程线程执行体中调用B线程的join()方法,则A线程被阻塞,知道B线程执行完为止,A才能得以继续执行。

    public class MyRunnable implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName() + " " + i);
            }
        }
    }
    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Runnable runnable = new MyRunnable();
            Thread thread = new Thread(runnable);
            for(int i=0;i<5;i++){
                System.out.println("main:"+Thread.currentThread().getName() + " " + i);
                if(i==2){
                    thread.start();
                    thread.join();
                }
            }
        }
    }

    运行结果:

    main:main 0
    main:main 1
    main:main 2
    Thread-0 0
    Thread-0 1
    Thread-0 2
    Thread-0 3
    Thread-0 4
    main:main 3
    main:main 4

    2.Thread.sleep()方法

    sleep 是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复,调用sleep 不会释放对象锁。由于没有释放对象锁,所以不能调用里面的同步方法。

    public class Main {
        private int j = 10;
        Object obj = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            Main main = new Main();
            Runnable r1 = main.new MyRunnable();
            Runnable r2 = main.new MyRunnable();
            Thread t1 = new Thread(r1);
            Thread t2 = new Thread(r2);
            t1.start();
            t2.start();
        }
    
        class MyRunnable implements Runnable {
             public void run() {
                 synchronized(obj){
                     for (int i = 0; i < 5; i++) {
                         j++;
                         System.out.println("线程:"+Thread.currentThread()+"i:"+i+",j:"+j+",time:"+ System.currentTimeMillis());
                         if(i==2){
                             try {
                                 System.out.println("线程:"+Thread.currentThread()+"睡眠");
                                 Thread.sleep(2000);
                             } catch (InterruptedException e) {
                                 e.printStackTrace();
                             }
                         }
                     }
                 }
             }
        }
    }

    运行结果:

    线程:Thread[Thread-0,5,main]i:0,j:11,time:1503114191497
    线程:Thread[Thread-0,5,main]i:1,j:12,time:1503114191497
    线程:Thread[Thread-0,5,main]i:2,j:13,time:1503114191497
    线程:Thread[Thread-0,5,main]睡眠
    线程:Thread[Thread-0,5,main]i:3,j:14,time:1503114193500
    线程:Thread[Thread-0,5,main]i:4,j:15,time:1503114193500
    线程:Thread[Thread-1,5,main]i:0,j:16,time:1503114193500
    线程:Thread[Thread-1,5,main]i:1,j:17,time:1503114193500
    线程:Thread[Thread-1,5,main]i:2,j:18,time:1503114193501
    线程:Thread[Thread-1,5,main]睡眠
    线程:Thread[Thread-1,5,main]i:3,j:19,time:1503114195502
    线程:Thread[Thread-1,5,main]i:4,j:20,time:1503114195502

    从结果看,Thread-0休眠后没有释放锁,所以Thread-1并没有执行,直到Thread-0休眠结束并执行完毕后Thread-1才开始执行。

    3.Thread.yield( )方法

    调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程。它跟sleep方法类似,同样不会释放锁。但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。

    注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。

    public class Main {
        public static void main(String[] args) throws InterruptedException {
            Main main = new Main();
            Thread t1 = main.new MyThread();
            t1.start();
        }
    
        class MyThread extends Thread{
            public void run() {
                long beginTime=System.currentTimeMillis();
                int count=0;
                for (int i=0;i<50000000;i++){
                    count=count+(i+1);
                    Thread.yield();
                }
                long endTime=System.currentTimeMillis();
                System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
            }
        }
    }

    运行结果:

    用时:4448 毫秒!

    把Thread.yield();注释掉,运行结果:

    用时:1 毫秒!

    4.getPriority和setPriority

    用来获取和设置线程优先级。

    Thread t1 = new Thread();
    t1.setPriority(Thread.MAX_PRIORITY);

    MAX_PRIORITY:10

    MIN_PRIORITY:1

    NORM_PRIORITY:5

    5.setDaemon和isDaemon

    用来设置线程是否成为守护线程和判断线程是否是守护线程。

    Thread t1 = new Thread();
    t1.setDaemon(true);

    守护线程(后台线程)主要是为其他线程提供服务。如JVM中的垃圾回收线程。

    守护线程的生命周期与前台线程生命周期有一定关联。主要体现在:当所有的前台线程都进入死亡状态时,后台线程会自动死亡。

    6.wait()、notify()和notifyAll()

    wait()、notify()和notifyAll()是Object类中的方法:

    /**
     * Wakes up a single thread that is waiting on this object's
     * monitor. If any threads are waiting on this object, one of them
     * is chosen to be awakened. The choice is arbitrary and occurs at
     * the discretion of the implementation. A thread waits on an object's
     * monitor by calling one of the wait methods
     */
    public final native void notify();
     
    /**
     * Wakes up all threads that are waiting on this object's monitor. A
     * thread waits on an object's monitor by calling one of the
     * wait methods.
     */
    public final native void notifyAll();
     
    /**
     * Causes the current thread to wait until either another thread invokes the
     * {@link java.lang.Object#notify()} method or the
     * {@link java.lang.Object#notifyAll()} method for this object, or a
     * specified amount of time has elapsed.
     * <p>
     * The current thread must own this object's monitor.
     */
    public final native void wait(long timeout) throws InterruptedException;

    从这三个方法的文字描述可以知道以下几点信息:

      1)wait()、notify()和notifyAll()方法是本地方法,并且为final方法,无法被重写。

      2)调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的monitor(即锁)

      3)调用某个对象的notify()方法能够唤醒一个正在等待这个对象的monitor的线程,如果有多个线程都在等待这个对象的monitor,则只能唤醒其中一个线程;

      4)调用notifyAll()方法能够唤醒所有正在等待这个对象的monitor的线程;

      有朋友可能会有疑问:为何这三个不是Thread类声明中的方法,而是Object类中声明的方法(当然由于Thread类继承了Object类,所以Thread也可以调用者三个方法)?其实这个问题很简单,由于每个对象都拥有monitor(即锁),所以让当前线程等待某个对象的锁,当然应该通过这个对象来操作了。而不是用当前线程来操作,因为当前线程可能会等待多个线程的锁,如果通过线程来操作,就非常复杂了。

      上面已经提到,如果调用某个对象的wait()方法,当前线程必须拥有这个对象的monitor(即锁),因此调用wait()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

      调用某个对象的wait()方法,相当于让当前线程交出此对象的monitor,然后进入等待状态,等待后续再次获得此对象的锁(Thread类中的sleep方法使当前线程暂停执行一段时间,从而让其他线程有机会继续执行,但它并不释放对象锁);

      notify()方法能够唤醒一个正在等待该对象的monitor的线程,当有多个线程都在等待该对象的monitor的话,则只能唤醒其中一个线程,具体唤醒哪个线程则不得而知。

      同样地,调用某个对象的notify()方法,当前线程也必须拥有这个对象的monitor,因此调用notify()方法必须在同步块或者同步方法中进行(synchronized块或者synchronized方法)。

      nofityAll()方法能够唤醒所有正在等待该对象的monitor的线程,这一点与notify()方法是不同的。

      这里要注意一点:notify()和notifyAll()方法只是唤醒等待该对象的monitor的线程,并不决定哪个线程能够获取到monitor。

      举个简单的例子:假如有三个线程Thread1、Thread2和Thread3都在等待对象objectA的monitor,此时Thread4拥有对象objectA的monitor,当在Thread4中调用objectA.notify()方法之后,Thread1、Thread2和Thread3只有一个能被唤醒。注意,被唤醒不等于立刻就获取了objectA的monitor。假若在Thread4中调用objectA.notifyAll()方法,则Thread1、Thread2和Thread3三个线程都会被唤醒,至于哪个线程接下来能够获取到objectA的monitor就具体依赖于操作系统的调度了。

      上面尤其要注意一点,一个线程被唤醒不代表立即获取了对象的monitor,只有等调用完notify()或者notifyAll()并退出synchronized块,释放对象锁后,其余线程才可获得锁执行。

       synchronized, wait, notify结合:典型场景生产者消费者问题:

    /**
       * 生产者生产出来的产品交给店员
       */
      public synchronized void produce()
      {
          if(this.product >= MAX_PRODUCT)
          {
              try
              {
                  wait();  
                  System.out.println("产品已满,请稍候再生产");
              }
              catch(InterruptedException e)
              {
                  e.printStackTrace();
              }
              return;
          }
    
          this.product++;
          System.out.println("生产者生产第" + this.product + "个产品.");
          notifyAll();   //通知等待区的消费者可以取出产品了
      }
    
      /**
       * 消费者从店员取产品
       */
      public synchronized void consume()
      {
          if(this.product <= MIN_PRODUCT)
          {
              try 
              {
                  wait(); 
                  System.out.println("缺货,稍候再取");
              } 
              catch (InterruptedException e) 
              {
                  e.printStackTrace();
              }
              return;
          }
    
          System.out.println("消费者取走了第" + this.product + "个产品.");
          this.product--;
          notifyAll();   //通知等待去的生产者可以生产产品了
      }

    参考:

    Java中的多线程你只要看这一篇就够了

  • 相关阅读:
    Jenkins历史构建作业jobs的删除与管理
    Redis消息队列与主流的消息队列中间件对比
    GDAL数据模型
    Android的语言切换
    GDAL驱动实现向导
    Win7 安装IIS
    局域网中其他机器不能访问本机IIS网站
    DXF库(dxflib)使用指南
    GDAL中文学习资料
    QT的中文站址
  • 原文地址:https://www.cnblogs.com/Jason-Xiang/p/7514719.html
Copyright © 2020-2023  润新知