Java多线程——线程的状态和线程操作的相关方法

1、线程状态

线程状态:6种

  • NEW 新建状态
  • RUNNABLE 可运行状态(就绪状态)
  • RUNNING 运行状态
  • BLOCKED 阻塞状态
  • WAITING 等待状态
  • TERMINATED 终止状态(死亡状态)

线程的状态转换示意图:
这里写图片描述

2、线程操作的相关方法

  • getName() 获取线程名称 setName()是设置线程名称
  • getId() 获取线程ID
  • getPriority() 获取线程优先级, Java线程优先级10个等级,最小的是1,默认值是5,最大的是10,优先级越高被优先调用的概率最高
  • run() 线程Thread的重要方法
  • start() 创建一个线程,新线程操作执行体,调用run()
  • sleep() 线程休息,
  • join() 加入线程
  • yield() 暂停当前线程,交出CPU的使用权,其他线程可被调度,可被调度的线程优先级要大于或等于当前线程,线程并不一定会停止执行,有可能再次获取CPU的调度
  • wait() 等待状态,是当前线程等待,并让该线程释放对象锁,放进对象的等待池中,等待其他线程将其唤醒。
  • notify() 通知方法,唤醒等待池中的线程,(随机选择一个唤醒)
  • notifyAll() 通知方法,唤醒等待池中所有线程
class ThreadDemo implements Runnable {
    public void run() {
        for(int i = 1;i <= 5;i++) {
            System.out.println("当前运行线程:"+Thread.currentThread().getName()
                    +",i="+i+",活动线程的数目:"+Thread.activeCount());
        }

    }
}
public class TestDemo6 {
    /**
     * 获取和设置线程名称
     */
    private static void gain() {
        System.out.println("获取和设置线程名称");
        ThreadDemo tm = new ThreadDemo();
        Thread t1 = new Thread(tm);     //系统自动命名
        Thread t2 = new Thread(tm,"线程-1");  //自己设置线程名称
        Thread t3 = new Thread(tm);   
        t3.setName("线程-2");   //为t3设置线程名称
        System.out.println("活动线程数目:"+Thread.activeCount()); //获取活动线程的数目
        t1.start();
        t2.start();
        t3.start();
    }
    /**
     * 设置线程优先级
     */
    private static void install() {
        System.out.println("===========================");
        ThreadDemo tm = new ThreadDemo();
        Thread t1 = new Thread(tm,"线程-1");
        Thread t2 = new Thread(tm,"线程-2");
        Thread t3 = new Thread(tm,"线程-3");
        Thread t4 = new Thread(tm,"线程-4");
        t1.setPriority(Thread.MIN_PRIORITY); //设置线程-1的优先级最低
        t2.setPriority(2);                    //设置线程-2的优先级为2
        t3.setPriority(Thread.NORM_PRIORITY); //设置线程-3的优先级为默认5
        t4.setPriority(Thread.MAX_PRIORITY);  //设置线程-4的优先级最高10
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

    public static void main(String[] args) {
        //gain();
        install();

    }
}

运行gain()方法:

获取和设置线程名称
活动线程数目:1
当前运行线程:Thread-0,i=1,活动线程的数目:3
当前运行线程:线程-1,i=1,活动线程的数目:3
当前运行线程:Thread-0,i=2,活动线程的数目:4
当前运行线程:线程-1,i=2,活动线程的数目:4
当前运行线程:Thread-0,i=3,活动线程的数目:4
当前运行线程:线程-1,i=3,活动线程的数目:4
当前运行线程:Thread-0,i=4,活动线程的数目:4
当前运行线程:Thread-0,i=5,活动线程的数目:4
当前运行线程:线程-1,i=4,活动线程的数目:3
当前运行线程:线程-1,i=5,活动线程的数目:3
当前运行线程:线程-2,i=1,活动线程的数目:3
当前运行线程:线程-2,i=2,活动线程的数目:2
当前运行线程:线程-2,i=3,活动线程的数目:2
当前运行线程:线程-2,i=4,活动线程的数目:2
当前运行线程:线程-2,i=5,活动线程的数目:2

运行install()方法:

===========================
当前运行线程:线程-3,i=1,活动线程的数目:4
当前运行线程:线程-1,i=1,活动线程的数目:5
当前运行线程:线程-3,i=2,活动线程的数目:5
当前运行线程:线程-4,i=1,活动线程的数目:5
当前运行线程:线程-3,i=3,活动线程的数目:5
当前运行线程:线程-2,i=1,活动线程的数目:5
当前运行线程:线程-1,i=2,活动线程的数目:5
当前运行线程:线程-2,i=2,活动线程的数目:5
当前运行线程:线程-3,i=4,活动线程的数目:5
当前运行线程:线程-4,i=2,活动线程的数目:5
当前运行线程:线程-3,i=5,活动线程的数目:5
当前运行线程:线程-2,i=3,活动线程的数目:5
当前运行线程:线程-2,i=4,活动线程的数目:4
当前运行线程:线程-2,i=5,活动线程的数目:4
当前运行线程:线程-1,i=3,活动线程的数目:5
当前运行线程:线程-4,i=3,活动线程的数目:5
当前运行线程:线程-4,i=4,活动线程的数目:3
当前运行线程:线程-4,i=5,活动线程的数目:3
当前运行线程:线程-1,i=4,活动线程的数目:3
当前运行线程:线程-1,i=5,活动线程的数目:2

线程休眠:

