- 什么是程序
- 什么是线程
- 单核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();优雅的结束线程
- 自然结束。
- 线程的stop方法。不建议使用,原因:太过粗暴,在锁同步代码块的时候,可能执行一半,线程被终止,导致数据不一致。
- 线程的suspend方法和 线程的resume方法(暂停和恢复)。不建议使用,原因:同样存在数据不一致的问题。
- 使用volatile关键字修饰公共静态变量 ,控制while循环。有局限性,如果循环中存在 wait之类的阻塞方法,就不能及时的结束线程。
- 使用interrupt方法标记打断标记,在线程内部拍断是否有标识,此方法可以使sleep、wait、join方法抛出InterruptedException异常。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)