一、多线程[创建,interrupt,setDaemon,getPriority,isAlive等]

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MR_ChenT/article/details/84801131

一、多线程简介

在CPU上的线程,执行一个任务,宏观当然是同时执行的,但微观里的确实串行执行的,CPU通过线程中断,让某一个线程挂起来,然后切换到另一个线程,执行一会儿,再切换回来。但是线程切换来回会牺牲一定的性能,如果增加CPU那么线程是可以达到并行的。

  • 联想下我们去买电影票,先不说可以网上购买,那么们去买票人一多就要排队去, 还是得等待(这种性质就是同步)
    扯下皮:如果大家都不排队了,在你正在买票的时候,其他人进行插队把你的票抢走,觉得这个人神马素质嘛,有可能让他体验到被揍的感觉,然后售票厅一片狼藉混乱,…,无规矩不成方圆。这个规矩就是“互斥锁”,排队并且不允许插队一个个来。
  • 访问共享资源的时候要锁住线程,不能让其他人来抢劫。别人就只能等待了
  • 单线程:好比电影院只有一个窗口,假设电影快开始了,你要买3D眼镜,那么你就只能等待前面的人员买完票。
  • 多线程:多线程的话可以多开一个卖3D眼镜的窗口,如果有人排队可以处理完当前这个人的再去处理另外一个窗口,这样买3D眼镜的人也不用等那么久了。
  • 注意 :线程数如果大于物理CPU个数的时候,是可以充分利用CPU,提高性能的。但无限制的加大线程数会带来线程切换的开销,所以一个服务程序的最优线程数需要根据具体情况来具体评估

二、创建多线程的两种方式

1、继承Thread
public  class  JobRun extends Thread{
    @Override
    public  void run() {
        for(int i=0;i<5;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
public static void main(String[] agrs){
            JobRun jobRun=new JobRun();
            jobRun.start();
			//jobRun.start();再加一个就报错了,面试中有提到过
            for(int i=0;i<5;i++){
                System.out.println(Thread.currentThread().getName()+":"+i);
            }
        }
}

结果:
main:0
Thread-00
main:1
Thread-02

2、实现Runnable
public class JobRunnable implements Runnable {
    @Override
    public  void run() {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+i);
        }
    }
}
 public static void main(String[] agrs){
        JobRunnable jobRunnable=new JobRunnable();
        Thread t=new Thread(jobRunnable);
        t.start();
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
}

结果:
main:0
Thread-00

总结:大同小异,输出方式一样,但是java是单继承,所以尽量选择实现的方式。具体怎么运作,如果不清晰把上面的的概念再理解一下。

三、线程的属性与方法

1、执行顺序:

那么各个之间的执行顺序呢
代码示例:

@Override
public  void run() {
   System.out.println(Thread.currentThread().getName());
}
public static void main(String[] agrs){
   JobRun jobRun1=new JobRun();
   JobRun jobRun2=new JobRun();
   JobRun jobRun3=new JobRun();
   jobRun1.start();
   jobRun2.start();
   jobRun3.start();
   System.out.println(Thread.currentThread().getName()+"-Main-:");
}

结果: 有没有看到为什么不是0 2 1呢。
main-Main-:
Thread-0
Thread-2
Thread-1

总结:调用start()方法的顺序不代表线程启动的顺序,线程启动顺序具有不确定性

2、run

代码示例:

@Override
public  void run() {
   System.out.println(Thread.currentThread().getName());
}
public static void main(String[] agrs){
	JobRun jobRun1=new JobRun();
	JobRun jobRun2=new JobRun();
	JobRun jobRun3=new JobRun();
	jobRun1.run();
	jobRun2.run();
	jobRun3.run();
	System.out.println(Thread.currentThread().getName()+"-Main-:");
}

结果:
main
main
main
main-Main-:

总结:Thread实例run()方法里面的内容是没有任何异步效果的,全部被main函数执行。换句话说,只有run()而不调用start()启动线程是没有任何意义的。

  • 有没有感觉start有点像“线程规划器”
3、isAlive,getId,getName

代码示例:

