java thread null_Java基础知识_Thread

一、Thread线程类API

实现多线程从本质上都是由Thread类来进行操作的,我们来看看Thread类的一些重要的知识点。

Thread这个类很大,所以就看一些常见的,重要的方法。

1.1 设置线程名

我们在使用多线程的时候,想要查看线程名是很简单的,调用Thread.currentThread().getname即可。

如果没有做什么设置,我们会发现线程的名字是这样子的:主线程叫main,其他线程是Thread-x

下面我们来看看是怎么命名的

1 publicThread() {2 init(null, null, "Thread-" + nextThreadNum(), 0);3 }

nextThreadNum的方法实现是这样的

1 private static synchronized intnextThreadNum() {2 return threadInitNumber++;3 }

基于这么一个变量,线程的初始化数量

1 private static int threadInitNumber;

点进去看看init方法就可以确定了

1 private voidinit(ThreadGroup g, Runnable target, String name,2 longstackSize, AccessControlContext acc) {3 if (name == null) {4 throw new NullPointerException("name cannot be null");5 }6

7 this.name = name.toCharArray();

看到这里,如果我们想要为线程起个名字也是很简单的。Thread给我们提供了构造方法

1 publicThread(Runnable target, String name) {2 init(null, target, name, 0);3 }

当然了,我们还可以通过setName(String name)方法来改掉线程的名字,我们来看看方法的实现

1 public final synchronized voidsetName(String name) {2 checkAccess();3 this.name =name.toCharArray();4 if (threadStatus != 0) {5 setNativeName(name);6 }7 }

检查是否有权限修改

1 public voidcheckAccess(Thread t) {2 if (t == null) {3 throw new NullPointerException("thread can't be null");4 }5 if (t.getThreadGroup() ==rootGroup) {6 checkPermission(SecurityConstants.MODIFY_THREAD_PERMISSION);7 } else{8 //just return

9 }10 }

1.2 守护线程

守护线程是为其他线程服务的

垃圾回收线程就是守护线程~

守护线程有一个特点

当别的用户线程执行完了,虚拟机就会退出,守护线程也就会被停止了

也就是说:守护线程为一个服务线程,没有服务对象就没必要继续运行了。

使用线程的时候要注意的地方

在线程启动前设置为守护线程,方法是setDaemon(boolean on)

使用守护线程不要访问共享资源和文件,因为它可能在任何时候就挂掉了

守护线程中产生的新线程也是守护线程

测试以下

1 public class MyThread implementsRunnable {2

3 @Override4 public voidrun() {5 //打印出当前线程的名字

6 System.out.println(Thread.currentThread().getName());7 }8 }

1 public classMyThreadDemo {2 public static voidmain(String[] args) {3

4

5 MyThread myThread = newMyThread();6

7 //带参构造方法给线程起名字

8 Thread thread1 = new Thread(myThread, "线程1");9 Thread thread2 = new Thread(myThread, "守护");10

11 //设置为守护线程

12 thread2.setDaemon(true);13

14 thread1.start();15 thread2.start();16 System.out.println("主线程"+Thread.currentThread().getName());17 }18 }

结果

05bb4e4b3af11e4807399b29a8ce6340.png

1.3 优先级线程

线程优先级高仅仅表示线程获取CPU的时间片的几率高,但这不是一个确定因素,线程的优先级是高度依赖于操作系统的

可以看到的是Java提供的优先级默认是5,最低是1 ,最高是10

85c4107f657ae230f9f7878d016680b1.png

实现

1 public final void setPriority(intnewPriority) {2 ThreadGroup g;3 checkAccess();4 if (newPriority > MAX_PRIORITY || newPriority g.getMaxPriority()) {9 newPriority =g.getMaxPriority();10 }11 setPriority0(priority =newPriority);12 }13 }

先参数检查,如果存在线程组,那么该线程的优先级不能比组的优先级高

setPriority0是一个本地native方法

1 private native void setPriority0(int newPriority);

1.4 线程的生命周期

在上篇介绍过了线程有三个基本状态:执行就绪阻塞

在Java中我们就有了这个图,Thread上有很多方法都是用来切换线程的状态的,这一部分是重点

下面就来讲解与线程生命周期相关的方法

1.4.1 sleep方法

