• 2.Thread中的实例方法


    (转自:http://www.cnblogs.com/xrq730/p/4851233.html)

    Thread类中的方法调用方式:

    1.this.XXX

    这种调用方式表示的线程是:线程实例本身

    2.Thread.currentThread.XXX() 或者Thread.XXX()

    上面这两种写法一样,表示的线程是正在执行Thread.currentThread.XXX()所在代码块的线程

    Thread类中的实例方法

    从Thread类中的实例方法和类方法的角度讲解Thread中的方法,这种区分的角度也有助于理解多线程中的方法。实例方法,只和实例线程(也就是new出来的线程)本身挂钩,和当前运行的是哪个线程无关。

    1.start()

    通知”线程规划器“此线程可以运行了。正在等待CPU调用线程对象的run()方法,产生一个异步执行的效果

     1 public class Thread01 extends Thread {
     2     public void run() {
     3         for (int i = 0; i < 3; i++) {
     4             try {
     5                 Thread.sleep((int) Math.random() * 1000);
     6             } catch (InterruptedException e) {
     7                 // TODO Auto-generated catch block
     8                 e.printStackTrace();
     9             }
    10             System.out.println("run = " + Thread.currentThread().getName());
    11         }
    12     }
    13 
    14     public static void main(String[] args) {
    15         Thread01 thread01 = new Thread01();
    16         thread01.start();
    17 
    18         for (int i = 0; i < 3; i++) {
    19             try {
    20                 Thread.sleep((int) Math.random() * 1000);
    21             } catch (InterruptedException e) {
    22                 // TODO Auto-generated catch block
    23                 e.printStackTrace();
    24             }
    25             System.out.println("run = " + Thread.currentThread().getName());
    26         }
    27     }
    28 
    29 }

    运行结果:

    run = Thread-0
    run = main
    run = Thread-0
    run = main
    run = Thread-0
    run = main

    结果表明:CPU执行哪个线程的代码具有不确定性

     1 public class Thread01 extends Thread {
     2     public void run() {
     3         for (int i = 0; i < 3; i++) {
     4             try {
     5                 Thread.sleep((int) Math.random() * 1000);
     6             } catch (InterruptedException e) {
     7                 // TODO Auto-generated catch block
     8                 e.printStackTrace();
     9             }
    10             System.out.println("run = " + Thread.currentThread().getName());
    11         }
    12     }
    13 
    14     public static void main(String[] args) {
    15         Thread01 thread01 = new Thread01();
    16         thread01.run();//将start()换为run()则不启动新的线程,执行的还是主方法
    17 
    18         for (int i = 0; i < 3; i++) {
    19             try {
    20                 Thread.sleep((int) Math.random() * 1000);
    21             } catch (InterruptedException e) {
    22                 // TODO Auto-generated catch block
    23                 e.printStackTrace();
    24             }
    25             System.out.println("run = " + Thread.currentThread().getName());
    26         }
    27     }
    28 
    29 }

    结果:

    run = main
    run = main
    run = main
    run = main
    run = main
    run = main

    这里就是很明显看出来start()是线程的启动入口。另外需要注意的是:调用start()方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性

    3、isAlive()

    测试线程是否处于活动状态,只要线程启动且没有终止,方法返回的就是true。看一下例子:

    复制代码
    public class MyThread06 extends Thread
    {
        public void run()
        {
            System.out.println("run = " + this.isAlive());
        }
    }
    复制代码
    复制代码
    public static void main(String[] args) throws Exception
    {
        MyThread06 mt = new MyThread06();
        System.out.println("begin == " + mt.isAlive());
        mt.start();
        Thread.sleep(100);
        System.out.println("end == " + mt.isAlive());
    }
    复制代码

    看下运行结果:

    begin == false
    run = true
    end == false

    看到在start()之前,线程的isAlive是false,start()之后就是true了。main函数中加上Thread.sleep(100)的原因是为了确保Thread06的run()方法中的代码执行完,否则有可能end这里打印出来的是true,有兴趣可以自己试验一下。

    4、getId()

    这个方法比较简单,就不写例子了。在一个Java应用中,有一个long型的全局唯一的线程ID生成器threadSeqNumber,每new出来一个线程都会把这个自增一次,并赋予线程的tid属性,这个是Thread自己做的,用户无法执行一个线程的Id。

    5、getName()

    这个方法也比较简单,也不写例子了。我们new一个线程的时候,可以指定该线程的名字,也可以不指定。如果指定,那么线程的名字就是我们自己指定的,getName()返回的也是开发者指定的线程的名字;如果不指定,那么Thread中有一个int型全局唯一的线程初始号生成器threadInitNum,Java先把threadInitNum自增,然后以"Thread-threadInitNum"的方式来命名新生成的线程

    6、getPriority()和setPriority(int newPriority)

    这两个方法用于获取和设置线程的优先级,优先级高的CPU得到的CPU资源比较多,设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行。换句话说,两个在等待CPU的线程,优先级高的线程越容易被CU选择执行。下面来看一下例子,并得出几个结论:

    复制代码
    public class MyThread09_0 extends Thread
    {
        public void run()
        {
            System.out.println("MyThread9_0 run priority = " + 
                    this.getPriority());
        }
    }
    复制代码
    复制代码
    public class MyThread09_1 extends Thread
    {
        public void run()
        {
            System.out.println("MyThread9_1 run priority = " + 
                    this.getPriority());
            MyThread09_0 thread = new MyThread09_0();
            thread.start();
        }
    }
    复制代码
    复制代码
    public static void main(String[] args)
    {
        System.out.println("main thread begin, priority = " + 
                Thread.currentThread().getPriority());
        System.out.println("main thread end, priority = " + 
                Thread.currentThread().getPriority());
        MyThread09_1 thread = new MyThread09_1();
        thread.start();
    }
    复制代码

    看一下运行结果:

    main thread begin, priority = 5
    main thread end, priority = 5
    MyThread9_1 run priority = 5
    MyThread9_0 run priority = 5

    从这个例子我们得出结论:线程默认优先级为5,如果不手动指定,那么线程优先级具有继承性,比如线程A启动线程B,那么线程B的优先级和线程A的优先级相同。下面的例子演示了设置线程优先级带来的效果:

    复制代码
    public class MyThread10_0 extends Thread
    {
        public void run()
        {
            long beginTime = System.currentTimeMillis();
            for (int j = 0; j < 100000; j++){}
            long endTime = System.currentTimeMillis();
            System.out.println("◆◆◆◆◆ thread0 use time = " + 
                    (endTime - beginTime));
        }
    }
    复制代码
    复制代码
    public class MyThread10_1 extends Thread
    {
        public void run()
        {
            long beginTime = System.currentTimeMillis();
            for (int j = 0; j < 100000; j++){}
            long endTime = System.currentTimeMillis();
            System.out.println("◇◇◇◇◇ thread1 use time = " + 
                    (endTime - beginTime));
        }
    }
    复制代码
    复制代码
    public static void main(String[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            MyThread10_0 mt0 = new MyThread10_0();
            mt0.setPriority(5);
            mt0.start();
            MyThread10_1 mt1 = new MyThread10_1();
            mt1.setPriority(4);
            mt1.start();
        }
    }
    复制代码

    看下运行结果:

    复制代码
    ◆◆◆◆◆ thread0 use time = 0
    ◆◆◆◆◆ thread0 use time = 0
    ◆◆◆◆◆ thread0 use time = 1
    ◆◆◆◆◆ thread0 use time = 2
    ◆◆◆◆◆ thread0 use time = 2
    ◇◇◇◇◇ thread1 use time = 0
    ◇◇◇◇◇ thread1 use time = 1
    ◇◇◇◇◇ thread1 use time = 5
    ◇◇◇◇◇ thread1 use time = 2
    ◇◇◇◇◇ thread1 use time = 0
    复制代码

    看到黑色菱形(线程优先级高的)先打印完,再多试几次也是一样的。为了产生一个对比效果,把yMyThread10_0的优先级设置为4,看下运行结果:

    复制代码
    ◆◆◆◆◆ thread0 use time = 0
    ◇◇◇◇◇ thread1 use time = 1
    ◇◇◇◇◇ thread1 use time = 1
    ◆◆◆◆◆ thread0 use time = 0
    ◇◇◇◇◇ thread1 use time = 0
    ◆◆◆◆◆ thread0 use time = 1
    ◆◆◆◆◆ thread0 use time = 1
    ◇◇◇◇◇ thread1 use time = 2
    ◇◇◇◇◇ thread1 use time = 1
    ◆◆◆◆◆ thread0 use time = 0
    复制代码

    看到,马上差别就出来了。从这个例子我们得出结论:CPU会尽量将执行资源让给优先级比较高的线程

    7、isDaeMon、setDaemon(boolean on)

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

    复制代码
    public class MyThread11 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
            {
                MyThread11 mt = new MyThread11();
                mt.setDaemon(true);
                mt.start();
                Thread.sleep(5000);
                System.out.println("我离开thread对象再也不打印了,我停止了!");
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    复制代码

    看一下运行结果:

    复制代码
     1 i = 1
     2 i = 2
     3 i = 3
     4 i = 4
     5 i = 5
     6 我离开thread对象再也不打印了,我停止了!
     7 i = 6
    复制代码

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

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

    8、interrupt()

    这是一个有点误导性的名字,实际上Thread类的interrupt()方法无法中断线程。看一下例子:

    复制代码
    public class TestMain12_0
    {
        public static void main(String[] args)
        {
            try
            {
                MyThread12 mt = new MyThread12();
                mt.start();
                Thread.sleep(2000);
                mt.interrupt();
            } 
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    复制代码
    复制代码
    public void run()
    {
        for (int i = 0; i < 500000; i++)
        {
            System.out.println("i = " + (i + 1));
        }
    }
    复制代码

    看下运行结果:

    复制代码
    ...
    i = 499995 i = 499996 i = 499997 i = 499998 i = 499999 i = 500000
    复制代码

    看结果还是打印到了50000。也就是说,尽管调用了interrupt()方法,但是线程并没有停止。interrupt()方法的作用实际上是:在线程受到阻塞时抛出一个中断信号,这样线程就得以退出阻塞状态。换句话说,没有被阻塞的线程,调用interrupt()方法是不起作用的。关于这个会在之后讲中断机制的时候,专门写一篇文章讲解。

    9、isInterrupted()

    测试线程是否已经中断,但不清除状态标识。这个和interrupt()方法一样,在后面讲中断机制的文章中专门会讲到。

    10、join()

    讲解join()方法之前请确保对于http://www.cnblogs.com/xrq730/p/4853932.html一文,即wait()/notify()/notifyAll()机制已熟练掌握。

    join()方法的作用是等待线程销毁。join()方法反应的是一个很现实的问题,比如main线程的执行时间是1s,子线程的执行时间是10s,但是主线程依赖子线程执行完的结果,这时怎么办?可以像生产者/消费者模型一样,搞一个缓冲区,子线程执行完把数据放在缓冲区中,通知main线程,main线程去拿,这样就不会浪费main线程的时间了。另外一种方法,就是join()了。先看一下例子:

    复制代码
    public class MyThread36 extends Thread
    {
        public void run()
        {
            try
            {
                int secondValue = (int)(Math.random() * 10000);
                System.out.println(secondValue);
                Thread.sleep(secondValue);
            } 
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    复制代码
    复制代码
    public static void main(String[] args) throws Exception
    {
        MyThread36 mt = new MyThread36();
        mt.start();
        mt.join();
        System.out.println("我想当mt对象执行完毕之后我再执行,我做到了");
    }
    复制代码

    看一下运行结果:

    3111
    我想当mt对象执行完毕之后我再执行,我做到了

    如果把mt.join()注释掉的话,执行结果就成了:

    我想当thread13对象执行完之后再执行,我做到了。
    878

    意思是,join()方法会使调用join()方法的线程(也就是mt线程)所在的线程(也就是main线程)无限阻塞,直到调用join()方法的线程销毁为止,此例中main线程就会无限期阻塞直到mt的run()方法执行完毕。

    join()方法的一个重点是要区分出和sleep()方法的区别。join(2000)也是可以的,表示调用join()方法所在的线程最多等待2000ms,两者的区别在于:

    sleep(2000)不释放锁,join(2000)释放锁,因为join()方法内部使用的是wait(),因此会释放锁。看一下join(2000)的源码就知道了,join()其实和join(2000)一样,无非是join(0)而已:

    复制代码
     1 public final synchronized void join(long millis) 
     2     throws InterruptedException {
     3     long base = System.currentTimeMillis();
     4     long now = 0;
     5 
     6     if (millis < 0) {
     7             throw new IllegalArgumentException("timeout value is negative");
     8     }
     9 
    10     if (millis == 0) {
    11         while (isAlive()) {
    12         wait(0);
    13         }
    14     } else {
    15         while (isAlive()) {
    16         long delay = millis - now;
    17         if (delay <= 0) {
    18             break;
    19         }
    20         wait(delay);
    21         now = System.currentTimeMillis() - base;
    22         }
    23     }
    24     }
    复制代码
     
     
  • 相关阅读:
    css动画集合地址
    邮箱正则
    好用的工具之一 ---- Sublime Text
    组件化表单解决方案AForm 1.3 发布
    WinScp几个极大提高开发效率的小功能
    session的本质及如何实现共享?
    使用phantomjs操作DOM并对页面进行截图需要注意的几个问题
    Ubuntu 12.04 安装最新版本NodeJS
    IIS 8 nodejs + iisnode 配置
    Bagging和Boosting的介绍及对比
  • 原文地址:https://www.cnblogs.com/leehfly/p/4972434.html
Copyright © 2020-2023  润新知