并发编程之线程中断&CAS

《C++ Concurrency in Action》笔记30 高级线程管理——线程池及中断线程 9 高级线程管理 在之前的章节,我们通过直接创建std::thread对象来管理线程。有几处你已经看出,这是不可取的,因为之后你必须管理线程对象的生命期,以及确定适合该问题的线程数和当前的硬件,等等。理想的情况是,你可以最大程度的将代码分成可以并行执行的小块,把它们交给编译器和标准库,然后说:“把它们并行化以获得最佳性能”。 这些例子中的另一个常见问题就是,你可能使用了多个线程来解决一个问 阅读详情

synchronized和LOCK的区别

在前面的笔记中已经记录了synchronized,这个锁是JVM内置的锁,不需要程序员手动释放锁,我们在前面已经知道了synchronized锁主要是使用monitor锁的机制来实现的,而synchronized在锁升级到重量级锁过后,是通过操作系统的mutex互斥量来实现的,这就会在用户态和内核态直接频繁切换,导致性能有一定的影响,而我们知道synchronized锁的是对象,而锁的升级过程是通过对象的对象头的markword来实现,markword在锁升级的情况下会出现不同的形态;
既然synchronized是针对的对象锁,那么我们知道java.lang.Object提供了wait和notify,锁的等待和释放,我们要注意,object的wait和notify是必须在synchronized中使用的,如果不在synchronized中使用,程序会出现错误,为什么呢?因为wait和notify底层使用的monitor机制,而monitor机制是基于对象头的markword来实现的,而monitor机制是synchronized中特有的一种机制,所以说wait和notify是基于synchronized锁对象的monitor机制来实现,简单来说wait和notify只能用于synchronized中,而Juc包下的LockSupport和ReentrantLock是基于线程的锁;比如我们来看一段代码:

public class T0925 {
    private static final Object obj = new Object();
    public static void main(String[] args) throws InterruptedException {
        obj.wait();
    }
}

在这里插入图片描述
通过上面的例子就可以看出我们在直接调用object的wait方法,会出现非法的监视器状态异常;
我们知道synchronized是基于monitor实现的,而synchronized是锁的是对象,也就是阻止其他线程进入,简单来说就是让所有的线程串行执行,序列化的访问临界资源,既然锁的对象,那么对象锁的释放是通过monitor来来实现的,那么我们也可以通过object的wait来让出cpu的执行时间片,也可以使用notify来让wait的对象重新获得锁,那么我来看一个例子:

public class T0925 {


    private static final Object obj = new Object();
    public static void main(String[] args) throws InterruptedException {
        T0925 t = new T0925();
        Thread thread_01 = new Thread(()->{
            t.waitTest();
        },"Thread-01");
        Thread thread_02 = new Thread(()->{
            t.notifyTest();
        },"Thread-02");
        thread_01.start();
        Thread.sleep(3000);
        thread_02.start();


    }

    public void waitTest(){
        synchronized (obj){
            System.out.println(Thread.currentThread().getName() + " wait enter ");
            try {
                obj.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " wait out ");

        }
    }
    public void notifyTest(){
        synchronized (obj){
            System.out.println(Thread.currentThread().getName() + " notifyTest enter ");
            obj.notify();
            System.out.println(Thread.currentThread().getName() + " notifyTest out ");

        }
    }


}

输出:

Thread-01 wait enter 
Thread-02 notifyTest enter 
Thread-02 notifyTest out 
Thread-01 wait out 

所以wait是可以释放锁的,其他线程可以获得锁,然后进入同步代码块

线程中断

线程中断是什么?我们如果要中断一个线程该怎么来做,用stop?肯定不行,stop非常危险,为什么呢?因为我们的线程正在运行,如果你突然调用stop,那么这个线程可能正在往内存写值,那么你中断了的话,那么可能其他线程读取到的是stop前的值,就是脏数据,总之来说,stop是不建议使用的,非常危险。那么我们如何优雅的中断一个线程?通过一个标志来中断?就是运行的时候给线程发一个中断标志,线程获取到中断标志然后进行中断?
在jdk中的Thread中比较常用的一种线程中断就是interrupt,我们来看下面线程中断的3个api:

//检查当前线程的中断标记,并且设置一个中断标记为true
Thread.interrupted();
//为当前线程设置一个中断标记为true
Thread.currentThread().interrupt();
//检查当前线程的线程中断标志并且设置中断标记为false
Thread.currentThread().isInterrupted()

