1.线程设置名字

建议对每个线程启动之前进行定义,不建议启动后和为不同线程设置重名

可以使用Thread类的下面的方法

  • 构造方法: public Thread(Runnable target, String name);
  • 设置名字: public final voidsetName(String name);
  • 取得名字: public final StringgetName();
  • 取得当前线程对象: public static Thread currentThread();

如果在实例化Thread类对象时候没有设置名字,那么会自动进行编号命名,保证不重复


2.线程的休眠

如果线程持有锁,sleep方法结束前并不会释放该锁
休眠方法

  1. public static void sleep(long millis) throws InterruptedException

调用Thread.sleep(1000)后,线程会被暂停,如果被interrupt,则会抛出InterruptedException异常

Thread.sleep(2000);

3.线程的优先级

优先级越高,越有可能先执行,只是大概率先执行,并不是一定
在Thread类提供

* 设置优先级: public final void setPriority(int newPriority);

* 取得优先级: public final int getPriority();

发现设置和取得优先级都是使用了int数据类型,对于此内容有三种取值:

* 最高优先级: public static final int MAX_PRIORITY;        //10

* 中等优先级: public static final int NORM_PRIORITY;        //5

* 最低优先级: public static final int MIN_PRIORITY;            //1

例子

Thread thread1 = new Thread(task1, "task1线程");
Thread thread2 = new Thread(task2, "task2线程");

thread1.setPriority(Thread.MAX_PRIORITY);
thread1.setPriority(Thread.MIN_PRIORITY);

thread1.start();
thread2.start();

主线程的优先级是多少?

public static void main(String[] args) {
    System.out.println(Thread.currentThread().getPriority());    //5,中等
}

4.yield休眠当前线程

休眠当前线程, 是线程编程可执行状态,以便其他相同优先级的线程有机会执行, 使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态里选择,也就是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程在下一次中不会执行到了。

注意: 再次执行调度程序决定的, 可以立刻会再次执行。。。

yield会释放资源锁吗?
yield不会释放资源所,当暂停后,等待调度程序再次调用,走完同步区域代码后才释放锁

package com.study;

public class Test01 implements Runnable {

    private String name;

    public Test01(String name) {
        this.name = name;
    }

    @Override
    public synchronized void run() {
        System.out.println(name + "-> start");

        for (int i = 0; i < 1000; i++) {
            if (i % 2 == 0) {
                Thread.yield();
            }
            System.out.println(Thread.currentThread().getName() + " --- i = " + (i));
        }
        System.out.println(name + "-> stop");
    }

