线程入门2
线程中断
在java中,终止/打断线程的方式只有尽量让线程里的方法早点结束。
如上图:我们定义一个全局变量来控制线程里的方法结束。
lambda 表达式,有一个语法规则,变量捕获。lambda表达式里的代码,是可以自动捕获到上层作用域中涉及到的局部变量。所谓变量捕获,其实就是让lambda表达式把当前作用域中的变量在lambda内部复制了一份!(此时,外面是否销毁,就无所谓了)。
在java中,变量捕获语法,还有一个前提限制,就是必须只能捕获一个final或者实际上是final的变量(变量虽然没有使用final修饰,但是却没有修改,就是事实上的final)。因此我们为了不出现报错就不能修改isQuit的值,让他保持一个常量的状态。
上面的这种例子都是不太好的中断方式,Java中有专门的方法能够中断线程
线程中while里面的参数表示当前调用currentThread()方法的类 来 调用isInterrupted()方法 判断是都循环继续,默认返回是false, 当我们在主线程中调用interrupted()方法,可以修改返回变成true,这是如果 t 线程还在阻塞中会被直接唤醒,并且检测到异常终止唤醒,就会抛出异常,然后跳出循环,让线程方法执行结束。
线程等待
让一个线程,等待另一个线程执行结束,再继续执行,本质上就是控制线程结束的顺序。
在主线程中调用t.join ,表示主线程在等待t线程结束,一旦调用join,主线程就会出阻塞,此时 t 线程就可以趁机完成后续的工作。一直阻塞到t执行完毕了 t 线程结束了,join才会解除阻塞,主线程才能继续执行
但如果t线程一直不结束,join默认就会"死等" ,但是一般开发中不建议死等,可以用join(xxx)带参数进行等待,带一个超时时间,超时时间到了t线程还没结束,就会不在等待,继续执行主线程的剩余代码。
执行结构为:
需要注意:
线程休眠时间会大于设定的值,在此过程中还包括了调度开销所花费的时间 当开始调度后,系统会安装指定时间的休眠,当时间结束后会唤醒这个进程,状态从阻塞->就绪,但就绪状态也不是说立即就能回到cpu上执行
线程的状态
进程最核心的状态一个是就绪状态,一个是阻塞状态(对于线程也适用)
在java中又给线程赋予了一些其他的状态
NEW: Thread对象已经有了,start方法还没调用
TERMINATED: Thread对象还在,内核中的线程已经没了
RUNNABLE: 就绪状态(线程已经在CPU上执行了/线程已经在排队等待CPU执行了)
TIMED_WAITING:阻塞,由于sleep这种固定时间的方式产生的阻塞
WAITING: 阻塞,由于wait这种不固定时间的方式产生的阻塞
BLOCKED: 阻塞,由于锁竞争导致阻塞
运行结果为:
线程安全
线程安全问题:有些代码在单个线程环境下执行,没有问题正常执行,但是同样的代码在多个线程环境下同时执行,此时就可能出现bug,着就叫“线程安全问题”/"线程不安全"
线程的随机调度,使两个线程执行逻辑的先后顺序,存在诸多可能,必须保证所有可能情况下,代码都是正确的。
注意下面这串代码
我们分别让两个线程都把同一个变量都自增50000次,预期的结果是100000。但运行结果却是下面这样
而且每次运行结果还都可能不太一样,这是我们的程序就出现bug了,也是因为线程不安全导致的。
产生线程安全的原因
1.操作系统中,线程的调度顺序是随机的(抢占式执行)。
2.两个线程,针对同一个变量进行修改。
3.修改操作,不是原子的。
4.内存可见性问题
5.指令重排序问题
解决上诉问题的办法是上枷锁,利用关键字synchronized,在使用synchronized的时候,需要搭配一给代码块{},进入{}就会枷锁,{}里的内容执行完毕就会解锁,在已经加锁的状态中,另一个线程也对此同样进行加锁时,就会产生“锁竞争/锁冲突”,后一个线程就会阻塞等待,一直等到前一个线程解锁为止。
我们加上锁后,运行结果就不会有问题了