这三个api我们通过例子来说明,首先大家要知道,线程中断标志设置了不意味着我们的线程会终止,中断标志就是一个标志位,设置了这个标志意味着你的线程可以手动中断,停止执行,而jvm线程是不会停止当前线程的,只是当前线程会有一个标志为表示当前线程可以中断,需要程序员手动的去终止线程,比如:

public class interruptedTest {

    public static void main(String[] args) {
          Thread thread = new Thread(()->{
              test();
          });
          thread.start();
          thread.interrupt();

    }

    private static  void test(){
        for(int i = 1; i< 1000000 ;  i++ ){
            System.out.println(i);
            if(Thread.currentThread().isInterrupted()){
                System.out.println("线程中断标志。。。。为true");
            }
            if(i > 30 ){
                break;
            }
        }
    }


}

输出:

1
线程中断标志。。。。为true
2
线程中断标志。。。。为true
3
线程中断标志。。。。为true
4
线程中断标志。。。。为true
5
线程中断标志。。。。为true
6
线程中断标志。。。。为true
7
线程中断标志。。。。为true
8
线程中断标志。。。。为true
9
线程中断标志。。。。为true
10
线程中断标志。。。。为true
11
线程中断标志。。。。为true
12
线程中断标志。。。。为true
13
线程中断标志。。。。为true
14
线程中断标志。。。。为true
15
线程中断标志。。。。为true
16
线程中断标志。。。。为true
17
线程中断标志。。。。为true
18
线程中断标志。。。。为true
19
线程中断标志。。。。为true
20
线程中断标志。。。。为true
21
线程中断标志。。。。为true
22
线程中断标志。。。。为true
23
线程中断标志。。。。为true
24
线程中断标志。。。。为true
25
线程中断标志。。。。为true
26
线程中断标志。。。。为true
27
线程中断标志。。。。为true
28
线程中断标志。。。。为true
29
线程中断标志。。。。为true
30
线程中断标志。。。。为true
31
线程中断标志。。。。为true

执行中的线程已经收到了中断标志,但是线程不会自己停止,只会设置一个标记而已
但是当我们修改下程序:

private static  void test(){
        for(int i = 1; i< 1000000 ;  i++ ){
            System.out.println(i);
            if(Thread.currentThread().isInterrupted()){
                System.out.println("线程中断标志。。。。为true");
                break;
            }
            if(i > 30 ){
                break;
            }
        }
    }


}

那么线程就会退出,而且isInterrupted是判断线程是否设置了线程中断标志,而interrupt是设置线程中断标志为true,而isInterrupted是不会修改线程中断标志的。
我们再看下另外一个api,Thread.interrupted,这个api是干什么的呢?这个api的意思就是坚持当前的线程是否设置了线程中断标志,如果设置了那么就重置为false,简单来说就是你要停止线程,我不让你停,什么意思呢?比如说我们的线程进入一个方法中或者代码块中,我们知道代码的执行时有序的进行的,比如我下面的代码需要检测你是否设置了中断标记,如果设置了我就给你停止,如果没有设置,就不停止,但是我在这个检测的前面强制检测如果设置了,再给你设置回去,那么就用于不会终止线程,但是没有这种做法,但是有些场景也会有遇到,比如设置了一个标志表示在某种场景下我不让你线程停止,在某些场景下设置了中断就可以停止,比如下面的代码:

public class interruptedTest {

    //falg=0的时候不能终止线程 1=可以终止线程
    private static final int falg = 1;
    public static void main(String[] args) {
          Thread thread = new Thread(()->{
              test();
          });
          thread.start();
          thread.interrupt();

    }

    private static  void test(){
        for(int i = 1; i< 1000000 ;  i++ ){
            System.out.println(i);
            if(falg ==0){
                //这种场景下我不让你停止线程,强制将线程中断标志设置为false
                Thread.interrupted();
            }


            if(Thread.currentThread().isInterrupted()){
                System.out.println("线程中断标志。。。。为true");
                break;
            }
            if(i > 30 ){
                break;
            }
        }
    }


}

就类似上面的情况

线程中断之唤醒

如果说我们的一个线程正在睡眠或者wait的时候,我们将其中断,那么wait和sleep的线程是否能够检测的到呢?我们来看个程序例子:

public class interruptedTest {


