JUC多并发编程 LockSupport和线程中断

中断机制

  • 首先 一个线程不应该由其他线程来强制中断和停止,而是应该由线程自己自行停止。
  • 其次 在 Java 中没有办法立即停止一条线程,然而停止线程线程却显得尤为重要,如取消一个耗时操作。因此,java 提供了一种用于停止线程的协商机制--中断,也即中断标识协商中断
  • 中断只有一种协商机制,Java  没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的 interrupt 方法,该方法也仅仅是将线程对象的中断标识符设置成 true;
方法 说明
public void interrupt()

实例方法 interrupt() 仅仅是设置线程的中断状态为true, 发起一个协商而不会立刻停止线程

public static boolean interrupted()

静态方法 返回当前线程的中断状态,测试当前线程是否已被中断

将当前线程的中断状态清零并重新设为 false,清除线程的中断状态

public boolean isInterrupted() 实例方法 判断当前线程是否呗中断(通过检查中断标志位)

如何中断运行中的线程:

  • 通过 一个 volatile 变量实现
  • 通过 AtomicBoolean
  • 通过 Thread 类 自带的 中断 API 实例方法实现
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class InterruptDemo {
    static volatile boolean isStop = false;
    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "\t isInterrupted 被修改为 true, 程序停止");
                    break;
                }
                System.out.println("t1 ----- hello  Interrupted");
            }
        }, "t1");
        t1.start();

        try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            t1.interrupt();
        },"t2").start();
    }
    public static void m2() {
        new Thread(() -> {
            while(true) {
                if(atomicBoolean.get()) {
                    System.out.println(Thread.currentThread().getName() + "\t atomicBoolean 被修改为 true, 程序停止");
                    break;
                }
                System.out.println("t1 ----- hello  atomicBoolean");
            }
        },"t1").start();

        try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            atomicBoolean.set(true);
        },"t2").start();
    }

    public static void m1() {
        new Thread(() -> {
            while(true) {
                if(isStop) {
                    System.out.println(Thread.currentThread().getName() + "\t isStop 被修改为 true, 程序停止");
                    break;
                }
                System.out.println("t1 ----- hello  volatile");
            }
        },"t1").start();

        try{ TimeUnit.MILLISECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
          isStop = true;
        },"t2").start();
    }
}

当前线程的中断标识为 true, 是不是线程就立刻停止:

  • 如果线程处于 正常活动状态,那么会将该线程的中断标志设置为 true,被设置中断标志的线程正常运行,不受影响
  • 如果线程处于被阻塞状态(sleep,wait,join 等状态),在别的线程中调用当前线程对象的interrupt 方法,那么线程将立即退出被阻塞状态,并抛出一个 InterruptedExecption 异常
import java.util.concurrent.TimeUnit;

public class InterruptDemo2 {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while(true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + "\t 中断标志位:" + Thread.currentThread().isInterrupted() + "程序停止" );
                    break;
                }

                try{
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    // 如果不中断,则程序会进入死循环
                    // Thread.currentThread().interrupt();
                    e.printStackTrace();
                }
                System.out.println("hello InterruptDemo2");
            }
        }, "t1");
        t1.start();
        try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()-> t1.interrupt(),"t2").start();
    }
}

静态方法 Thread.interrupted() 理解:

    public class InterrupteDemo4 {
        public static void main(String[] args) {
            System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
            System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
            System.out.println("-----1");
            Thread.currentThread().interrupt();
            System.out.println("-----2");
            System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
            System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
        }
    }




    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    private native boolean isInterrupted(boolean ClearInterrupted);

LockSupport

  • 用来创建锁和其他同步类的基本线程阻塞原语
  • park() 和 unpark() 的作用分别是阻塞线程和解除线程阻塞(LockSupport 和 每个使用它的线程都是一个许可关联,每个线程都有一个相关的 permit,permit 最多只有一个,重复调用 unpart 也不会积累凭证)
  • 是一个线程阻塞工具类,所有发的方法都是静态方法,可以让线程在任意位置阻塞,阻塞之后也有对应的唤醒方法,归根揭底,LockSupport 调用的 Unsafe 中的 native 代码。
  • park 方法: 如果有凭证,则会直接消耗掉这个凭证然后正常退出,如果无凭证,就必须阻塞等待凭证可用
  • unpark方法: 会增加一个凭证,单凭证只能有一个,累加无效

线程等待唤醒机制

  • 使用 Object 中的wait() 方法让线程等待,使用 Object 中的 notify() 方法唤醒线程
import java.util.concurrent.TimeUnit;

public class LockSupportDemo {
    public static void main(String[] args) {
        Object objectLock = new Object();
        // 必须放在同步块或者方法里
        // 将 notify 放在 wait 方法前面,程序无法执行,无法唤醒
        new Thread(()-> {
            synchronized (objectLock) {
                System.out.println(Thread.currentThread().getName() + "\t ----come in");
                try {
                    objectLock.wait();
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "\t ----被唤醒");
            }
        },"t1").start();
        try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()-> {
            synchronized (objectLock) {
                objectLock.notify();
                System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
            }
        },"t2").start();
    }
}
  • 使用 JUC 报中的 Condition的 await() 方法让线程等待,使用 signal() 方法唤醒线程
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockSupportDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        // 必须放在同步块或者方法里
        // 将 signal 放在 await 方法前面,程序无法执行,无法唤醒
        new Thread(()-> {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "\t ----come in");
                condition.await();
                System.out.println(Thread.currentThread().getName() + "\t ----被唤醒");
            }catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();
            }
        },"t1").start();
        try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()-> {
            lock.lock();
            condition.signal();
            System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
            lock.unlock();
        },"t2").start();
    }
}
  • LockSupport 类 可以阻塞当前线程以及 唤醒执行被阻塞线程

public class LockSupportDemo {
    /**
     * Permit 许可证默认没有不能放行,所以一开始调 park() 方法当前线程就会阻塞,直到别的线程给当前线程的发放 permit, park 方法还会被唤醒
     * 调用 uppark(thread) 方法后,就会将 thread 线程的许可证 permit 发放,会自动唤醒 park 线程,即之前阻塞中的 LockSupport.park() 方法会立即返回
     * @param args
     */
    public static void main(String[] args) {
        // 无锁块要求
        // 加锁和解锁不需要顺序
        Thread t1 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ----come in");
            LockSupport.park();
            System.out.println(Thread.currentThread().getName() + "\t ----被唤醒");
        }, "t1");
        t1.start();
        try{ TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(()-> {
            LockSupport.unpark(t1);
            System.out.println(Thread.currentThread().getName() + "\t ----发出通知");
        },"t2").start();
    }
}

你可能感兴趣的:(JUC,多并发,java)