调用sleep方法会进入计时等待状态,等时间到了,进入的是就绪状态而并非是运行状态。

1.4.2 yield方法

调用yield方法会先让别的线程执行,但是不确保真正让出

意思是:我有空,可以的话,就让你们执行,实际上很少用,它不确保一定会会让出CPU

1.4.3 join方法

调用join方法会等待该线程执行完毕后才执行别的线程

1 /**

2 * Waits for this thread to die.3 *4 *

An invocation of this method behaves in exactly the same5 * way as the invocation6 *7 *

8 * {@linkplain#join(long) join}{@code(0)}9 *
10 *11 *@throwsInterruptedException12 * if any thread has interrupted the current thread. The13 * interrupted status of the current thread is14 * cleared when this exception is thrown.15 */

16 public final void join() throwsInterruptedException {17 join(0);18 }

我们进去看看具体的实现:

1 /**

2 * Waits at most {@codemillis} milliseconds for this thread to3 * die. A timeout of {@code0} means to wait forever.4 *5 *

This implementation uses a loop of {@codethis.wait} calls6 * conditioned on {@codethis.isAlive}. As a thread terminates the7 * {@codethis.notifyAll} method is invoked. It is recommended that8 * applications not use {@codewait}, {@codenotify}, or9 * {@codenotifyAll} on {@codeThread} instances.10 *11 *@parammillis12 * the time to wait in milliseconds13 *14 *@throwsIllegalArgumentException15 * if the value of {@codemillis} is negative16 *17 *@throwsInterruptedException18 * if any thread has interrupted the current thread. The19 * interrupted status of the current thread is20 * cleared when this exception is thrown.21 */

22 public final synchronized void join(longmillis)23 throwsInterruptedException {24 long base =System.currentTimeMillis();25 long now = 0;26

27 if (millis < 0) {28 throw new IllegalArgumentException("timeout value is negative");29 }30

31 if (millis == 0) {32 while(isAlive()) {33 wait(0);34 }35 } else{36 while(isAlive()) {37 long delay = millis -now;38 if (delay <= 0) {39 break;40 }41 wait(delay);42 now = System.currentTimeMillis() -base;43 }44 }45 }

0表示永远等待,循环调用wait方法,当线程终止了会调用notifyAll方法来唤醒

wait方法是在Object上定义的,它是native本地方法,所以就看不了了

wait方法实际上它也是计时等待的一种

1.4.3 interrupt方法

线程中断在之前的版本有stop方法,但是被设置过时了。现在已经没有强制线程终止的方法了。

由于stop方法可以让一个线程A终止掉另一个线程B

被终止的线程B会立刻释放锁,这可能会让对象处于不一致的状态

线程A也不知道线程B什么时候能够被终止掉,万一线程B还处理运行计算阶段,线程A调用stop方法终止B,那就很无辜了。

总而言之,stop就很暴力,不安全,所以被设置过时了。

我们一般使用的是interrupt来请求终止线程

要注意的是:interrupt不会真正停止一个线程,它仅仅是给这个线程发了一个信号,告诉它,它应该要结束了(明白这点非常重要)

也就是说:Java设计者实际上是想线程自己来终止,通过上面的信号,就可以判断处理了什么业务了。

具体到底是中断还是继续运行应该由被通知的线程自己处理

1 Thread t1 = new Thread( newRunnable(){2 public voidrun(){3 //若未发生中断,就正常执行任务

4 while(!Thread.currentThread.isInterrupted()){5 //正常任务代码……

6 }7 //中断的处理代码……

8 doSomething();9 }10 } ).start();

再次说明:调用interrupt并不是要真正终止掉当前线程,仅仅是设置了一个中断标志。这个中断标志可以给我们用来判断什么时候该干什么活。什么时候中断由我们自己来决定,这样就可以安全地终止线程了。

我们来看看源码是怎么讲的吧