    private final static Object object = new Object();

    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            waitInterrrupt();
        }, "Thread-sleep");

        Thread waitThread = new Thread(() -> {
            waitInterrrupt();
        }, "Thread-wait");
        Thread parkThread = new Thread(() -> {
            parakInterrrupt();
        }, "Thread-lock");
        thread.start();
        waitThread.start();
        parkThread.start();
        thread.interrupt();
        waitThread.interrupt();
        parkThread.interrupt();
    }

    private static void sleepInterrrupt() {
        for (int i = 1; i < 1000000; i++) {
            System.out.println(i);
            try {
                //调用了线程中断,可以唤醒在睡眠中的线程,并且清除中断标志
                Thread.sleep(20);
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println(Thread.currentThread().getName() + " 在睡眠中被中断...");
            }
            if(Thread.currentThread().isInterrupted()){
                System.out.println("sleep....中断标志位true");
                break;
            }
        }
    }

    private static void waitInterrrupt() {
        for (int i = 1; i < 1000000; i++) {
            synchronized (object) {
                System.out.println(i);
                try {
                    //wait在等待中的线程也可以被中断线程唤醒,并且清除中断标志
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    System.out.println(Thread.currentThread().getName() + " 在睡眠中被中断...");
                }
                if(Thread.currentThread().isInterrupted()){
                    System.out.println("wait....中断标志位true");
                    break;
                }
            }

        }
    }

    private static void parakInterrrupt() {
         for (int i = 1; i < 1000000; i++) {
            System.out.println(i);
             //wait在等待中的线程也可以被中断线程唤醒,不会清除中断标志
            LockSupport.park();
            if(Thread.currentThread().isInterrupted()){
                System.out.println("parak....中断标志位true");
                break;
            }
        }
    }
}

上面有三个线程,分别是测试sleep、wait和parak在阻塞的情况下,通过线程的interrupt标志是否能唤醒线程,就是通过设置线程的中断标志是否能够唤起阻塞的线程,我们看下输入结果:

1
1
Thread-lock parak....中断标志位true
Thread-sleep 在睡眠中被中断...
2
1
Thread-wait 在睡眠中被中断...
2
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at org.bml.juc.cas.interruptedTest.waitInterrrupt(interruptedTest.java:57)
	at org.bml.juc.cas.interruptedTest.lambda$main$0(interruptedTest.java:17)
	at java.lang.Thread.run(Thread.java:748)
java.lang.InterruptedException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at org.bml.juc.cas.interruptedTest.waitInterrrupt(interruptedTest.java:57)
	at org.bml.juc.cas.interruptedTest.lambda$main$1(interruptedTest.java:21)
	at java.lang.Thread.run(Thread.java:748)

可以看到设置了线程中断标志过后都能被唤醒,所以线程中断标志是可以唤醒sleep、wait和park的线程的,但是他们有区别,首先sleep在睡眠期间如果设置了线程中断,那么会进入InterruptedException异常,我们可以在异常中去捕获中断线程的信息;而wait必须在同步代码块中使用,在wait期间设置线程中断标志也是能够唤醒wait的线程,但是也是要捕获InterruptedException异常,也可以在异常中获取中断信息;parak也可以接受到中断信号,其实对于park来说,用interrupt和unpark也是一样的,对于LockSupport来说,设置中断标志和发一个许可unpark也是一样的效果;但是sleep和wait与parak有两个区别:
1.park接受到线程中断不是出现异常,类似于unpark的效果一样,唤起处于阻塞的线程;
2.wait和sleep在阻塞期间,如果设置了线程的中断标志,那么会立马清除中断标志,而park是不会清除中断标志,简单来说就是,这三个在阻塞期间,如果设置了中断标志,那么只有parak这个不会清除中断标志,sleep和wait都会清除中断标志,也就是线程的中断标志由true变成了false;

CAS原理

CAS是什么呢?可能没有接触过底层的程序员来说,cas用在底层的特别多,包括synchronized,juc包下面的很多api都用了cas,包括阻塞队列等;如果说你对cas不熟悉,那么AtomicInteger大家总是用过了,大家都知道非常典型的线程安全例子i++,大家都知道,但是我们使用了AtomicInteger就能保证原子性了,大家有没有疑问,为什么它能保证原子性,而AtomicInteger底层没有使用任何的锁机制来保证原子性,那么它是如何保证原子性的呢?这边我们来揭开它的面纱,它底层就是使用的CAS自旋锁来保证原子性,简单来说就是AtomicInteger的值是vloatile的,值是在内存中是可见的,然后通过cas来进行比较,cas就是比较和替换的操作。