  • 当线程休眠结束后,该线程并不会立即进入运行状态,而是进入就绪状态,等待JVM为他分配内存
  • 当线程处于休眠状态,如果该线程被中断,则会抛出InterruptedException异常,中断线程可以使用Thread类提供的interrupt()方法
  • sleep()是静态方法,在主方法内,无论是通过线程对象去调用sleep()还是直接通过Thread.sleep()形式去调用sleep()方法,都是休眠主线程。若想要线程实例休眠,sleep()方法应该放在run()方法内
class ThreadDemo1 implements Runnable {
    public void run() {
        for(int i = 1;i <= 5;i++) {
            System.out.println("当前运行线程:"+Thread.currentThread().getName()
                    +",i="+i+",时间:"+(new Date()));
            try {
                Thread.sleep(1000);   //线程休眠1s
            }catch(InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}
public class TestDemo7 {

    public static void main(String[] args) {
        ThreadDemo1 tm = new ThreadDemo1();
        Thread t1 = new Thread(tm,"线程-1");
        Thread t2 = new Thread(tm,"线程-2");
        Thread t3 = new Thread(tm,"线程-3");
        t1.start();
        t2.start();
        t3.start();
    }

}

运行结果:

当前运行线程:线程-2,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-3,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-1,i=1,时间:Tue Aug 07 19:10:23 CST 2018
当前运行线程:线程-2,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-1,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-3,i=2,时间:Tue Aug 07 19:10:24 CST 2018
当前运行线程:线程-2,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-3,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-1,i=3,时间:Tue Aug 07 19:10:25 CST 2018
当前运行线程:线程-1,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-2,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-3,i=4,时间:Tue Aug 07 19:10:26 CST 2018
当前运行线程:线程-1,i=5,时间:Tue Aug 07 19:10:27 CST 2018
当前运行线程:线程-2,i=5,时间:Tue Aug 07 19:10:27 CST 2018
当前运行线程:线程-3,i=5,时间:Tue Aug 07 19:10:27 CST 2018

线程的加入:
假设在当前运行线程A中调用线程B的join()方法:

  • 如果没有在join()方法中指定长度,则线程A会等到线程B运行结束后,才从阻塞状态转变为就绪状态,等待获取CPU
  • 如果在join()方法中指定了长度,线程B没有运行结束,则线程A也会在时间结束时,从阻塞状态转变为就绪状态。
  • 如果在join()方法中指定了长度,且时间长度超过线程B的运行时间,则线程A会等到线程B运行结束后,从阻塞状态转变为就绪状态
class ThreadDemo2 implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000);   //线程休眠1s
        }catch(InterruptedException e) {
            e.printStackTrace();
        }
        for(int i = 1;i <= 5;i++) {
            System.out.println("当前运行线程:"+Thread.currentThread().getName()
                    +",i="+i+",时间:"+(new Date()));

        }

    }
}
public class TestDemo8 {