扫描二维码关注公众号,回复: 4377601 查看本文章
public  class  JobRun extends Thread{
    @Override
    public  void run() {
        Long t1=System.currentTimeMillis();
        for (int i=0;i<100000000;i++){}
        Long t2=System.currentTimeMillis();
        System.out.println(this.getName()+"jobRun结束时间:"+t2+" \t执行的耗时:"+(t2-t1)+"\t 的优先级:"+this.getPriority());
    }
}
public static void main(String[] agrs){
	JobRun jobRun1=new JobRun();
	System.out.println("线程是否在运行 new :"+jobRun1.isAlive());
	System.out.println("new get Id :"+jobRun1.getId());
	System.out.println("new get Name :"+jobRun1.getName());
	jobRun1.start();
	System.out.println("线程是否在运行 start :"+jobRun1.isAlive());
	Thread.sleep(100);
	System.out.println("线程是否在运行 new :"+jobRun1.isAlive());
	System.out.println("new get Id :"+jobRun1.getId());
	System.out.println("new get Name :"+jobRun1.getName());
}

结果:
线程是否在运行 new :false
new get Id :12
new get Name :Thread-0
线程是否在运行 start :true
Thread-0
线程是否在运行 new :false
new get Id :12
new get Name :Thread-0

总结:

  • isAlive:只有在执行的时候是True,其余的都为false。
  • ID: 有一个long型的全局唯一的线程ID生成器threadSeqNumber,每new出来一个线程都会把这个自增一次,并赋予线程的tid属性,这个是Thread自己做的,用户无法执行一个线程的Id。
  • Name: 在new时,可以之定义线程的名字,getName()返回的也是自定义线程的名字;默认,Thread使用int型全局唯一的线程初始号生成器threadInitNum,自增后,以"Thread-threadInitNum"的方式来命名新生成的线程。
4、getPriority()和setPriority(int newPriority)

设置优先级有助于帮"线程规划器"确定下一次选择哪一个线程优先执行
两个在等待CPU的线程,优先级高的线程可能优先被CU选择执行

代码示例:

public  class JobRun_1 extends Thread{
    @Override
    public  void run() {
        Long t1=System.currentTimeMillis();
        for (int i=0;i<100000000;i++){}
        Long t2=System.currentTimeMillis();
        System.out.println(this.getName()+"jobRun_1结束时间:"+t2+" \tjobRun执行的耗时:"+(t2-t1)+"\t 的优先级:"+this.getPriority());
    }
}
public class JobRunnable implements Runnable {
    public void run() {
        Long t1 = System.currentTimeMillis();
        for (int i = 0; i < 1000; i++) {
        }
        Long t2 = System.currentTimeMillis();
        System.out.println("JobRunnable执行的耗时:" + (t1 - t2));
    }
}
public static void main(String[] agrs) {
        System.out.println("main的优先级:"+Thread.currentThread().getPriority());
		JobRun jobRun=new JobRun();
        jobRun.start();
		System.out.println("jobRun 的优先级是:"+jobRun.getPriority());
					
    for (int i =0;i<30;i++){
        JobRun jobRun=new JobRun();
        jobRun.setPriority(9);
        jobRun.start();

        JobRun_1 jobRun_1=new JobRun_1();
        jobRun_1.setPriority(1);
        jobRun_1.start();
    }
}

结果:
main的优先级:5
jobRunExtMain 的优先级是:5
Thread-59jobRun_1结束时间:1541743992074 jobRun执行的耗时:4 的优先级:1
Thread-57jobRun_1结束时间:1541743992092 jobRun执行的耗时:18 的优先级:1

Thread-42jobRun结束时间:1541743992391 执行的耗时:0 的优先级:9
Thread-38jobRun结束时间:1541743992432 执行的耗时:0 的优先级:9

Thread-1jobRun_1结束时间:1541743992969 jobRun执行的耗时:0 的优先级:1

__ 注意: __
默认执行级别是5,如果没有指定默认继承调用者的优先级,1-10,超过了这个值就报异常。
在测试过程中,将开发工具设置成单个CPU,现在的电脑应该都是超线程的并且多核,系统会识别成8个CPU,指定一个就好了。显示的效果会好一丢丢。