什么是CAS

CAS可以看做是乐观锁的一种实现方式,Java原子类中的递增操作就通过CAS自旋实现的。
CAS全称Compare And Swap (比较与交换),是一种无锁算法。在不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步。
LOCK_IF_MP(%4) "cmpxchgl %1,(%3)“ lock cmpxchgl:汇编指令(CAS操作的汇编指令)
unsafe.compareAndSwapInt(this, valueOffset, expect, update)
this:需要改变属性值的对象,也是在内存中获取属性偏移量的对象;
valueOffset:value变量在内存中的偏移量的内存地址;
expect:期望更新的值
update:比较成功过后要更新的值
如果原子变量中的value,也就是从内存中获取的最新值等于expect,则使用update更新最新的值,然后返回true,否则返回false.

AtomicInteger(CAS典型使用案例)

我们就用AtomicInteger来简单了解下CAS的原理;

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;
    

private static final Unsafe unsafe = Unsafe.getUnsafe():申明一个全局静态的唯一Unsafe静态对象,cas操作必须要借助于Unsafe对象,Unsafe 是可以直接操作内存,操作内存做什么呢?我们都知道我们的java对象都存放在内存中,所有通过Unsafe 可以直接拿到我们要比较的值在内存中的最新值;
private static final long valueOffset:这个叫类的属性在内存中的偏移量,也就是可以拿到我们在内存中的最新的值;
private volatile int value:内存中的值为什么要加volatile ?因为AtomicInteger是在多线程环境下保证数据安全的的操作,所以首先要保证整个值在内存中对所有的线程可见,所以是volatile修饰的,这个值是配合对象使用的。
这边用一个自增长的方法做说明:

public final int incrementAndGet() {
    return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int getAndAddInt(Object var1, long var2, int var4) {
    //最后我们要返回的值
     int var5;
    do {
        //var5就是获取我们要比较的值在内存中的偏移量的最新的值,它是通过
        //var1和var2进行获取的,如果不好理解,你就理解下比如有个类Demo,Demo中
        //有个属性a,然后如果你要获取整个a的值,那么你可以通过Demo的对象.getA()
        //来获取,下面的这个方法类似这种效果,就是通过var1(value值所指的对象this)
        //和var(value)来获取,getIntVolatile是本地方法,在c++中实现的,java中没有实现
        var5 = this.getIntVolatile(var1, var2);
        //使用do while循环一直去cas,直到成功为true过后,方可成功
        //这个过程中:var1和var2主要是用来获取value在内存中的最新值
        //var5是上面的第一次从内存中获取的最新值,那么为什么还要把var1和var2传过去呢?
        //因为cas是在c++中实现的,你真正去调用的时候,可能其他线程已经把值该了
        //这个时候cas是不能成功的,需要从新从内存中获取最新的值再次进行比较
        //var5就是每次需要比较的值,var5+var4就是cas成功过后要更新的值
    } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

    return var5;
}

compareAndSwapInt jvm中的实现

openjdk的路径\hotspot\src\share\vm\prims\unsafe.cpp

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x))
  UnsafeWrapper("Unsafe_CompareAndSwapInt");
  oop p = JNIHandles::resolve(obj);
  jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
  return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
  

上面的这个cas的代码就是我们在上面的方法中调用compareAndSwapInt本地方法执行
我们知道我们的对象在c++中就是一个oop,在jvm笔记中已经详细介绍了,那么
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *) index_oop_from_field_offset_long(p, offset);
这两行代码就是获取我们要比较的值在内存中的最新值
p=var1
offset=var2
最后调用Atomic::cmpxchg进行cas
在这里插入图片描述

CAS缺点

1.只能保证一个变量的原子操作;
2.如果竞争激励,可能长期更新不成功,自旋长期占用CPU的时间片;
3.ABA问题。

什么是ABA问题

比如说,一个值等于1,线程1要更新成2,线程2这个时候把这个值更新为3后,在线程2更新之前,又把这个值更新成了1,那么线程2发现这个是是1,cas是成功的,所以这就是aba问题。
举个例子:某某公司财务职工,从公司挪用了100w去投资,一个月后,赚了150w,然后在把100万还回去了,自己赚了50w,而原来的100w其实已经不是之前的100w了。

