线程大乱记

线程大乱记,第1张

线程大乱记

线程大乱记
    • 什么是程序
    • 什么是线程
    • 单核cup设定多线程有意义吗?
    • 工作线程数是不是设置的越大越好
    • 线程池中的线程数量设置多少合适?
    • 线程状态转换
    • 线程的打断
    • 优雅的结束线程

什么是程序

程序是一个落到磁盘上的一个可执行文件,鼠标双击这个文件可以使这个文件运行。比如QQ,每次双击的时候都可以打开一个新的登陆页面,这些登陆页面就相当于一个一个进程。

什么是线程

专业角度来讲,进程叫做资源分配的基本单位,线程在进程的内部,叫做调度执行的基本单位,多个线程共享同一个进程里面的资源。

单核cup设定多线程有意义吗?

答:有意义。因为有一些线程在执行过程中 可能在等在资源 或者 sleep,此时等待的过程中是不消耗cpu算力的,切换到其他线程执行的时候使用cpu算力,才能更好的压榨cpu。

工作线程数是不是设置的越大越好

并不是,因为线程之间切换也是需要消耗资源的,

线程池中的线程数量设置多少合适?

线程数= 核心线程数 * 期望里忧虑 * (1+等待时间/计算时间)

//获取cpu核心数
int cpuCoreCount = Runtime.getRuntime().availableProcessors();
//计算一个等待时间和计算时间比率各占一半, 期望cpu利用率达到90%的线程数量
int threadCount=cpuCoreCount*90/100*(1+50/50);

如何知道等待时间 和 计算时间? 有工具

线程状态转换

NEW、RUNNABLE、TERMINATED 状态演示

        Thread t1=new Thread(() -> {
            System.out.println("2.t1执行start方法后,此时的状态为:"+Thread.currentThread().getState());
            for (int i = 0; i < 3; i++) {
                SleepHelper.sleepS(1);
                System.out.println(i+"");
            }
            System.out.println();
        });
        System.out.println("1.t1线程此时还没有调用start方法,状态为:"+t1.getState());
        t1.start();
        //等待t1线程执行完毕
        t1.join();
        System.out.println("3. t1线程执行完毕,线程状态为:"+t1.getState());

WAITING、TIMED_WAITING 状态演示

		Thread t2=new Thread(() -> {
            //线程阻塞,此时线程的状态应该为 waiting
            LockSupport.park();
            System.out.println("t2线程正在执行...");
            SleepHelper.sleepS(5);
        });
        t2.start();
        //主线程休眠一秒,保证t2线程已经启动
        TimeUnit.SECONDS.sleep(1);
        System.out.println("调用LockSupport.park()方法线程被挂起后,t2线程状态为:"+ t2.getState());
        //放开线程,会走sleep方法休眠5秒,此时的线程的状态应该为 time waiting
        LockSupport.unpark(t2);
        //休眠一秒,确保t2线程被放开
        TimeUnit.SECONDS.sleep(1);
        System.out.println("调用SleepHelper.sleepS(5)方法线程休眠,t2线程状态为:"+ t2.getState());

BLOCKED状态的演示

        Object o=new Object();
        Thread t3=new Thread(() -> {
            synchronized (o){
                System.out.println("t3获取到了o锁");
            }
        });
        new Thread(() -> {
            //创建线程休眠5秒钟并启动拿到o锁,再启动t3线程 并查看状态,此时由于o锁被当前线程获取所以无法进入sync代码块,此时线程的状态为blocking
            synchronized (o){
                SleepHelper.sleepS(5);
            }
        }).start();
        TimeUnit.SECONDS.sleep(1);
        t3.start();
        TimeUnit.SECONDS.sleep(1);
        System.out.println("t3无法获取到o锁被阻塞,此时t3线程的状态为:"+t3.getState());

总结: 线程阻塞状态,只有synchronized关键字才会使线程状态进入BLOCKED状态,其他的阻塞方式 例如LockSupport.park()、ReentrantLock的lock方法都是WAITING状态。

线程的打断

线程的“打断”,并非是字面意义上的 线程执行过程中被中断,而是给当前线程打标记,至于获取到这个标记如何使用?可以自定义代码逻辑。

jdk提供了三个关于打断线程的方法:

  • interrupt() 打断某个线程(设置标记)
  • isInterrupt() 查询某个线程是否被打断(查询标记)
  • static interrupted() 查询当前线程是否被打断过,并重置打断标记(false)

代码示例

        Thread t1=new Thread(() -> {
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    System.out.println("当前线程被标识打断标记");
                    System.out.println(Thread.currentThread().isInterrupted());
                    //如果被标记了打断标识,则跳出循环,结束线程
                    break;
                }else {
                    System.out.println("当前线程没有标识打断标记");
                }
            }

            //判断是否有打断标识,并重置打断标识,此方法为一个静态方法
            if (Thread.interrupted()){
                System.out.println("重置打断标识:"+Thread.currentThread().isInterrupted());
            }

        });
        t1.start();
        TimeUnit.SECONDS.sleep(2);
        t1.interrupt();

interrupt配合sleep()、wait()、join()使用

在使用线程方法sleep()、wait()、join()的时候,会捕获InterruptedException 异常,当线程被标记打断标识后,则会捕获该异常,处理逻辑由程序员自定义编写。示例代码如下:

        Thread t1=new Thread(() -> {
            while (true){
                try {
                    Thread.sleep(1000);
                    System.out.println("1");
                } catch (InterruptedException e) { //catch的是 InterruptedException一场
//                    e.printStackTrace();
                    System.out.println("当前线程设置 打断标识");
                    //在捕获异常后,打断标识会被重置,已防止其他人再次打断无法捕获
                    System.out.println(Thread.currentThread().isInterrupted());
                }
            }
        });
        t1.start();
        //只打断一次,就会被恢复
        t1.interrupt();
        SleepHelper.sleepS(20);

多个线程 在使用synchronized或者ReentrantLock的locak()同步代码块争抢锁的时候,并不会受 interrupt的影响。如果希望受interrupt的影响,可以使用ReentrantLock的lockInterruptibly()来实现。代码示例如下:

        Lock myLock=new ReentrantLock();

        Thread t1=new Thread(() -> {
            try {
                myLock.lock();
                System.out.println("t1开始执行");
                SleepHelper.sleepS(10);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                myLock.unlock();
            }
            System.out.println("t1线程执行完毕");
        });
        t1.start();

        SleepHelper.sleepS(1);

        Thread t2=new Thread(() -> {
            try {
                //t1获得锁之后会休眠10秒,因此t2无法获取到锁,被标记打断标识后会抛出一场
                myLock.lockInterruptibly();
            } catch (InterruptedException e) {
//                e.printStackTrace();
                System.out.println("t2线程被标记打断标识");
            }finally {
                myLock.unlock();
            }
            System.out.println("t2线程执行完毕");
        });
        t2.start();
        SleepHelper.sleepS(1);
        t2.interrupt();
优雅的结束线程
  1. 自然结束。
  2. 线程的stop方法。不建议使用,原因:太过粗暴,在锁同步代码块的时候,可能执行一半,线程被终止,导致数据不一致。
  3. 线程的suspend方法和 线程的resume方法(暂停和恢复)。不建议使用,原因:同样存在数据不一致的问题。
  4. 使用volatile关键字修饰公共静态变量 ,控制while循环。有局限性,如果循环中存在 wait之类的阻塞方法,就不能及时的结束线程。
  5. 使用interrupt方法标记打断标记,在线程内部拍断是否有标识,此方法可以使sleep、wait、join方法抛出InterruptedException异常。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/5696882.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存