线程的方法
- @Test
- public void bb() {
- Thread thread = Thread.currentThread();
- System.out.println(thread.getName());//打印 main
- }
- @Test
- public void bb() {
- Thread.currentThread().setName("heihei");
- String name = Thread.currentThread().getName();
- System.out.println(name); // 打印 heihei
- }
判断当前线程是否正在执行
- @Test
- public void bb() {
- MyThread mt = new MyThread() ; // 实例化Runnable子类对象
- Thread t = new Thread(mt,"线程"); // 实例化Thread对象
- System.out.println("线程开始执行之前 --> " + t.isAlive()) ; // 打印结果:线程开始执行之前 --> false
- t.start() ; // 启动线程
- System.out.println("线程开始执行之后 --> " + t.isAlive()) ; // 打印结果:线程开始执行之前 --> true
- }
调用start()方法才是 启动一个新的线程。
调用run()方法只是主线程调用了一个类的一个普通方法.并没有开启一个新线程.
- @Test
- public void bb() {
- MyThread myThread = new MyThread();
- myThread.start(); //打印 Thread-2运行了
- myThread.run(); //打印 main方法运行了
- }
将两个线程绑定在一起,看成是共同体,B线程执行完成后,必然执行A线程.
join()就是A无限期等待,当B线程执行完,A线程才执行
join(1000)就是A只等待1000毫秒
如果B 500毫秒执行完了,那么 A就直接结束等待时间,直接被唤醒
如果A等待1000毫秒后,B还没执行完,那A也不等了,就直接被唤醒和B一块并发执行
底层其实就是A线程被wait,下面是底层代码
- public final synchronized void join(long millis)
- throws InterruptedException {
- long base = System.currentTimeMillis();
- long now = 0;
-
- if (millis < 0) {
- throw new IllegalArgumentException("timeout value is negative");
- }
-
- if (millis == 0) {
- while (isAlive()) {//只要B线程还没结束,A线程就会一直阻塞
- wait(0);//这里的wait调用的本地方法。
- }
- } else {//等待一段指定的时间
- while (isAlive()) {
- long delay = millis - now;
- if (delay <= 0) {
- break;
- }
- wait(delay);
- now = System.currentTimeMillis() - base;
- }
- }
- }
下面是例子
- @Test
- public void bb() {
- MyBThread myThread = new MyBThread();
- myThread.start();
- for (int i = 0; i < 15; i++) {
- if(i>5){
- try{
- myThread.join() ; // 线程强制运行
- }catch(InterruptedException e){}
- }
- System.out.println("Main线程运行 --> " + i) ;
- }
- }
-
- }
- class MyBThread extends Thread{
- @Override
- public void run(){
- for (int i = 0; i < 15; i++) {
- System.out.println(Thread.currentThread().getName()+"运行了,i="+i);
- }
-
- }
- }
- Main线程运行 --> 0
- Main线程运行 --> 1
- Main线程运行 --> 2
- Main线程运行 --> 3
- Main线程运行 --> 4
- Main线程运行 --> 5
- Thread-2运行了,i=0
- Thread-2运行了,i=1
- Thread-2运行了,i=2
- Thread-2运行了,i=3
- Thread-2运行了,i=4
- Thread-2运行了,i=5
- Thread-2运行了,i=6
- Thread-2运行了,i=7
- Thread-2运行了,i=8
- Thread-2运行了,i=9
- Thread-2运行了,i=10
- Thread-2运行了,i=11
- Thread-2运行了,i=12
- Thread-2运行了,i=13
- Thread-2运行了,i=14
- Main线程运行 --> 6
- Main线程运行 --> 7
- Main线程运行 --> 8
- Main线程运行 --> 9
- Main线程运行 --> 10
- Main线程运行 --> 11
- Main线程运行 --> 12
- Main线程运行 --> 13
- Main线程运行 --> 14
yield() 暂停当前方法,释放自己拥有的CPU,线程进入就绪状态。它能让当前线程由“运行状态”进入到“就绪状态”,然后和其他优先级比它高的或者和它相同的线程一起去重新争抢cpu的使用.
优先级高的线程抢到cpu的概率较大,但是不一定会第一个抢到, 因为这还和操作系统的调度和cpu的执行有关系. 建议少用.
线程休眠:让执行的线程暂停一段时间,进入计时等待状态。
static void sleep(long millis):调用此方法后,当前线程放弃 CPU 资源,在指定的时间内,sleep 所在的线程不会获得可运行的机会,此状态下的线程不会释放同步锁。
该方法更多的是用来模拟网络延迟,让多线程并发访问同一资源时的错误效果更加明显。
wait()/wait(long timeout)/wait(long timeout, int nanos)
先看一看wait的三个重载方法
方法参数
timeout - 等待时间(以毫秒为单位)。
nanos - 额外等待时间(以纳秒为单位)。
wait()相当于调用wait(0),0表示永远不超时。让当前线程处于等待状态,只有调用对象的notify或者notifyAll才能将其唤醒.
wait(1000):等待1000毫秒, 让当前线程处于计时等待状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过参数 timeout设置的超时时间才能将其唤醒.
wait(1000,50):等待1000毫秒+50纳秒, 让当前线程处于计时等待状态,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过参数 timeout 加上 nanos 设置的超时时间才能将其唤醒.
notify()/notifyAll()
唤醒:notify()-随机唤醒wait的一个线程,notifyAll()-唤醒所有线程
注意:此方法需和wait()成对使用,必须在同步代码块或同步方法中
上面的方法必须在同步代码块或同步方法中
每一个对象都有一个跟它关联的monitor,只有获取到对象的monitor才能调用对象的wait方法和调用对象的notify和notifyAll方法。也就是说wait,notify,notifyAll都必须在对象的synchronized同步方法里面调用。如果wait没有在对象的synchronized同步块里面执行会抛出
java.lang.IllegalMonitorStateException
一般 run()方法执行完,线程就会正常结束,然而,常常有些线程是伺服线程。它们需要长时间的运行,只有在外部某些条件满足的情况下,才能关闭这些线程。使用一个变量来控制循环,例如: 最直接的方法就是设一个boolean类型的标志,并通过设置这个标志为true或false来控制while 循环是否退出,代码示例:
- @RestController
- @RequestMapping("/test")
- public class Docx4jOfficeFileToPDF {
- public static void main(String args[]) throws InterruptedException {
- MyBThread mt = new MyBThread() ;
- mt.start();
- Thread.sleep(1);//主线程等待1ms
- mt.exit = true; //修改标志位,退出线程
- }
- }
- class MyBThread extends Thread{
- //volatile修饰符用来保证其它线程读取的总是该变量的最新的值
- public volatile boolean exit = false;
- @Override
- public void run() {
- int i =1;
- while(!exit){
- System.out.println(i++);
- }
- System.out.println("结束");
- }
- }
定义了一个退出标志exit,当exit为true时,while循环退出,exit的默认值为false.在定义exit 时,使用了一个 Java 关键字 volatile,这个关键字的目的是使 exit 同步,也就是说在同一时刻只 能由一个线程来修改exit的值。
interrupt() 方法仅仅是在当前线程中打一个停止的标记,并不是真的停止线程。
① 如果线程使用了 sleep,同步锁的 wait,socket 中的 receiver,accept 等方法时, 会使线程处于阻塞状态。当sleep()等方法和interrupt() 方法相遇时.sleep()等方法会抛出 InterruptException 异常,并清除掉线程的中断标识.。
② 如果线程处于正常活动状态,那么会将该线程的中断标志设置为 true,仅此而已。被设置中断标志的线程将继续正常运行,丝毫不受影响。
所以我们只有手动去停止线程,我们需要配合下面的两个方法,来手动写停止逻辑代码,以达到手动停止线程的目的
- public boolean Thread.isInterrupted() //判断是否被中断
- public static boolean Thread.interrupted() //判断是否被中断,并清除当前中断状态
实验得知:
1.在线程sleep()阻塞的时候, 线程再去调 interrupt()方法,会抛出 InterruptException 异常
2.在线程调完interrupt()方法,有中断标识的时候, 线程再去调sleep()方法.也会抛出InterruptException 异常
因此,实验得出下面的代码100%能结束线程.
- public class Docx4jOfficeFileToPDF {
- public static void main(String args[]) throws InterruptedException {
- MyBThread mt = new MyBThread() ;
- mt.start();//子线程执行
- mt.interrupt();//给子线程加上中断标志
-
- }
- }
- class MyBThread extends Thread{
- @Override
- public void run() {
- while (true){
- try{
- Thread.sleep(500000);//阻塞过程捕获中断异常来退出
- }catch(InterruptedException e){
- e.printStackTrace();
- break;//捕获到异常之后,执行break跳出循环,相当于结束子线程
- }
- }
- }
- }
或者用interrupt()当做标准位终止线程的那种方式
- @RestController
- @RequestMapping("/test")
- public class Docx4jOfficeFileToPDF {
- public static void main(String args[]) throws InterruptedException {
- MyBThread mt = new MyBThread() ;
- mt.start();
- mt.interrupt(); //修改标志位,退出线程
- }
- }
- class MyBThread extends Thread{
- @Override
- public void run() {
- int i =1;
- while(!isInterrupted()){
- System.out.println(i++);
- }
- System.out.println("结束");
- }
- }
此方法已被弃用,不安全.
为什么弃用stop:
例如,存在一个对象 u 持有 ID 和 NAME 两个字段,假如写入线程在写对象的过程中,只完成了对 ID 的赋值,但没来得及为 NAME 赋值,就被 stop() 导致锁被释放,那么当读取线程得到锁之后再去读取对象 u 的 ID 和 Name 时,就会出现数据不一致的问题
- @RestController
- @RequestMapping("/test")
- public class Docx4jOfficeFileToPDF {
- public static void main(String args[]){
- MyBThread mt = new MyBThread() ;
- mt.start();
- }
- }
- class MyBThread extends Thread{
- @Override
- public void run(){
- System.out.println("运行了111");
- this.stop();
- System.out.println("运行了222");
- }
- }
运行了111