public class ABATest {

    public final AtomicInteger sum = new AtomicInteger(0);


    public static void main(String[] args) {

        ABATest aba = new ABATest();
        Thread thread1 = new Thread(() -> {
            aba.thread1();
        }, "Thread-1");

        Thread thread2 = new Thread(() -> {
            aba.thread2();
        }, "Thread-2");
        thread1.start();
        thread2.start();
    }


    private void thread1() {

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " 开始执行...");

        if (sum.compareAndSet(0, 1)) {
            System.out.println(Thread.currentThread().getName() + " update sum 0 to 1 success");
        } else {
            System.out.println(Thread.currentThread().getName() + " update sum 0 to 1 failure");

        }


    }

    private void thread2() {

        System.out.println(Thread.currentThread().getName() + " 开始执行...");

        sum.compareAndSet(0, 1);

        System.out.println(Thread.currentThread().getName() + "update sum 0 to 1");

        sum.compareAndSet(1, 0);
        System.out.println(Thread.currentThread().getName() + "update sum 1 to 0");


    }
}

上面的代码就是启动2个线程,cas 的value初始值为0,线程1要把0修改为1,线程1由于某些原因需要等待2s后执行,这个时候线程2启动了,线程2将0修改为1过后,又把值修改为0,那么这个时候线程1开始运行了,线程1发现value还是0,就更新了,那么这就是ABA问题,类似上面举个例子
输出如下:

Thread-2 开始执行...
Thread-2update sum 0 to 1
Thread-2update sum 1 to 0
Thread-1 开始执行...
Thread-1 update sum 0 to 1 success

如何解决ABA问题

那么我们知道了cas的ABA问题,那么我们如何解决呢?在jdk中Doug Lea大神已经给我们想到了,在jdk中解决ABA问题的一个原子类操作AtomicStampedReference,这个类在cas的时候api有4个参数
expectedReference:期望更新的值(就是期望比较的值)
newReference:cas成功过后的更新的值
expectedStamp:期望的版本号(也就是cas前的版本号)
newStamp:cas成功过后的版本号
废话不多说,来看实例:

public class ResoveAbaTest {


    //初始的值为aa,版本号为0
    private final AtomicStampedReference<String> atomicStampedReference =
            new AtomicStampedReference<>("aa", 1);

    public static void main(String[] args) {
        ResoveAbaTest aba = new ResoveAbaTest();
        Thread thread1 = new Thread(() -> {
            aba.thread1();
        }, "Thread-1");

        Thread thread2 = new Thread(() -> {
            aba.thread2();
        }, "Thread-2");
        thread1.start();
        thread2.start();
    }


    private void thread1() {

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + " 开始执行...");

        if (atomicStampedReference.compareAndSet("aa", "bb", 1, 2)) {
            System.out.println(Thread.currentThread().getName() + " update atomicStampedReference 'aa' to 'bb' success");
        } else {
            System.out.println(Thread.currentThread().getName() + " update atomicStampedReference 'aa' to 'bb' failure");
        }
    }

    private void thread2() {
        System.out.println(Thread.currentThread().getName() + " 开始执行...");

        atomicStampedReference.compareAndSet("aa", "bb", 1, 2);

        System.out.println(Thread.currentThread().getName() + "update atomicStampedReference 'aa' to 'bb'");

        atomicStampedReference.compareAndSet("bb", "aa", 2, 3);
        System.out.println(Thread.currentThread().getName() + "update atomicStampedReference 'bb' to 'aa'");
    }
}

代码逻辑比较简单,和ABATest逻辑一样,看结果

Thread-2 开始执行...
Thread-2update atomicStampedReference 'aa' to 'bb'
Thread-2update atomicStampedReference 'bb' to 'aa'
Thread-1 开始执行...
Thread-1 update atomicStampedReference 'aa' to 'bb' failure

这个时候失败了,线程2将值修改成了bb,由修改到aa,线程1开始启动,修改,但是这个时候修改失败了,因为线程1发现你已经不是原来的你了(o( ̄︶ ̄)o),你已经变了,我不能接受你了。所以就是这个道理,这个过程中其实就是用了stamp来控制的,我们把它理解成版本号,反正就是一个标志。在实际的工作开发过程中,我们要特别注意的就是如果使用上面的场景的话,那么每次cas过后的版本号不能手动设置,需要设置一个只能增长,不能回退的版本号来控制,当然了这个得具体看的业务场景,因为如果这个版本号可以随便设置,那么还是可能会出现,你把expect的值又给你修改回来了,版本号也是给你改回来了,那么其实还是存在ABA问题的。所以这个就的看你的业务场景和实现的考量。