    public static void main(String[] args) {
        Test01 a = new Test01("A");
        Thread thread1 = new Thread(a);
        Thread thread2 = new Thread(a);
        Thread thread3 = new Thread(a);

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

5.join线程之间的并行执行变为串行执行

join等待线程结束

# 等待线程执行结束,或者指定的最大等待时间到了,调用方线程再次被唤醒,如果最大等待时间为0,则只能等线程执行结束,才能被唤醒。
public final void join(long millis) throws InterruptedException

public final void join(long millis, int nanos) throws InterruptedException

public final void join() throws InterruptedException

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

join的意思是使得放弃当前线程的执行,并返回对应的线程

package com.study;

public class Test01 {

    public static void main(String[] args) throws InterruptedException {
        ThreadJoinTest t1 = new ThreadJoinTest("小明");
        ThreadJoinTest t2 = new ThreadJoinTest("小冬");
        t1.start();
        /**
         * 程序在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 < 10; i++) {
            System.out.println(this.getName() + "---" + i);
        }
    }
}

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

上面注释也大概说明了join方法的作用:在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行。

注意,这里调用的join方法是没有传参的,join方法其实也可以传递一个参数给它的,具体看下面的简单例子:

package com.study;

public class Test01 {

    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 < 10; 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方法实现原理

public class HighConcurrency {

    public static void main(String[] args) {
        try {
            Thread threadTest = new Thread(){
                public void run(){
                    System.out.println("执行线程中方法");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
            threadTest.start();
            synchronized(threadTest){
                threadTest.wait();        //当线程终止的时候,会调用线程自身的notifyAll()方法
            }
            System.out.println("执行到了这里");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出

执行线程中方法
执行到了这里

首先开始线程threadTest,在主线程执行到threadTest.wait()时,主线程会等待,奇怪的是主线程并没有别的线程使用notify或notifyAll方法唤醒,竟然直接执行了后面的语句”执行了这里”。

查阅发现如果synchronized获得对象锁是线程的实例时,此时比较特殊,当该线程终止的时候,会调用线程自身的notifyAll()方法,会通知所有等待在该线程对象上的线程

就是利用了线程实例做对象锁时,在线程执行完后,会调用线程自身的notifyAll()方法,此时主线程会接着执行,用处可以控制线程的执行顺序,例如我可以让子线程做计算,在子线程计算完后,在主线程中输出计算结果

有了上面的例子,我们大概知道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) {             //如果时执行的join(0)
            while (isAlive()) {        //如果线程是运行状态,就会执行下面的等待
                wait(0);               
            }
        } else {                       //如果是执行的join(time)
            while (isAlive()) {               //如果线程时运行状态
                long delay = millis - now;    
                if (delay <= 0) {
                    break;
                }
                wait(delay);                 //等待delay时间后自动返回继续执行
                now = System.currentTimeMillis() - base;
            }
        }
    }

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

核心代码

//如果线程是运行状态,就会执行下面的等待, this.isAlive(), 这个this就是调用的那个线程对象
while (isAlive()) {
    wait(0);               
}

6.isAlive是否处于活动状态

方法isAlive()功能是判断当前线程是否处于活动状态。

活动状态就是线程启动且尚未终止,比如正在运行或准备开始运行。

package com.study;

class IsAliveThread extends Thread {
    public IsAliveThread() {
        System.out.println("begin");
        System.out.println( Thread.currentThread().getName() + "- status -" + Thread.currentThread().isAlive());
        System.out.println("end");

    }

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

public class Test01 {

    public static void main(String[] args) {
        IsAliveThread ist = new IsAliveThread();
        Thread th = new Thread(ist);
        System.out.println("Main begin th isAlive = " + th.isAlive());
        th.start();
        System.out.println("Main end th isAlive = " + th.isAlive());
    }
}

3个线程按顺序执行

T3先执行,在T3的run中,调用t2.join,让t2执行完成后再执行t3

在T2的run中,调用t1.join,让t1执行完成后再让T2执行

package testThread;

import java.util.Date;

public class TestThread {
    //现在有T1、T2、T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行
    public static void main(String[] args) {
        //线程1
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("t1");
            }
        });

        //线程2
        final Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //引用1线程,等待t1线程执行完
                    t1.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("t2");
            }
        });

        //线程3
        final Thread t3 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    //引用2线程,等待t2线程执行完
                    t2.join();
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println("t3");
            }
        });

        //这3个线程启动没有先后顺序
        t3.start();
        t2.start();
        t1.start();

    }
}

7.中断

java.lang.Thread类有一个interrupt方法,该方法直接对线程调用。当被interrupt的线程正在sleep或wait时,会抛出InterruptedException异常。
事实上,interrupt方法只是改变目标线程的中断状态(interrupt status),而那些会抛出InterruptedException异常的方法,如wait、sleep、join等,都是在方法内部不断地检查中断状态的值。

  • interrupt方法

Thread实例方法:必须由其它线程获取被调用线程的实例后,进行调用。实际上,只是改变了被调用线程的内部中断状态;

  • Thread.interrupted方法

Thread类方法:必须在当前执行线程内调用,该方法返回当前线程的内部中断状态,然后清除中断状态(置为false) ;

  • isInterrupted方法

Thread实例方法:用来检查指定线程的中断状态。当线程为中断状态时,会返回true;否则返回false。

image.png