1 /**

2 * Interrupts this thread.3 *4 *

Unless the current thread is interrupting itself, which is5 * always permitted, the {@link#checkAccess() checkAccess} method6 * of this thread is invoked, which may cause a {@link

7 * SecurityException} to be thrown.8 *9 *

If this thread is blocked in an invocation of the {@link

10 * Object#wait() wait()}, {@linkObject#wait(long) wait(long)}, or {@link

11 * Object#wait(long, int) wait(long, int)} methods of the {@linkObject}12 * class, or of the {@link#join()}, {@link#join(long)}, {@link

13 * #join(long, int)}, {@link#sleep(long)}, or {@link#sleep(long, int)},14 * methods of this class, then its interrupt status will be cleared and it15 * will receive an {@linkInterruptedException}.16 *17 *

If this thread is blocked in an I/O operation upon an {@link

18 * java.nio.channels.InterruptibleChannel InterruptibleChannel}19 * then the channel will be closed, the thread's interrupt20 * status will be set, and the thread will receive a {@link

21 * java.nio.channels.ClosedByInterruptException}.22 *23 *

If this thread is blocked in a {@linkjava.nio.channels.Selector}24 * then the thread's interrupt status will be set and it will return25 * immediately from the selection operation, possibly with a non-zero26 * value, just as if the selector's {@link

27 * java.nio.channels.Selector#wakeup wakeup} method were invoked.28 *29 *

If none of the previous conditions hold then this thread's interrupt30 * status will be set.

31 *32 *

Interrupting a thread that is not alive need not have any effect.33 *34 *@throwsSecurityException35 * if the current thread cannot modify this thread36 *37 * @revised 6.038 * @spec JSR-5139 */

40 public voidinterrupt() {41 if (this !=Thread.currentThread())42 checkAccess();43

44 synchronized(blockerLock) {45 Interruptible b =blocker;46 if (b != null) {47 interrupt0(); //Just to set the interrupt flag

48 b.interrupt(this);49 return;50 }51 }52 interrupt0();53 }

中断当前线程,只能自己调用,不然会抛出安全异常。

前面说了,Java设计者设置中断标志的目的是想由被通知的线程自己处理,而这些方式都阻塞掉了。

被阻塞掉的线程调用中断方法是不合理的(不允许中断已经阻塞的线程)

因为可能会造成 中断无效。

以上三种情况都不发生,才能将对应的标志位置换

中断一个不活动的线程是没有意义的,首先检查有没有权限,任何看看是不是阻塞的线程调用

如果是,抛出异常,将中断标志位改为false

如果很顺利,就可以修改到标志位了。

再来看看刚才说抛出的异常是什么东东吧

e4c5b4949340766c08d36ddbeaf19c1f.png

所以说,interrupt方法根本不会对线程的状态造成影响的,它仅仅设置一个标志位罢了。

interrupt线程中断还有另外两个方法(检查该线程是否被中断)

静态方法:interrupted-->会清除中断标志位

实例方法:isInterrupted-->不会清除中断标志位

如果阻塞线程调用了interrupt方法,那么会抛出异常,设置标志位为false,同时该线程会退出阻塞

来测试一下

1 public classMain {2 /**

3 *@paramargs4 */

5 public static voidmain(String[] args) {6 Main main = newMain();7

8 //创建线程并启动

9 Thread t = newThread(main.runnable);10 System.out.println("This is main ");11 t.start();12

13 try{14

15 //在 main线程睡个3秒钟

16 Thread.sleep(3000);17 } catch(InterruptedException e) {18 System.out.println("In main");19 e.printStackTrace();20 }21

22 //设置中断

23 t.interrupt();24 }25

26 Runnable runnable = () ->{27 int i = 0;28 try{29 while (i < 1000) {30

31 //睡个半秒钟我们再执行

32 Thread.sleep(500);33

34 System.out.println(i++);35 }36 } catch(InterruptedException e) {37

38

39 //判断该阻塞线程是否还在

40 System.out.println(Thread.currentThread().isAlive());41

42 //判断该线程的中断标志位状态

43 System.out.println(Thread.currentThread().isInterrupted());44

45 System.out.println("In Runnable");46 e.printStackTrace();47 }48 };49 }

接下来我们分析一下它的执行流程是怎么样的:

1、在main线程中睡了3秒钟,因此我们去新的线程中执行了。

2、每次睡半秒钟才执行

3、在睡半秒钟的同时,main线程的3秒钟过去了,main线程设置了该线程中断

4、抛出了异常,该阻塞线程(睡半秒钟立马停止了阻塞)在catch方法还存活着,线程的中断标志位变成了false

76dc74a43979c6f4ece207f7e4c3817a.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值