Java线程中断攻略: 详解Java线程中断及线程中断的几种使用场景 在多线程编程中,线程中断技术是确保程序健壮性和可靠性的不可或缺的一部分。本文将深入探讨Java中的线程中断技术,以通俗易懂的方式介绍其概念、机制以及在实际项目中的应用。 阅读详情

相关推荐

Python实战:用MNE-Python从脑电数据中提取P300成分(附完整代码)

本文提供了一份使用MNE-Python从脑电数据中提取P300成分的完整实战指南。通过详细的代码示例,系统讲解了从环境搭建、数据加载、预处理(滤波、分段、伪迹剔除)到P300波形提取、可视化与参数量化的全流程。重点介绍了如何利用零相位滤波和叠加平均技术,从EEG信号中精准分离出反映认知加工的P300事件相关电位,适用于脑机接口与认知神经科学研究。

weixin_29240343的博客 338

2022-11-03 C++并发编程( 三十九 )

上篇文章我们介绍了线程池, 比较复杂, 本文会在线程池的基础上增加中断线程, 完善线程的较高级操作.线程的中断需要让线程主动而安全的停止, 通过信号的传入结束线程, 将其停止运行.线程中断部分已经讲解完成, 代码稍显复杂, 可中断线程类是可以不必配合线程池使用的, 但基本的运用方法和线程池差不多.

老林的博客 672

Conformal LEC逻辑等价性检查介绍和参考脚本

分为三个产品:Conformal L、Conformal XL、Conformal GXL。L是功能性检查,XL支持复杂数据通路的综合和APR优化,GXL支持定制的stdcell和定制memory的检查。Cadence Conformal Equivalence Checker,就是常说的LEC,可以用来验证RTL、门级网表、甚至spice网表的逻辑等价性。do文件是conformal的脚本文件,可以由综合工具(RTL Compiler/Genus)自己产生(write_do_lec),也可以手写。

NanDigits的博客 7256

线程中断的方式(interrupt详解)

1.线程中断的方式总结? 1.正常退出:在线程start()开启之后,当线程执行完run()或call()方法之后,此时,该线程会正常退出。 2.stop强制退出:在Thread类中,可以发现其中的stop()方法上加有@Deprecated注解,表示此方法已废弃、暂时可用,但以后此类或方法都不会再更新、后期可能会删除,建议后来人不要调用此方法。其主要原因是线程会立即停止,而且线程拥有的资源不会释...

sjh10000的博客 5015

【附源码】Node.js毕业设计高校院级校友信息管理系统(Express)

首先,该系统可以提高高校对校友信息管理的效率和准确性。通过系统提供的查询功能,高校可以方便地获取校友的最新联系方式和动态,及时了解校友的发展情况,为校友提供更好的服务和支持。总之,开发高校院级校友信息管理系统将为高校提供一个高效、便捷的工具,有助于提升校友管理水平和服务质量,促进高校与校友之间的良好互动关系。通过使用前端技术HTML、CSS、JavaScript和Vue框架,结合后端技术Node.js和Express框架,以及MySQL数据库,可以实现对校友信息的快速录入、查询、修改和删除等操作。

佳愉毕设源码 894

YOLOv11 损失函数解析

YOLOv11 在 YOLO 系列的持续演进中,通过 ​多模态损失协同优化、动态任务感知机制 和 ​超轻量化设计,实现了检测精度与推理效率的再突破。其损失函数设计融合了前沿的几何建模、分布学习与自监督技术,显著提升了复杂场景下的鲁棒性。

浩瀚之水的专栏 6702

【线程】线程中断详解

文章目录中断线程判断线程是否被中断如何中断线程(中断原理)底层中断异常处理方式 中断线程 线程的thread.interrupt()方法是中断线程,将会设置该线程的中断状态位,即设置为true,中断的结果线程是死亡、还是等待新的任务或是继续运行至下一步,就取决于这个程序本身。线程会不时地检测这个中断标示位,以判断线程是否应该被中断(中断标示值是否为true)。 它并不像stop方法那样会中断一个正在运行的线程。 判断线程是否被中断 判断某个线程是否已被发送过中断请求,请使用Thread.currentThr

