Java多线程 - Thread类中的几个方法

1. 实例变量与线程安全
自定义线程类中的实例变量针对其他线程有共享与不共享之分,这在多个线程之间交互时是一个很重要的技术点。
不共享数据的情况:每个线程都有各自的实例变量,多个线程交互时不影响各自的实例变量值,不存在线程安全问题。
共享数据的情况:多个线程访问同一个实例变量,多个线程交互时存在“非线程安全问题”。
“非线程安全”是指多个线程对同一个对象中的实例变量进行操作时值被更改、值不同步的情况,进而影响程序的执行流程。
解决“非线程安全”可以使用synchronized关键字。

2. i–或i++与System.out.println()的异常
看下面例子中,i++与println()联合使用时有可能出现的异常情况:

public class MyThread implements Runnable{

    private Integer count = 0;

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() 
                + " count = " + (count++));
    }

    public static void main(String[] args) {

        MyThread myThread = new MyThread();
        Thread thread1 = new Thread(myThread);
        Thread thread2 = new Thread(myThread);
        Thread thread3 = new Thread(myThread);
        Thread thread4 = new Thread(myThread);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }

}

打印结果:

Thread-0 count = 0
Thread-2 count = 3
Thread-3 count = 2
Thread-1 count = 1

而如果改变run()方法成这样:

@Override
public void run() {
    count++;
    System.out.println(Thread.currentThread().getName() 
            + " count = " + count);
}

则打印结果:

Thread-0 count = 1
Thread-3 count = 3
Thread-2 count = 2
Thread-1 count = 4

由上面实验可知:虽然println方法内部是同步的,但是i++操作是在进入println方法之前发生的,所以有发生非线程安全的概率。

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

而i++操作是非原子的,在某些JVM中,i++要分为三步:
a. 取得原有i的值;
b. 计算i + 1;
c. 对i进行赋值;
这三步中,如果有多个线程访问,那么一定会出现非线程安全问题。

为了线程安全,还是需要使用同步方法。

3. currentThread()方法
currentThread()方法可以返回代码段正在被哪个线程调用的信息。

public class MyThread5 extends Thread{

    public MyThread5() {
        System.out.println("construction begin");
        System.out.println("Thread.currentThread().getName():"
                + Thread.currentThread().getName());
        System.out.println("this.getName():" + this.getName());
        System.out.println("contruction end");
    }

    @Override
    public void run() {
        System.out.println("run begin");
        System.out.println("Thread.currentThread().getName():"
                + Thread.currentThread().getName());
        System.out.println("this.getName():" + this.getName());
        System.out.println("run end");
    }

    public static void main(String[] args) {

        MyThread5 myThread5 = new MyThread5();
        Thread thread = new Thread(myThread5);
        thread.setName("A");

        thread.start();
    }
}

输出结果:

construction begin
Thread.currentThread().getName():main
this.getName():Thread-0
contruction end
run begin
Thread.currentThread().getName():A
this.getName():Thread-0
run end

这里的this指该线程类的实例,改线程类继承Thread类,没有对Name重命名,所以在run()方法中通过this.getName()输出的是默认名称:Thread-0。
注意:如果将main方法中创建线程并启动的过程改成下面这种,那么run()方法中的this就代表当前线程:

public static void main(String[] args) {

    MyThread5 myThread5 = new MyThread5();

    /*Thread thread = new Thread(myThread5);
    thread.setName("A");

    thread.start();*/

    myThread5.setName("A");
    myThread5.start();
}

输出结果:

construction begin
Thread.currentThread().getName():main
this.getName():Thread-0
contruction end
run begin
Thread.currentThread().getName():A
this.getName():A // 区别
run end

4. isAlive()
isAlive()用于判断当前线程是否处于活动状态。活动状态是指线程已经启动且尚未终止。线程处于运行或者就绪状态,就认为线程是“存活”的。
看下面例子通过isAlive()方法区分this和Thread.currentThread()。

public class MyThread extends Thread{

    public MyThread() {
        System.out.println("构造方法 begin");
        System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive():" + Thread.currentThread().isAlive());

        System.out.println("this.getName():" + this.getName());
        System.out.println("this.isAlive():" + this.isAlive());
        System.out.println("构造方法 end");
    }

    @Override
    public void run() {
        System.out.println("run begin");
        System.out.println("Thread.currentThread().getName():" + Thread.currentThread().getName());
        System.out.println("Thread.currentThread().isAlive():" + Thread.currentThread().isAlive());

        System.out.println("this.getName():" + this.getName());
        System.out.println("this.isAlive():" + this.isAlive());
        System.out.println("run end");
    }

    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        thread.setName("A");
        thread.start();
    }
}

输出结果:

构造方法 begin
Thread.currentThread().getName():main
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
构造方法 end
run begin
Thread.currentThread().getName():A
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
run end

如果main方法改成如下:

public static void main(String[] args) {
    MyThread myThread = new MyThread();
    myThread.setName("A");
    myThread.start();
}

输出结果:

构造方法 begin
Thread.currentThread().getName():main
Thread.currentThread().isAlive():true
this.getName():Thread-0
this.isAlive():false
构造方法 end
run begin
Thread.currentThread().getName():A
Thread.currentThread().isAlive():true
this.getName():A // 区别
this.isAlive():true // 区别
run end

将线程对象以构造参数的方式传递给Thread对象进行start()启动线程,我们直接启动的线程是thread,而作为构造参数的myThread,赋值给Thread对象的target,之后通过thread的run方法调用target.run()。此时Thread.currentThread是Thread的引用thread,而this则表示myThread。

5. sleep()
sleep()的作用是在指定毫秒时间内,让当前正在执行的线程休眠(暂停执行)。这个正在执行的线程是指this.currentThread()返回的线程(Thread.currentThread())。

6. getId()
getId()方法的作用的取的线程的唯一标识。比如thread-0的0。

7. yield()
yield()方法的作用是放弃当前的CPU资源,将它让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

上述内容总结自《Java多线程编程核心技术》一书。

你可能感兴趣的:(java多线程)