Java Multithreading 5: Instance Methods in Thread

Method invocation method in Thread class:fast to the end

Learning the methods in the Thread class is the first step in learning about multithreading. Before learning multi-threading, I made a special point. When calling the method in Thread, in the thread class (don't forget this precondition) , there are two ways, you must understand the difference between these two ways:

1、this.XXX()

The thread represented by this invocation is the thread instance itself

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

The above two spellings have the same meaning. The thread represented by this invocation is the thread that is executing the code block where Thread.currentThread.XXX() is located

Of course, with that said, there are bound to be people who don't understand the difference between the two. It doesn't matter, I will make it clear later, especially when it comes to the Thread constructor. After the explanation, go back and look at the above 2 points, which will deepen your understanding.

 

Instance methods in the Thread class

Explain the methods in Thread from the perspective of instance methods and class methods in the Thread class. This distinction also helps to understand the methods in multithreading. Instance methods are only linked to the instance thread (that is, the new thread) itself, and have nothing to do with which thread is currently running. Take a look at the instance methods in the Thread class:

1、start()

The role of the start() method is to tell the "thread planner" that the thread is ready to run, waiting for the CPU to call the thread object to execute the run() method, resulting in an asynchronous execution effect. One thing to note: the order in which the start() method is called does not represent the order in which the threads are started, and the order in which the threads are started is uncertain .

2、run()

The run() method is a method that is executed after the thread starts to run after the thread calls the start() method.

3、isAlive()

The role of the isAlive() method is to test whether the thread is alive

see a demo

public class MyThread extends Thread
{
    public void run()
    {
        System.out.println( "run = " + this .isAlive());   // this represents the mt thread (note the difference from Thread)
    }
}
public static void main(String[] args) throws Exception
{
    MyThread mt = new MyThread();
    System.out.println( "begin == " + mt.isAlive());   // It hasn't started yet, so it's false 
    mt.start(); 
Thread.sleep(
100 );   System.out.println( "end == " + mt.isAlive()); // The main thread slept for 100ms, the mt thread must have finished executing, so return false }

operation result

begin == false
run = true
end == false

From the results we can conclude that the method returns true as long as the thread is started and not terminated

4、getId()

This method is relatively simple, so I won't write an example. In a Java application, there is a long-type globally unique thread ID generator threadSeqNumber, which will be incremented every time a new thread comes out and given the tid attribute of the thread. This is done by Thread itself, and the user cannot execute a thread. Id of the thread.

5、getName()

This method is also relatively simple, and no examples are written. When we create a new thread, we can specify the name of the thread or not. If specified, then the name of the thread is specified by ourselves, and getName() returns the name of the thread specified by the developer; if not specified, then there is an int-type globally unique thread initial number generator threadInitNum in Thread, and Java first Increment threadInitNum and name the newly generated thread with "Thread-threadInitNum"

6、getPriority() 和 setPriority(int newPriority)

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

public class MyThread_01 extends Thread
{
    public void run()
    {
        System.out.println("MyThread_01 priority = " + this.getPriority());
    }
}
public class MyThread_02 extends Thread
{
    public void run()
    {
        System.out.println("MyThread_02 priority = " + this.getPriority());
        MyThread_01 myThread_01 = new MyThread_01();
        myThread_01.start();
    }
}
public static void main(String[] args)
{
    System.out.println("main thread, priority = " + Thread.currentThread().getPriority());
    MyThread_02 myThread_02 = new MyThread_02();
    myThread_02.start();
}

运行结果

main thread, priority = 5
MyThread_02 priority = 5
MyThread_01 priority = 5

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

下面的 Demo 演示了设置线程优先级带来的效果:

package com.tkz;

public class MyThread_01 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 static void main(String[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            MyThread_01 mt0 = new MyThread_01();
            mt0.setPriority(10);  // 在这里极端一点更能看出效果
            mt0.start();
            MyThread_02 mt1 = new MyThread_02();
            mt1.setPriority(1);
            mt1.start();
        }
    }
}

class MyThread_02 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));
    }
}

运行结果

◆◆◆◆◆ thread0 use time = 2
◇◇◇◇◇ thread1 use time = 1
◆◆◆◆◆ thread0 use time = 2
◆◆◆◆◆ thread0 use time = 0
◆◆◆◆◆ thread0 use time = 4
◆◆◆◆◆ thread0 use time = 4
◇◇◇◇◇ thread1 use time = 0
◇◇◇◇◇ thread1 use time = 2
◇◇◇◇◇ thread1 use time = 0
◇◇◇◇◇ thread1 use time = 10

从这个运行结果来看基本能得出结论:优先级越高的线程越能获取 CPU 资源

7、isDaeMon、setDaemon(boolean on)

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

public class MyThread 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
        {
            MyThread mt = new MyThread();
            mt.setDaemon(true);
            mt.start();
            
            Thread.sleep(5000); // main 线程在这里睡了 5 秒,那么 mt 作为守护线程在这 5s 内做了哪些事
            
            System.out.println("我离开thread对象再也不打印了,我停止了!");
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

运行结果

i = 1
i = 2
i = 3
i = 4
i = 5
main 线程即将运行结束了

8、interrupt()

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

public class TestThreadInterupt
{
    public static void main(String[] args)
    {
        try
        {
            MyThread mt = new MyThread();
            mt.start();
            Thread.sleep(1000); // main 线程睡了 2s
            
            mt.interrupt();
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

class MyThread extends Thread
{
    @Override
    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()方法之前要对 wait()/notify()/notifyAll() 机制已熟练掌握。

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

看一个 Demo

public class TestThreadInterupt
{
    public static void main(String[] args) throws Exception
    {
        MyThread mt = new MyThread();
            mt.start();
            mt.join();
            
            System.out.println("我是 "+Thread.currentThread().getName()+" 线程,当 mt 线程执行完毕之后我再执行");
    }
}

class MyThread extends Thread
{
    public void run()
    {
        try
        {
            int secondValue = (int)(Math.random() * 10000);
            System.out.println(secondValue);
            
            Thread.sleep(secondValue);
        } 
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}

运行结果

8241
我是 main 线程,当 mt 线程执行完毕之后我再执行

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) 而已:

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; } } }

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324794725&siteId=291194637