总结:线程的执行顺序跟“优先级”无关,优先级高CPU尽可能将资源分配给它。

5、isDaeMon、setDaemon(boolean on)

Java中有两种线程,用户线程,守护线程。
守护线程会随着用户线程而自动摧毁

代码示例:

public class JobRun_2  extends Thread {
    int i=0;
    @Override
    public  void run() {
        while(true){
            try {
                i++;
                Thread.sleep(1000);
                System.out.println("默默地输出.."+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public static void main(String[] agrs) throws InterruptedException {
        JobRun_2 jobRun_2=new JobRun_2();
        jobRun_2.setDaemon(true);
        jobRun_2.start();
        System.out.println("优先级:"+jobRun_2.getPriority());
        Thread.sleep(3000);
        System.out.println("我执行完了,守护线程不再执行...");
}

结果:
优先级:5
默默地输出…1
默默地输出…2
我执行完了,守护线程不再执行…

注意: setDaemon(true)必须在线程start()之前

总结:优先级不变,随着用户线程死完,那么它可以做什么么?
··守护线程是一种特殊的线程,它的作用是为其他线程的运行提供便利的服务,最典型的应用便是GC线程

6、interrupt()/isInterrupted

中断线程

代码示例:

 public int i=0;
    @Override
    public  void run() {
        while(i<10000000){
            try {
                System.out.println("有本事中断我啊,继续出招:"+Thread.currentThread().isInterrupted()+ ++i);
                Thread.sleep(500);
                System.out.println("出招完毕:"+Thread.currentThread().isInterrupted()+ i);
                //System.out.println("出招完毕:"+i);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
	
public static void main(String[] agrs) throws InterruptedException {
      JobRun_3 jobRun_3=new JobRun_3();
      jobRun_3.start();
      Thread.sleep(2000);
      jobRun_3.interrupt();
      System.out.println("知道我的厉害了吧,还不点赞!");
 }

__ 结果: __
有本事中断我啊,继续出招:false1
出招完毕:false1
有本事中断我啊,继续出招:false2
出招完毕:false2
有本事中断我啊,继续出招:false3
出招完毕:false3
有本事中断我啊,继续出招:false4
知道我的厉害了吧,还不点赞!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Doem_Thread.JobRun_3.run(JobRun_3.java:15)
有本事中断我啊,继续出招:false5
出招完毕:false5

总结:调用中断程序时,如果程序堵塞或者调用了Sleep是会抛出异常,但是不一定会停止。程序根本不鸟中断命令 哈哈

  • 那我们加强一下
    代码示例:
@Override
public void run() {
        while (i < 10000000) {
            if (Thread.currentThread().isInterrupted()) {
                System.out.println("命中要害...中断");
                break;
            }
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                this.interrupt();
                e.printStackTrace();
            }
            System.out.println("有本事中断我啊,继续出招:" + Thread.currentThread().isInterrupted() + ++i);
            System.out.println("出招完毕:" + Thread.currentThread().isInterrupted() + i);
        }
}
	
	main不改代码,运行..

结果:
出招完毕:false3
知道我的厉害了吧,还不点赞!
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at Doem_Thread.JobRun_3.run(JobRun_3.java:18)
有本事中断我啊,继续出招:true4
出招完毕:true4
命中要害…中断

原来要和isInterrupted 配合使用,不知道有没有留意到,如果加了sleep,isInterrupted一直都为false。
中断命令指示做了一个通知,sleep或堵塞的时候会抛出异常,那可以利用异常去通知到下一次是否该执行。

  • 扩展

1、多线程与超线程的区别?
相信大家以及理解了上面所讲的多线程了
超线程:CPU可以一次性执行多个不同线程技术,不需要来回切,
超线程技术会让系统以为有两块物理CPU,操作系统就会同时向那‘两’块CPU发送2个线程的任务,会让CPU尝试同时执行两个线程。主要看CPU支不支持超线程。

总结:两者的区别是:多线程是并发,超线程是并行

猜你喜欢

转载自blog.csdn.net/MR_ChenT/article/details/84801131
今日推荐