m0_45406092的博客 2903

JavaFX Stage

JavaFXStagejavafx.stage.Stage表示 JavaFX 桌面应用程序中的窗口。在 JavaFX 内部,Stage您可以插入一个 JavaFX Scene,它表示在窗口内显示的内容 - 在Stage.当 JavaFX 应用程序启动时,它会创建一个根Stage对象,该对象将传递给 start(Stage primaryStage)JavaFX 应用程序的根类的方法。此Stage 对象代表 JavaFX 应用程序的主窗口。

2012年开始工作,全栈开发老兵,目前已是一个老师,分享这么多年的心得体会 508

终止线程的方法

转载自:https://zhidao.baidu.com/question/366236031.html?qbl=relate_question_0&word=java%CF%DF%B3%CC%D0%E8%D2%AA%B9%D8%B1%D5%C2%F0 终止线程的三种方法 1. 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。 2. 使用stop方法强行终止线程(这个方法...

Bernard Han的博客 998

SAP RAP 里的 EML 全解:从语言定位到事务语义,再到真实案例的落地方式

摘要:EML(Entity Manipulation Language)是ABAP RAP模型的核心组件,作为ABAP的子集用于操作业务对象(BO)。它通过声明式语法(READ/MODIFY/EXECUTE/COMMIT)统一访问BO的行为与数据,并由RAP运行时自动处理校验、锁定、草稿管理等生命周期。EML将业务语义融入代码,确保与Fiori应用共享同一套校验逻辑,并通过FAILED/REPORTED/MAPPED管道传递错误与消息。其事务控制(IN LOCAL MODE与COMMIT ENTITIES)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 528

【CAN总线测试】——使用VH6501 进行Busoff快慢恢复测试

即在IDBase中填标准帧11位二进制ID,如果不满11位,高位补零,凑齐11位,并将下方CurrTriggerField设置干扰前一位AckSlot位,Triggeroffset设置为0。首先确认环境支持VN6501,6501硬件连接成功后,工程的小闪电是正常黄色状态,若是灰色证明缺少License,或者软件安装环境不正确。填写干扰报文ID、干扰位置 注意这里的干扰位置是实际干扰的前一位,TriggerOffset表示偏移量,可以干扰某个字节的具体位.找到并打开Panel后,开启工程,并且配置面板。

LOVE135149的博客 5633

【Java】Callable&锁&CAS

总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;非公平锁:可以插队,比如有些线程执行时间很短,但是也需要等待拿到锁的线程执行完(3h,3s),synchronized和lock默认都是非公平锁。...

X_xiaojuzi的博客 308

UNet改进(54):当医学图像分割遇上频域学习与自监督

本文提出了一种改进U-Net的方法,通过频域增强模块和代理任务模块提升医学图像分割性能。频域增强模块在傅里叶空间对特征图进行可学习的频率调制,重点增强高频细节特征;代理任务模块通过图像重建约束编码器保留关键信息。两个模块协同工作,在仅增加5%参数量的情况下,形成空间域、频域和重建约束三条互补的信息路径。该方法特别适用于需要精细边缘分割的医学图像任务,预期能提升5%-10%的边缘分割精度,在小样本场景效果更显著。部署时可移除代理任务模块,不影响推理效率。未来可探索空间自适应频域调制和更丰富的代理任务设计。

2501_91516851的博客 227

什么是线程中断?

下面的这断代码大家应该再熟悉不过了,线程休眠需要捕获或者抛出线程中断异常,也就是你在睡觉的时候突然有个人冲进来把你吵醒了。 try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } 此时线程被打断后,代码会继续运行或者抛出异常结束运行,这并不是我们需要的中断线程的作用。 到底是什么是线程中断? 线程中断即线程运行过程中被其他线程给打断了,它与 stop 最大的区别是:stop 是由系

meser88的博客 2469

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

线程中断

线程中断就是处于运行状态的线程被强制打断。线程中断总体来说有三种方法:正常退出、stop暴力停止、interrupt异常停止。其中使用stop()方法是不安全的,可能导致数据不同步或者资源无法回收,目前stop()方法已经被标注为作废方法。一般使用interrupt停止线程,这里有几个与之相关的方法: public void interrupt() {} // 中断线程 public boo...