    public static void main(String[] args) {
        ThreadDemo2 tm = new ThreadDemo2();
        Thread t1 = new Thread(tm,"线程-1");
        Thread main = Thread.currentThread();
        t1.start();
        for(int i = 0;i < 10;i++) {
            if(i == 5) {
                try {
                    t1.join(3000);
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
            System.out.println("main="+i+"-->"+new Date());
        }

    }

}

运行结果:

main=0-->Tue Aug 07 19:21:42 CST 2018
main=1-->Tue Aug 07 19:21:43 CST 2018
main=2-->Tue Aug 07 19:21:43 CST 2018
main=3-->Tue Aug 07 19:21:43 CST 2018
main=4-->Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=1,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=2,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=3,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=4,时间:Tue Aug 07 19:21:43 CST 2018
当前运行线程:线程-1,i=5,时间:Tue Aug 07 19:21:43 CST 2018
main=5-->Tue Aug 07 19:21:43 CST 2018
main=6-->Tue Aug 07 19:21:43 CST 2018
main=7-->Tue Aug 07 19:21:43 CST 2018
main=8-->Tue Aug 07 19:21:43 CST 2018
main=9-->Tue Aug 07 19:21:43 CST 2018

线程的唤醒:

  • 线程的唤醒是指线程从休眠的阻塞状态转换为运行状态,可以通过Thread类的interrupt()方法
  • isInterrupted():这是一个实例方法,用于判断当前线程是否已经被中断,是,返回true,否则返回false,线程中的中断状态不受该方法的影响。
  • interrupted():这是一个静态方法,用于判断执行中的线程是否已经被中断,若是,返回true,否则发挥false,调用此方法后,线程的中断状态将被取消,即处于中断状态的线程将变为非中断状态
class ThreadDemo3 implements Runnable {
    public void run() {
        System.out.println("开始运行run()方法:"+new Date());
        try {
            Thread.sleep(5000);
        }catch(Exception e) {
            System.out.println("线程已经被唤醒!");
        }
        System.out.println("结束运行run()方法:"+new Date());
    }
}
public class TestDemo9 {


    public static void main(String[] args) {
        ThreadDemo3 tm = new ThreadDemo3();
        Thread t1 = new Thread(tm,"线程-1");
        t1.start();
        try {
            t1.sleep(2000);  //线程休眠
        }catch(Exception e) {
            e.printStackTrace();
        }
        t1.interrupt();   //线程唤醒
    }

}

运行结果:

开始运行run()方法:Tue Aug 07 19:38:51 CST 2018
线程已经被唤醒!
结束运行run()方法:Tue Aug 07 19:38:53 CST 2018

后台线程:
如要将一个线程设置为后台线程,必须要在该线程启动前调用setDaemon()方法,设置后台线程后,还可以通过isDaemon()方法判断该程序是否已经成为后台线程

线程的礼让:
可以用yield()方法来实现,该方法只能把运行权让出来,不能让给指定线程,即yield()方法让出运行权后,哪个线程抢占到,哪个线程就运行

class ThreadDemo4 implements Runnable {
    public void run() {
        for(int i = 1;i <= 10;i++) {
            System.out.println("当前运行的线程:"+Thread.currentThread().getName()
                    +",i="+i);
            Thread.currentThread().yield();
        }

    }
}

class ThreadDemo5 implements Runnable {
    public void run() {
        for(int i = 1;i <= 10;i++) {
            System.out.println("当前运行的线程:"+Thread.currentThread().getName()
                    +",i"+i);
            }
        }
}


public class TestDemo10 {

    public static void main(String[] args) {
        ThreadDemo4 tm1 = new ThreadDemo4();
        ThreadDemo5 tm2 = new ThreadDemo5();
        Thread t1 = new Thread(tm1,"线程-1");
        Thread t2 = new Thread(tm2,"线程-2");
        t1.start();
        t2.start();

    }

}

运行结果:
t1 线程运行的几率比t2线程运行的几率要小,因为t1线程总是让其他线程先执行

当前运行的线程:线程-2,i1
当前运行的线程:线程-2,i2
当前运行的线程:线程-1,i=1
当前运行的线程:线程-2,i3
当前运行的线程:线程-1,i=2
当前运行的线程:线程-2,i4
当前运行的线程:线程-1,i=3
当前运行的线程:线程-2,i5
当前运行的线程:线程-1,i=4
当前运行的线程:线程-2,i6
当前运行的线程:线程-2,i7
当前运行的线程:线程-1,i=5
当前运行的线程:线程-2,i8
当前运行的线程:线程-1,i=6
当前运行的线程:线程-2,i9
当前运行的线程:线程-1,i=7
当前运行的线程:线程-2,i10
当前运行的线程:线程-1,i=8
当前运行的线程:线程-1,i=9
当前运行的线程:线程-1,i=10

sleep()和yield()都可以让出运行权,但他们还是有很大的不同

  • sleep()方法让出运行权时,不考虑其他线程的优先级;而yield()方法将运行权让给与其具有相同优先级或比其优先级更高的线程
  • 调用sleep()方法会让当前线程转到阻塞状态;而调用yield()方法则将将当前线程转到就绪状态
  • sleep()方法声明抛出InterruptedException异常;而yield方法抛出任何异常
  • sleep()方法比yield()方法具有更好的移植性,不能依靠yield()方法来提高程序的并发性

猜你喜欢

转载自blog.csdn.net/QQ2899349953/article/details/81487729