zoukankan      html  css  js  c++  java
  • Thread类相关方法

    线程对象
    每一个线程都是和类Thread的实例相关联的。在Java中,有两种基本的使用Thread对象的方式,可用来创建并发性程序。
      1.在应用程序需要发起异步任务的时候,只要生成一个Thread对象即可(继承Thread类和实现runnable接口),这样可以直接控制线程的创建并对其进行管理。
      2.把应用程序的任务交给执行器(executor),这样可以将对线程的管理从程序中抽离出来。

    Thread类中的静态方法

    Thread类中的静态方法表示操作的线程是"正在执行静态方法所在的代码块的线程"。为什么Thread类中要有静态方法,这样就能对CPU当前正在运行的线程进行操作。下面来看一下Thread类中的静态方法:

    1、currentThread()

    currentThread()方法返回的是对当前正在执行线程对象的引用。看一个重要的例子,然后得出结论:

    public class MyThread04 extends Thread
    {
        static
        {
            System.out.println("静态块的打印:" + 
                    Thread.currentThread().getName());    
        }
        
        public MyThread04()
        {
            System.out.println("构造方法的打印:" + 
                    Thread.currentThread().getName());    
        }
        
        public void run()
        {
            System.out.println("run()方法的打印:" + 
                    Thread.currentThread().getName());
        }
    }
    
    public static void main(String[] args)
    {
        MyThread04 mt = new MyThread04();
        mt.start();
    }

    看一下运行结果:

    静态块的打印:main
    构造方法的打印:main
    run()方法的打印:Thread-0

    这个例子说明了,线程类的构造方法、静态块是被main线程调用的,而线程类的run()方法才是应用线程自己调用的

    1、睡眠  (public static native void sleep(long millis) throws InterruptedException;)(是Thread类的静态方法)
    Thread.sleep(long millis)和Thread.sleep(long millis, int nanos)静态方法强制当前正在执行的线程休眠(暂停执行),以“减慢线程”。该方法的作用是在指定的毫秒内让当前"正在执行的线程"休眠(暂停执行)。这个"正在执行的线程"是关键,指的是Thread.currentThread()返回的线程。根据JDK API的说法,"该线程不丢失任何监视器的所属权",简单说就是sleep代码上下文如果被加锁了,锁依然在,但是CPU资源会让出给其他线程。
    线程睡眠的原因:线程执行太快,或者需要强制进入下一轮,因为Java规范不保证合理的轮换。
    睡眠的实现:调用静态方法。
    try {
      Thread.sleep(123);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    睡眠的位置:为了让其他线程有机会执行,可以将Thread.sleep()的调用放线程run()之内。这样才能保证该线程执行过程中会睡眠。

    注意:

      1、线程睡眠是帮助所有线程获得运行机会的最好方法。

      2、线程睡眠到期自动苏醒,并返回到可运行状态,不是运行状态。sleep()中指定的时间是线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。

      3、sleep()是静态方法,只能控制当前正在运行的线程。

    2、线程的优先级和线程让步yield()  (是Thread类的静态方法)

    线程的让步是通过Thread.yield()来实现的。yield()方法的作用是:暂停当前正在执行的线程对象,并执行其他线程。

    要理解yield(),必须了解线程的优先级的概念。线程总是存在优先级,优先级范围在1~10之间。JVM线程调度程序是基于优先级的抢先调度机制。在大多数情况下,当前运行的线程优先级将大于或等于线程池中任何线程的优先级。但这仅仅是大多数情况。

    注意:当设计多线程应用程序的时候,一定不要依赖于线程的优先级。因为线程调度优先级操作是没有保障的,只能把线程优先级作用作为一种提高程序效率的方法,但是要保证程序不依赖这种操作。

    当线程池中线程都具有相同的优先级,调度程序的JVM实现自由选择它喜欢的线程。这时候调度程序的操作有两种可能:一是选择一个线程运行,直到它阻塞或者运行完成为止。二是时间分片,为池内的每个线程提供均等的运行机会。

    设置线程的优先级:线程默认的优先级是创建它的执行线程的优先级。可以通过setPriority(int newPriority)更改线程的优先级。例如:

    Thread t = new MyThread();
    t.setPriority(8);
    t.start();

    线程优先级为1~10之间的正整数,JVM从不会改变一个线程的优先级。然而,1~10之间的值是没有保证的。一些JVM可能不能识别10个不同的值,而将这些优先级进行每两个或多个合并,变成少于10个的优先级,则两个或多个优先级的线程可能被映射为一个优先级。

    线程默认优先级是5,Thread类中有三个常量,定义线程优先级范围:

    static int MAX_PRIORITY
    线程可以具有的最高优先级。
    static int MIN_PRIORITY
    线程可以具有的最低优先级。
    static int NORM_PRIORITY
    分配给线程的默认优先级。

    3、Thread.yield()方法

    Thread.yield()方法作用是:暂停当前正在执行的线程对象,并执行其他线程。

    yield()应该做的是让当前运行线程回到可运行状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。

    结论:yield()从未导致线程转到等待/睡眠/阻塞状态。在大多数情况下,yield()将导致线程从运行状态转到可运行状态,但有可能没有效果。

    看一下例子:

    public class MyThread08 extends Thread
    {
        public void run()
        {
            long beginTime = System.currentTimeMillis();
            int count = 0;
            for (int i = 0; i < 50000000; i++)
            {
                Thread.yield();
                count = count + i + 1;
            }
            long endTime = System.currentTimeMillis();
            System.out.println("用时:" + (endTime - beginTime) + "毫秒!");
        }
    }
    
    public static void main(String[] args)
    {
        MyThread08 mt = new MyThread08();
        mt.start();
    }
    ——————————————————————————————————————————————————————————————————————————————————————————————————
    结果:
    用时:3264毫秒!
    用时:3299毫秒!
    用时:3232毫秒!
    用时:3256毫秒!
    用时:3283毫秒!
    用时:3504毫秒!
    用时:3378毫秒!

    看到,每次执行的用时都不一样,证明了yield()方法放弃CPU的时间并不确定。

    4. isDaeMon、setDaemon(boolean on) (是Thread的实例方法)

    讲解两个方法前,首先要知道理解一个概念。Java中有两种线程,一种是用户线程,一种是守护线程。守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是GC线程。如果进程中不存在非守护线程了,那么守护线程自动销毁,因为没有存在的必要,为别人服务,结果服务的对象都没了,当然就销毁了。理解了这个概念后,看一下例子:

    public class MyThread1 extends Thread
    {
        private int i = 0;
        
        public void run()
        {
            try
            {
                while (true)
                {
                    i++;
                    System.out.println("i = " + i);
                    Thread.sleep(1000);
                }
            } 
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args)
        {
            try
            {
                MyThread1 mt = new MyThread1();
                mt.setDaemon(true);
                mt.start();
                Thread.sleep(5000);
                System.out.println("我离开thread对象再也不打印了,我停止了!");
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    
    ————————————————————————————————
    结果:
    i = 1
    i = 2
    i = 3
    i = 4
    i = 5
    我离开thread对象再也不打印了,我停止了!
    i = 6

    要解释一下。我们将MyThread1线程设置为守护线程,看到第6行的那句话,而i停在6不会再运行了。这说明,main线程运行了5秒多结束,而i每隔1秒累加一次,5秒后main线程执行完结束了,MyThread1作为守护线程,main函数都运行完了,自然也没有存在的必要了,就自动销毁了,因此也就没有再往下打印数字。

    关于守护线程,有一个细节注意下,setDaemon(true)必须在线程start()之前

    5. holdsLock()    public static native boolean holdsLock(Object obj);

    判断某个线程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着"某条线程"指的是当前线程。

    Object o = new Object();
    @Test
    public void test1() throws Exception {
        new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized(o) {
                    System.out.println("child thread: holdLock: " + 
                        Thread.holdsLock(o));
                }
            }
        }).start();
        System.out.println("main thread: holdLock: " + Thread.holdsLock(o));
        Thread.sleep(2000);
    }
    main thread: holdLock: false
    child thread: holdLock: true
    

     6. 线程实例对象的 join()方法    public final void join() throws InterruptedException

    一、作用

      Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行执行。具体看代码:

    public class JoinTest {
        public static void main(String [] args) throws InterruptedException {
            ThreadJoinTest t1 = new ThreadJoinTest("小明");
            ThreadJoinTest t2 = new ThreadJoinTest("小东");
            t1.start();
            /**join的意思是使得放弃当前线程的执行,并返回对应的线程,例如下面代码的意思就是:
             程序在main线程中调用t1线程的join方法,则main线程放弃cpu控制权,并返回t1线程继续执行直到线程t1执行完毕
             所以结果是t1线程执行完后,才到主线程执行,相当于在main线程中同步t1线程,t1执行完了,main线程才有执行的机会
             */
            t1.join();
            t2.start();
        }
    
    }
    class ThreadJoinTest extends Thread{
        public ThreadJoinTest(String name){
            super(name);
        }
        @Override
        public void run(){
            for(int i=0;i<1000;i++){
                System.out.println(this.getName() + ":" + i);
            }
        }
    }

    上面程序结果是先打印完小明线程,在打印小东线程;  

    上面注释也大概说明了join方法的作用:在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。注意,这里调用的join方法是没有传参的,join方法其实也可以传递一个参数给它的,具体看下面的简单例子:

    public class JoinTest {
        public static void main(String [] args) throws InterruptedException {
            ThreadJoinTest t1 = new ThreadJoinTest("小明");
            ThreadJoinTest t2 = new ThreadJoinTest("小东");
            t1.start();
            /**join方法可以传递参数,join(10)表示main线程会等待t1线程10毫秒,10毫秒过去后,
             * main线程和t1线程之间执行顺序由串行执行变为普通的并行执行
             */
            t1.join(10);
            t2.start();
        }
    
    }
    class ThreadJoinTest extends Thread{
        public ThreadJoinTest(String name){
            super(name);
        }
        @Override
        public void run(){
            for(int i=0;i<1000;i++){
                System.out.println(this.getName() + ":" + i);
            }
        }
    }

    上面代码结果是:程序执行前面10毫秒内打印的都是小明线程,10毫秒后,小明和小东程序交替打印。

    所以,join方法中如果传入参数,则表示这样的意思:如果A线程中掉用B线程的join(10),则表示A线程会等待B线程执行10毫秒,10毫秒过后,A、B线程并行执行。需要注意的是,jdk规定,join(0)的意思不是A线程等待B线程0秒,而是A线程等待B线程无限时间,直到B线程执行完毕,即join(0)等价于join()。

    二、join与start调用顺序问题

      上面的讨论大概知道了join的作用了,那么,入股 join在start前调用,会出现什么后果呢?先看下面的测试结果

    public class JoinTest {
        public static void main(String [] args) throws InterruptedException {
            ThreadJoinTest t1 = new ThreadJoinTest("小明");
            ThreadJoinTest t2 = new ThreadJoinTest("小东");
            /**join方法可以在start方法前调用时,并不能起到同步的作用
             */
            t1.join();
            t1.start();
            //Thread.yield();
            t2.start();
        }
    
    }
    class ThreadJoinTest extends Thread{
        public ThreadJoinTest(String name){
            super(name);
        }
        @Override
        public void run(){
            for(int i=0;i<1000;i++){
                System.out.println(this.getName() + ":" + i);
            }
        }
    }

    上面代码执行结果是:小明和小东线程交替打印。

    所以得到以下结论:join方法必须在线程start方法调用之后调用才有意义。这个也很容易理解:如果一个线程都没有start,那它也就无法同步了。

      三、join方法实现原理

      有了上面的例子,我们大概知道join方法的作用了,那么,join方法实现的原理是什么呢?

      其实,join方法是通过调用线程的wait方法来达到同步的目的的。例如,A线程中调用了B线程的join方法,则相当于A线程调用了B线程的wait方法,在调用了B线程的wait方法后,A线程就会进入阻塞状态,具体看下面的源码:

    public final synchronized void join(long millis)
        throws InterruptedException {
            long base = System.currentTimeMillis();
            long now = 0;
    
            if (millis < 0) {
                throw new IllegalArgumentException("timeout value is negative");
            }
    
            if (millis == 0) {
                while (isAlive()) {
                    wait(0);
                }
            } else {
                while (isAlive()) {
                    long delay = millis - now;
                    if (delay <= 0) {
                        break;
                    }
                    wait(delay);
                    now = System.currentTimeMillis() - base;
                }
            }
        }

    从源码中可以看到:join方法的原理就是调用相应线程的wait方法进行等待操作的,例如A线程中调用了B线程的join方法,则相当于在A线程中调用了B线程的wait方法,当B线程执行完(或者到达等待时间),B线程会自动调用自身的notifyAll方法唤醒A线程,从而达到同步的目的。

     

  • 相关阅读:
    php Thinkphp 经纬度查位置(误差200米左右)
    php redis 基础操作 Thinkphp 直接套用
    超实用的华为云服务器选购技巧经验!
    连续四年第一!华为云FusionAccess夺桌面云市场桂冠
    华为云服务器、云数据库、云安全免费送!
    Linux_CentOS_6.5安装Nginx
    PHP破解wifi密码(wifi万能钥匙的接口)
    PHP+json开发API接口实例
    52首歌,一个你
    js 获取前天、昨天、今天、明天、后天的时间
  • 原文地址:https://www.cnblogs.com/myseries/p/8638467.html
Copyright © 2011-2022 走看看