写写代码 3075

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

线程中断机制,使用中断标识停止线程的的3种方法(附代码示例)

1. 什么是中断? 1. 一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread stop, Thread. suspend, Thread.resume都已经被废弃了。 2. 在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制————中断。 3. 中断只是一种协作机制(并不能立刻stop一个线程),Java没有给中断增加任何语法,中断的过程完...

枫陵的博客 1920

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

线程的中断详解

JAVA中的线程,有一个状态叫做中断状态,用于标记线程是否被中断过。通过对线程中断状态的判断,可以实现一些例如优雅终止线程,唤醒线程等功能。 在Thread类中有interrupt(),interrupted()和isInterrupted()方法与线程的中断有关,本篇文章将对这些方法的具体作用进行详细解释。在文章的最后,还会通过一个典型例子,演示如何通过线程的中断状态来优雅的终止线程。

用朴素的话说技术。 867

CAS操作

1 .CAS :Compare And Swap (比较交换机制)–乐观锁 悲观锁:(JDK1 . 6之前的内建锁)假设每一次执行同步代码块均会产生冲突,所以当线程获取锁成功,会阻塞其他尝试获取该锁的线程。 乐观锁:(Lock)假设所有线程访问共享资源时不会出现冲突,既然不会出现冲突自然就不会阻塞时其他线程。线程不会出现阻塞状态。 CAS(无锁操作),使用CAS叫做比较交换来判断是否出现冲突,出现...

Burder的博客 1028

【JAVA-Day83】线程中断

欢迎来到默语博客!今天我们将深入探讨线程插队的概念,并探讨在Java中如何实现线程的中断。我们将详细介绍线程中断的概念、使用场景以及相关面试题,让你轻松掌握这一重要知识点。让我们开始吧!在多线程编程中,线程中断是一种线程之间协作的机制,用于在一个线程执行特定任务之前先执行另一个线程的现象。通常情况下,线程中断用于实现优先级任务的执行顺序控制或者某些条件的等待和唤醒。具体来说,当一个线程需要在另一个线程执行完特定任务后再执行时,可以通过线程中断来实现。

沉淀所学,分享所思,让热爱与成长同行。商务记录AI实战经验,助力开发者快速成长。 热爱AI,希望做出有影响力的技术成果。 技术点亮生活,分享连接价值与机会。 专注AI落地实战,陪你走好技术每一步。 2万+

线程中断:interrupt

线程中断的几种方式的详细讲解

m0_62160964的博客 828

Java 线程中断机制详解

Java 线程中断是一种强大而优雅的线程协作机制,用于请求线程停止其当前任务。它并非强制终止,而是依赖于目标线程的主动响应。

左灯右行的爱情 1449

线程的中断

本文介绍了Java线程中断机制,主要包括中断概念、相关方法和代码示例。这些示例展示了线程如何主动检查并响应中断信号,实现终止。

2401_84284464的博客 678

一文搞懂Java线程中断协商机制,如何优雅中断一个正在运行的线程?

一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运。所以,Thread.stop()、Thread.suspend、Thread.resume()等关于强制线程停止的方法都已经废弃了。在Java中,没有办法立即停止一个线程,然而停止线程本身这个功能是很重要的,比如说取消一个耗时的操作。中断,也即中断标识协商机制。Java的中断只是一种协商机制,Java并没有给中断增加任何语法,中断的过程完全需要程序员自己来实现。

秃了也弱了 1630

【并发基础】一篇文章带你彻底搞懂Java线程中断的底层原理——interrupt()、interrupted()、isInterrupted()

一篇文章带你彻底搞懂Java中断机制,从底层源码带你分析interrupt()的机制与用法。学习Java中断看这一篇就够了。

小七的博客 5909

第五章:线程中断机制

什么是中断? 首先一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止。所以,Thread.stop, Thread.suspend,Thread.resume 都已经被废弃了。 其次在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供了一种用于停止线程的机制——中断。 中断只是一种协作机制,Java没有给中断增加任何语法,中断的过程完全需要程序员自己实现。若要中断一个线程,你需要手动调用该线程的interrupt方法,该方法也仅仅是将线

weixin_48276298的博客 486
上一篇: Synchronized详细介绍之锁升级过程
下一篇: 并发编程之AQS(抽象队列同步器)独占锁
Life  journey
博客等级 码龄17年 1万+粉丝 80原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值