精华内容
下载资源
问答
  • 9.5 内部中断分类说明

    2019-09-20 21:03:14
    那么就来花一点时间,看一看内部中断到底有哪些不同类型。 我们还是以x86实模式为例,这个比较简单,但基本原理都是一样。 在x86实模式下,我们要来分析内部中断就是这四个。 这四个内部中断所使用类...

    计算机组成

    9 中断和异常

    9.5 内部中断分类说明

    Screen Shot 2018-09-30 at 9.53.53 am

    现在,我们已经知道了中断处理的基本过程。那么就来花一点时间,看一看内部中断到底有哪些不同的类型。

    我们还是以x86的实模式为例,这个比较简单,但基本原理都是一样的。

    Screen Shot 2018-09-30 at 9.54.02 am

    在x86的实模式下,我们要来分析的内部中断就是这四个。

    Screen Shot 2018-09-30 at 9.54.10 am

    这四个内部中断所使用的类型号,分别是0、1、3、4。而加在中间的类型2,是留给外部中断的,这个非屏蔽中断是外部中断的一种。

    现在,就分别来看一看这四个内部中断。

    Screen Shot 2018-09-30 at 9.54.19 am

    首先来看类型0的中断。这个中断是和除法有关的,我们不妨来回顾一下除法器的结构,在这个除法器支持的运算中,有一个64位的被除数和一个32为的除数,运算产生的商和余数,分别放到两个32位寄存器当中去。这样的设置对于多数除法运算来说,都是没有问题的。但难免有一些比较特别的情况,例如除数很小,比如说就是2,而被除数很大。那么一个很大的被除数除以2之后,得到的商就会超过32位,没有办法放进这个商寄存器当中去。当运算器遇到这种情况时,就会产生一个除法错中断,这个中断的类型号是0。所以,CPU就会去中断向量表中取出0号中断向量,然后去执行对应的中断服务程序。

    那有一种情况,就是用0作除数,这样得到的商应该是无穷大,肯定超过目标寄存器所能表示的范围。所以,这个除法错中断有时也会被称为除0中断。

    Screen Shot 2018-09-30 at 9.54.29 am

    然后我们再来看4号中断,这个中断叫作溢出中断。也就是因为算术运算发生了溢出而引起的中断,这个中断的产生要借助一条特殊的指令,也就INTO指令。当执行这条指令时,硬件电路会去检查溢出标志位OF是否位1。如果为1,则会引起类型为4的内部中断。INTO这条指令的格式如图,它是一个没有操作数的指令,比如 add ax,bx 这条加法指令执行时,就有可能发生了溢出,那么运算器在运行完这个加法后,会去设置标志寄存器当中的标志位,也就是第11号溢出标志位,但这个操作本身并不会引发中断,只是将标志位置1。但如果之后执行了INTO指令,这条指令是会去检查OF标志位。如果这时OF标志位为1,那就引起了4号中断;但是如果INTO指令执行时,OF标志位为0,那就什么也不会发生,这条INTO指令就相当于一条空操作指令。所以,INTO指令通常会安排在算术运算指令之后,用来检查这个运算是否发生了溢出。并且,在发生溢出时,就调用中断程序进行处理。因为这是4号中断,所以我们也可以写成INT 4这样的形式。注意INT和4之间有一个空格,这个4是INT指令的操作数。实际上,任何一个类型的中断,都可以采用这样的形式进行调用,用INT指令带上这个中断的类型号,我们在后面还会看到这样的例子。

    我们要注意区分的是,这个4号中断和刚才介绍的0号中断,在引起中断的时机上是有区别的。虽然它们都在检查运算时出现的异常情况,但是0号中断是在那条除法指令执行后,立刻发生的,而4号中断则是要在编程时,加入INTO指令进行主动的检查。因为很多时候,这样的加法运算的溢出,并不需要进行处理,如果每一次溢出,都要引发中断,反而可能影响程序的性能。所以,在指令系统设计的时候,就把是否要检查这种溢出的情况,交给程序员来进行判断。

    那么类型0和类型4这两个中断,都是和运算结果出现了异常情况有关系的;而另两个内部中断,则是主要用来进行错误调试的。

    Screen Shot 2018-09-30 at 9.54.37 am

    其中,类型1中断称为单步中断。要引发这个中断,需要将标志寄存器当中的TF位置1,这时CPU就处于单步工作方式。在单步工作方式下,CPU每执行完一条指令,就会自动的产生一个类型1的中断,然后进入类型1中断服务程序。这种工作方式主要是用来进行错误调试的。比如说,你发现CPU执行一段程序有错误,但是又不清楚这个错误具体发生在什么地方,那就可以将TF标志位置为1,在单步工作方式下进行调试。

    通常情况下,我们会在这个类型1的中断服务程序当中,将CPU当中的各个寄存器的内容,在屏幕上显示出来,这样CPU每执行一条指令,我们就可以在屏幕上看见CPU当前正在执行的是哪一条指令,这条指令的地址是什么,执行这条指令的前后,那些通用寄存器又有什么样的变化。这样我们就有可能发现,到底在哪一步,发生了不符合我们预期的行为。这个方式对于调试是很有用的,但是CPU每执行完一条指令,就要产生一个中断,程序执行的速度就非常慢了。如果想要调试一个很大的程序,仅用单步中断就会变得比较困难。所以,还有一个用于调试的中断,就是类型3,断点中断。

    Screen Shot 2018-09-30 at 9.54.47 am

    断点中断通常和单步中断配合使用。在调试一个很大的程序时,一般我们会先通过断点中断,将错误定位在这个程序的一小段代码中,然后,再对这一小段代码用单步的方式进行跟踪调试。这样就可以大大提升调试的效率,这个思想也是很简明的,如果我一个大的程序运算结果出现问题,我们并不会马上从这个程序的开头,一条一条指令的顺序检查,而通常会是将这个程序切成几个大的部分,然后检查每一个部分的结果是否正确。

    我们刚才提过,所有的中断都可以用指令的形式来调用,那么用INT加中断类型号的这个形式的指令,都是一个两字节的指令。只有断点中断是一个例外,INT 3指令是一条单字节长的指令,这就是INT 3指令的编码,11001100。

    那为什么INT n形式的指令都是两个字节的呢?我们只用想一想这个n要表达多大的范围,我们一共有256个中断类型。所以,这个n要表示0到255,要表示这些数,需要多少个二进制位呢?需要8位,2的8次方就是256。那在前面,还得有一个字节的指令操作码,所以总共是两个字节。

    我们为什么要单独给3号中断,设置一个单字节长的指令编码呢?

    Screen Shot 2018-09-30 at 9.54.55 am

    这和它的使用方式是有关系的。这个断点中断指令的使用并不那么简单,我们要在需要调试的程序当中,选择一个希望中断的位置,然后用这条断点中断指令,去代替这个位置原有的指令。当然,我们需要把原有的这条指令保存起来。这个都是要由调试人员手工来完成的,替换完以后,我们再次运行这个程序,用户程序运行到我们选好的这个中断点的时候,它就执行了INT 3这条指令,从而进入了对应的中断服务程序。我们就可以在这个中断服务程序当中,将CPU的各个寄存器的值都打印在屏幕上,从而判断执行到这个断点的时候,这个用户程序是否还运行正常。如果运行正常,可能我们就需要把这个断点再往后挪一挪;如果这个时候,已经有寄存器的值不符合我们的预期了,我们就需要将断点放到更靠前的位置,进行进一步的检查。但我们还得记得,在这个中断服务程序当中,需要将这个断点位置原有的那条指令的编码再替换回去,并且将指令指针寄存器的值再回退一个字节,也就是指向这个原有的指令,以保证中断返回之后,CPU能从断点的这个地方继续执行。

    我们来看一个例子。

    Screen Shot 2018-09-30 at 9.55.04 am

    假设这是我们要调试的一段程序。这里有5条指令,左边是它们对应的指令的地址,其中有一些是两个字节的指令,有一些是一个字节的指令。如果我们想选择这条 inc al 指令作为断点,那么就需要把这条指令的编码,替换成 int 3 指令的编码。这时候就体现出了 int 3 这条指令是一个单字节指令的好处。因为x86的指令当中,最短的就是一个字节的指令,像这条 inc al 指令。如果断点中断指令是一个两字节的指令,那么在替换进来之后,就会影响到后续的指令,而后续的指令,却有可能在这个断点之前执行。比如说就像这段程序代码,在这个断点之前,就有一个转移指令,直接跳到了断点之后,然后经过条件判断,可能又跳转回来,才继续执行到断点的地方。所以,我们将这条 inc al 指令替换成断点中断指令的时候,一定不能影响后续的指令,这也就是为什么断点中断指令必须要是一个字节的。那么在这段程序执行的过程中,如果这个条件(转移)已经发生了,就会运行到断点中断指令,然后CPU内部就会发生中断,转而去执行3号中断向量所对应的中断服务程序。在这个中断服务程序中,我们就可以把AL寄存器的内容打出来,这样调试人员就可以观察到,这个时候AL寄存器的内容,是否符合我们的预期了,如果我们发现AL寄存器的内容有错误,那么就可以再次运行这个程序,并在附近的位置设置CPU进入单步工作模式,进行单步调试。这样就会比较容易的发现一些隐藏的很深的错误。

    Screen Shot 2018-09-30 at 9.55.15 am

    这些内部中断都有共同的特点。

    首先,它们的中断类型号是由CPU内部产生的。因为这些异常的情况,就是CPU自己在执行指令的过程中发生的。所以,它是知道到底发生的是什么类型的中断。而我们后面要介绍的外部中断,则有可能来自不同的外部设备。所以,CPU需要去读取外设,以得知中断类型号。这是第一个区别。

    第二个区别,是屏蔽的方式。在内部中断当中,除了单步中断以外,都不可以用软件的方式来进行屏蔽。也就是,我们不可以通过设置IF这个标志位,来让CPU不响应内部中断。

    第三个是优先级。也就是内部中断和外部中断同时发生时,CPU先处理哪个中断。那么除了单步中断以外,所有的内部中断优先级都比外部中断高。CPU总是优先处理自己内部发生的异常情况。

    Screen Shot 2018-09-30 at 9.55.23 am

    现在,我们已经了解了内部中断的基本类型。有两个是用来处理运算的异常情况,还有两个是CPU用来调试的。那后来,随着内部中断的类型不断的增加,其中增长(zhang)的大部分,都是CPU用来调试和管理用的中断。

    转载于:https://www.cnblogs.com/houhaibushihai/p/9743473.html

    展开全文
  • 上面是很多锁名词,这些分类并不是全是指锁状态,有的指锁特性,有的指锁设计,所以可能两种锁名词可以同时形容一种关键字,下面总结内容是对每个锁名词进行一定解释。 首先明确锁是什么东西,...

    在Java中锁的种类主要有这些:

    • 公平锁/非公平锁
    • 可重入锁
    • 独享锁/共享锁
    • 互斥锁/读写锁
    • 乐观锁/悲观锁
    • 分段锁
    • 偏向锁/轻量级锁/重量级锁
    • 自旋锁
    • 可中断锁

    上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,所以有可能两种锁的名词可以同时形容一种关键字,下面总结的内容是对每个锁的名词进行一定的解释。

     

    首先明确锁是什么东西,要理解锁的概念:

    锁是在资源中的,是要访问资源(如对象实例,Class类实例,属性变量,代码块等)的一部分,线程只有获取该资源的锁才有权访问这个资源,这个就是锁机制。线程想要持有资源的锁。资源的锁有很多种,下面就对锁的种类进行讲解。

    java的内置锁:每个java对象都可以用做一个实现同步的锁(因为每一个Java对象都对应着一个monitor对象,monitor对象就是实现锁机制的基础),这些锁称为内置锁(也就是Monitor锁)。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。

    获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

    java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,

    如果B线程不释放这个锁,那么A线程将永远等待下去。

     

    公平锁/非公平锁

    它们指的锁的两种种类

    公平锁是指多个线程按照申请锁的顺序来获取锁。

    非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。(非公平锁就是抢占式的)

    • 对于ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
    • 对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

     

    可重入锁

    它指的锁的一种种类

    可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

    • 对于ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
    • 对于Synchronized而言,也是一个可重入锁。synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题,即已经获取该资源的锁的线程,如果该线程再次访问该资源,系统会判定当前资源的锁已经被线程持有(实际是被该线程自己持有)无法获取该锁,需要阻塞等待,然后该线程就会进入阻塞状态,而锁本来就在它这,阻塞状态锁也释放不了,也获取不了,就进入到了死锁状态。可重入锁的一个好处是可一定程度避免死锁,可重入锁可以使持有锁的线程重复访问资源,不会发生死锁。
    synchronized void setA() throws Exception{
        Thread.sleep(1000);
        setB();
    }
    synchronized void setB() throws Exception{
        Thread.sleep(1000);
    }

     

    上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

     

    独享锁/共享锁

    它们指的锁的两种种类

    独享锁是指该锁一次只能被一个线程所持有。

    共享锁是指该锁可被多个线程所持有。

    • 对于ReentrantLock而言,其是独享锁。
    • 对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
      • 读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的(操作系统中的读者-写者问题)。
    • 对于Synchronized而言,当然是独享锁。

    独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

     

    互斥锁/读写锁

    它们指的锁的两种种类的具体实现

    上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

    • 互斥锁在Java中的具体实现就是ReentrantLock
    • 读写锁在Java中的具体实现就是ReadWriteLock
    • Synchronized也是互斥锁

     

    乐观锁/悲观锁

    乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

    悲观锁认为对于同一个数据的并发操作,一定是会发生修改的(或者增删改多,查少),哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题。

    乐观锁则认为对于同一个数据的并发操作,是不会发生修改的(或者增删改少,查多)。在更新数据的时候,会采用不断尝试更新的方式来更新数据。也就是先不管资源有没有被别的线程占用,直接取申请操作,如果没有产生冲突,那就操作成功,如果产生冲突,有其他线程已经在使用了,那么就不断地轮询。乐观的认为,不加锁的并发操作是没有事情的。就是通过设置多个版本,如果修改完之后发现有冲突再将版本返回到没修改的样子,乐观锁就是不加锁。

    从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升

    • 悲观锁在Java中的使用,就是利用各种锁。
    • 乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

     

    分段锁

    分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

    我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap(JDK7与JDK8中HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLock(Segment继承了ReentrantLock)。

    当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

    但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

    分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

     

    偏向锁/轻量级锁/重量级锁

    这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。这三种锁的状态是通过对象监视器在对象头中的字段来表明的锁是在资源中的,是要访问资源(如对象实例,Class类实例,属性变量,代码块等)的一部分,线程是要取得资源中的锁。

    • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
    • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
    • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

     

    自旋锁

    在Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁(即当循环的条件被其他线程改变时 才能进入临界区),这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU。

    典型的自旋锁实现的例子:

    public class SpinLock {
        private AtomicReference<Thread> sign =new AtomicReference<>();
     
        public void lock(){
            Thread current = Thread.currentThread();
            while(!sign .compareAndSet(null, current)){
            }
        }
     
        public void unlock (){
            Thread current = Thread.currentThread();
            sign .compareAndSet(current, null);
        }
    }

    使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

    当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

    由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

    注:该例子为非公平锁,获得锁的先后顺序,不会按照进入lock的先后顺序进行。

     

    可中断锁

    synchronized 是不可中断的,Lock 是可中断的,这里的可中断建立在阻塞等待中断,运行中是无法中断的。

     

     

    展开全文
  • 目录 锁概念 公平锁/非公平锁 可重入锁 独享锁/共享锁 互斥锁/读写锁 乐观锁/悲观锁 ...可中断锁 ...在Java中锁种类主要这些: ... 可中断锁 上面是很多锁名词,这些分类并不是全是指锁状...

    目录

    锁的概念

    公平锁/非公平锁

    可重入锁

    独享锁/共享锁

    互斥锁/读写锁

    乐观锁/悲观锁

    分段锁

    偏向锁/轻量级锁/重量级锁

    自旋锁

    可中断锁


    在Java中锁的种类主要有这些:

    • 公平锁/非公平锁
    • 可重入锁
    • 独享锁/共享锁
    • 互斥锁/读写锁
    • 乐观锁/悲观锁
    • 分段锁
    • 偏向锁/轻量级锁/重量级锁
    • 自旋锁
    • 可中断锁

    上面是很多锁的名词,这些分类并不是全是指锁的状态,有的指锁的特性,有的指锁的设计,所以有可能两种锁的名词可以同时形容一种关键字,下面总结的内容是对每个锁的名词进行一定的解释。

     

    首先明确锁是什么东西,要理解锁的概念:

    锁是在资源中的,是要访问资源(如对象实例,Class类实例,属性变量,代码块等)的一部分,线程只有获取该资源的锁才有权访问这个资源,这个就是锁机制。线程想要持有资源的锁。资源的锁有很多种,下面就对锁的种类进行讲解。

    java的内置锁:每个java对象都可以用做一个实现同步的锁(因为每一个Java对象都对应着一个monitor对象,monitor对象就是实现锁机制的基础),这些锁称为内置锁(也就是Monitor锁)。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。

    获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。

    java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,

    如果B线程不释放这个锁,那么A线程将永远等待下去。

     

    公平锁/非公平锁

    它们指的锁的两种种类

    公平锁是指多个线程按照申请锁的顺序来获取锁。

    非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁。有可能,会造成优先级反转或者饥饿现象。(非公平锁就是抢占式的)

    • 对于ReentrantLock而言,通过构造函数指定该锁是否是公平锁,默认是非公平锁。非公平锁的优点在于吞吐量比公平锁大。
    • 对于Synchronized而言,也是一种非公平锁。由于其并不像ReentrantLock是通过AQS的来实现线程调度,所以并没有任何办法使其变成公平锁。

     

    可重入锁

    它指的锁的一种种类

    可重入锁又名递归锁,是指在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。说的有点抽象,下面会有一个代码的示例。

    • 对于ReentrantLock而言, 他的名字就可以看出是一个可重入锁,其名字是Re entrant Lock重新进入锁。
    • 对于Synchronized而言,也是一个可重入锁。synchronized同步块对同一条线程来说是可重入的,不会出现自己把自己锁死的问题,即已经获取该资源的锁的线程,如果该线程再次访问该资源,系统会判定当前资源的锁已经被线程持有(实际是被该线程自己持有)无法获取该锁,需要阻塞等待,然后该线程就会进入阻塞状态,而锁本来就在它这,阻塞状态锁也释放不了,也获取不了,就进入到了死锁状态。可重入锁的一个好处是可一定程度避免死锁,可重入锁可以使持有锁的线程重复访问资源,不会发生死锁。
    synchronized void setA() throws Exception{
        Thread.sleep(1000);
        setB();
    }
    synchronized void setB() throws Exception{
        Thread.sleep(1000);
    }
    

    上面的代码就是一个可重入锁的一个特点,如果不是可重入锁的话,setB可能不会被当前线程执行,可能造成死锁。

     

    独享锁/共享锁

    它们指的锁的两种种类

    独享锁是指该锁一次只能被一个线程所持有。

    共享锁是指该锁可被多个线程所持有。

    • 对于ReentrantLock而言,其是独享锁。
    • 对于Lock的另一个实现类ReadWriteLock,其读锁是共享锁,其写锁是独享锁。
      • 读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的(操作系统中的读者-写者问题)。
    • 对于Synchronized而言,当然是独享锁。

    独享锁与共享锁也是通过AQS来实现的,通过实现不同的方法,来实现独享或者共享。

     

    互斥锁/读写锁

    它们指的锁的两种种类的具体实现

    上面讲的独享锁/共享锁就是一种广义的说法,互斥锁/读写锁就是具体的实现。

    • 互斥锁在Java中的具体实现就是ReentrantLock
    • 读写锁在Java中的具体实现就是ReadWriteLock
    • Synchronized也是互斥锁

     

    乐观锁/悲观锁

    乐观锁与悲观锁不是指具体的什么类型的锁,而是指看待并发同步的角度。

    悲观锁认为对于同一个数据的并发操作,一定是会发生修改的(或者增删改多,查少),哪怕没有修改,也会认为修改。因此对于同一个数据的并发操作,悲观锁采取加锁的形式。悲观的认为,不加锁的并发操作一定会出问题

    乐观锁则认为对于同一个数据的并发操作,是不会发生修改的(或者增删改少,查多)。在更新数据的时候,会采用不断尝试更新的方式来更新数据。也就是先不管资源有没有被别的线程占用,直接取申请操作,如果没有产生冲突,那就操作成功,如果产生冲突,有其他线程已经在使用了,那么就不断地轮询。乐观的认为,不加锁的并发操作是没有事情的。就是通过设置多个版本,如果修改完之后发现有冲突再将版本返回到没修改的样子,乐观锁就是不加锁

    从上面的描述我们可以看出,悲观锁适合写操作非常多的场景,乐观锁适合读操作非常多的场景,不加锁会带来大量的性能提升

    • 悲观锁在Java中的使用,就是利用各种锁。
    • 乐观锁在Java中的使用,是无锁编程,常常采用的是CAS算法,典型的例子就是原子类,通过CAS自旋实现原子操作的更新。

     

    分段锁

    分段锁其实是一种锁的设计,并不是具体的一种锁,对于ConcurrentHashMap而言,其并发的实现就是通过分段锁的形式来实现高效的并发操作。

    我们以ConcurrentHashMap来说一下分段锁的含义以及设计思想,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMapJDK7JDK8HashMap的实现)的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表;同时又是一个ReentrantLockSegment继承了ReentrantLock)

    当需要put元素的时候,并不是对整个hashmap进行加锁,而是先通过hashcode来知道他要放在哪一个分段中,然后对这个分段进行加锁,所以当多线程put的时候,只要不是放在一个分段中,就实现了真正的并行的插入。

    但是,在统计size的时候,可就是获取hashmap全局信息的时候,就需要获取所有的分段锁才能统计。

    分段锁的设计目的是细化锁的粒度,当操作不需要更新整个数组的时候,就仅仅针对数组中的一项进行加锁操作。

     

    偏向锁/轻量级锁/重量级锁

    这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized这三种锁的状态是通过对象监视器在对象头中的字段来表明的锁是在资源中的,是要访问资源(如对象实例,Class类实例,属性变量,代码块等)的一部分,线程是要取得资源中的锁。

    • 偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
    • 轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
    • 重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

     

    自旋锁

    Java中,自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁(即当循环的条件被其他线程改变时 才能进入临界区),这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU

    典型的自旋锁实现的例子:

    public class SpinLock {
        private AtomicReference<Thread> sign =new AtomicReference<>();
    
        public void lock(){
            Thread current = Thread.currentThread();
            while(!sign .compareAndSet(null, current)){
            }
        }
    
        public void unlock (){
            Thread current = Thread.currentThread();
            sign .compareAndSet(current, null);
        }
    }
    

    使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

    当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

    由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。

    注:该例子为非公平锁,获得锁的先后顺序,不会按照进入lock的先后顺序进行。

     

    可中断锁

    synchronized 是不可中断的,Lock 是可中断的,这里的可中断建立在阻塞等待中断,运行中是无法中断的。

    展开全文
  • 前几篇都介绍了Thread中的几个方法,相信大家都发现一个相似点...线程中断会有哪些问题呢?这一篇我们就来介绍一下线程中断的概念: 在java的线程Thread类中有三个方法,比较容易混淆,在这里解释一下 (...

    前几篇都介绍了Thread中的几个方法,相信大家都发现一个相似点,那就是sleep,join,wait这样的阻塞方法都必须捕获一个InterruptedException异常,顾名思义就是一个线程中断的异常,那什么是线程中断呢?线程中断会有哪些问题呢?这一篇我们就来介绍一下线程中断的概念:


    在java的线程Thread类中有三个方法,比较容易混淆,在这里解释一下
    (1)interrupt:置线程的中断状态
    (2)isInterrupt:线程是否中断
    (3)interrupted:返回线程的上次的中断状态,并清除中断状态

    举个例子:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. class MyThread extends Thread {    
    2.     ......    
    3.     ......    
    4.     public void run() {    
    5.         try {    
    6.             while(!Thread.currentThread().isInterrupted()) {  //如果线程没有被中断就继续运行  
    7.                 //阻塞代码:sleep,wait等      
    8.                 //当其他线程,调用此线程的interrupt()方法时,会给此线程设置一个中断标志    
    9.                 //sleep,wait等方法会检测这个标志位,同时会抛出InterruptedException,并清除线程的中断标志    
    10.                 //因此在异常段调用Thread.currentThread().isInterrupted()返回为false     
    11.             }    
    12.         } catch (InterruptedException e) {    
    13.             //由于阻塞库函数,如:Object.wait,Thread.sleep除了抛出异常外,还会清除线程中断状态,因此可能在这里要保留线程的中断状态    
    14.             Thread.currentThread().interrupt();//从新设置线程的中断标志    
    15.         }    
    16.     }    
    17.     public void cancel() {    
    18.         interrupt();  //中断线程  
    19.     }    
    20. }    

    外部调用  

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
    1. MyThread thread = new MyThread();    
    2. thread.start(); //开启线程   
    3. ......    
    4. thread.cancel();  //中断线程  
    5. System.out.println(thread.isInterrupt)//打印线程中断标志  

     

    一般来说,阻塞函数,如:Thread.sleep、Thread.join、Object.wait等在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态

    对于InterruptedException的处理,可以有两种情况:
    (1)外层代码可以处理这个异常,直接抛出这个异常即可
    (2)如果不能抛出这个异常,比如在run()方法内,因为在得到这个异常的同时,线程的中断状态已经被清除了,需要保留线程的中断状态,则需要调用Thread.currentThread().interrupt()

    另外,Thread.interrupted()在jdk库的源代码中比较常用,因为它既可以得到上一次线程的中断标志值,又可以同时清除线程的中断标志,一举两得,但同时也有坏处,就是这个函数有清除中断状态的副作用,不容易理解。


    从上面的分析可以查出,通过中断机制也可以实现线程的终止,但是这种方式也是基于抛出异常的,所以这种方式也是不安全的,同时我们也可以这样理解就是中断机制就是来结束那些阻塞的线程的,因为阻塞线程回去检查中断标志位,又中断就抛异常来结束线程的运行。


    通过这一篇貌似可以理解那些调用阻塞线程的方法为什么要抛出中断异常了,同时也理解了线程的中断机制。


    好吧,又结束了一篇,但是这一篇不是最后一篇奥,千万不要放弃呀,后面还有呢!要继续关注呀!

    版权声明:本文为博主原创文章,未经博主允许不得转载。

    转载于:https://www.cnblogs.com/pjdssswe/p/4696084.html

    展开全文
  • 数据搬迁目标是在最少存储中断服务时间内完成数据在两个存储设备之间快速有序迁移,并保证数据完整性、可用性、一致性。 客户面临挑战 主要使用场景:根据正式系统搭建测试环境、从内网复制到外网、数据库...
  • 首先会对锁的分类有一个整体的概念,了解锁究竟有哪些分类标准。 锁的 7 大分类 需要首先指出的是,这些多种多样的分类,是评价一个事物的多种标准,比如评价一个城市,标准有人口多少、经济发达与否、城市面积大小...
  • Java 中哪几种锁?

    千次阅读 2020-07-13 08:47:21
    本章主要是介绍 Java 并发编程中锁究竟有哪些分类标准,对锁的分类有一个整体的认识。 本篇因为大都是概念,所以内容与原文保持一直,并未做更改。 原文地址:《Java 并发编程 78 讲》- 徐隆曦 第 19 讲:你知道哪...
  • 1.运算器采用单组内总线、分立寄存器结构和单组内总线...17.简述中断的分类。 18.什么是中断向量?中断向量表?向量地址?向量中断? 19.简述中断响应过程。 20.简述中断处理过程。 21.某主机连接两个输入设备,两
  • 操作系统学习总结

    千次阅读 2020-12-18 11:54:55
    操作系统学习总结标题绪论操作系统的定义操作系统的特征操作系统的功能操作系统的分类硬件处理机的状态及分类管态(uperitor Mode )核态( Kemel Mode )管态用户态(User Mode)指令分类特权指令非特权指令中断的定义...
  • 1.2 异常的分类有哪些?Throwable分为:Error:错误,依靠程序自身无法修正的异常。Exception:可以修正。 Exception分为: RuntimeException:运行时异常不要求程序必须做出处理。 Checked异常: 程序必须...
  • 1.2 异常的分类有哪些?Throwable分为:Error:错误,依靠程序自身无法修正的异常。Exception:可以修正。Exception分为:RuntimeException:运行时异常不要求程序必须做出处理。Checked异常: 程序必须处理该...
  • STM32芯片命名规则

    2020-07-07 18:54:45
    在芯片中数量众多引脚,咱们怎么知道哪个引脚可用于USART、哪些引脚可用于ADC、哪些引脚可用于外部中断等等。由于stm32芯片引脚功能都固定了,我们可以查看相应数据手册有关引脚部分内容,即Pino.
  • Github上有许多不错的开源书籍,更多具体的分类与链接可转移如下的知乎问题:GitHub 上有哪些值得推荐的开源电子书?[1] 不过国内访问Github时总会发现访问速度过慢,下载意外中断的情况,这就影响了我们去下(白)载...
  • 2016-11-30 15:20:00
    /* 栈: 1、什么叫栈:栈就是一个存储结构,简单说就是一种存储方式 2、栈是怎么存储:栈是先进后出一种存储结构... 6、栈应用有哪些:迷宫,表达式计算,函数调用,中断等,应用相当广泛 ...
  • 网上搜答案也是五花八门,希望大家能推荐一个内存优化参数文章 另外我上面设置参数有哪些方面可以优化,或者哪些地方写不对。 <br />核心代码我都贴到下面:(我是刚入门菜鸟,大家别笑话我写...
  • 原语是不可中断的过程。 •加锁/开锁(LOCK/UNLOCK)原语 优点是实现互斥简单;缺点是效率很低。 •信号量(Semaphore)及PV操作 PV操作能够实现对临界区的管理要求。它由P操作原语和V操作原语...
  • Visual Studio程序员箴言--详细书签版

    热门讨论 2012-10-16 20:37:39
    CruiseYoung提供详细书签电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 Visual Studio程序员箴言 原书名: Microsoft Visual Studio Tips 原出版社: Microsoft Press 作者: (美)...
  • PHP168建站系统

    2010-06-01 15:51:35
    可以指定每次读取几条数据,数值大,备份快,数值小,有利于一些虚拟机空间备份成功率,不被中断。 4.可以指定备份出来数据为适应mysql高版本或低版本。 5.备份出来数据,可以自适应MYSQL高版本或低版本,...
  • GA 374-2019 电子防盗锁

    2019-10-24 11:49:03
    从分类上来看,GA374-2019《电子防盗锁》增加了连联网型和单机型智能锁的分类。标准要求联网型智能锁能够与远程终端进行开锁信息的在线交互,智能锁应能将本体上产生的错误报警、防拆报警及事件记录等信息上传至远程...
  • 软件工程教程

    热门讨论 2012-07-06 23:10:29
    删除操作一旦执行,立即被监听器捕获到,进而在执行 删除操作前执行自定义函数体,即判断实体有无undeletable标签,中断删除操作,无则正常删除。 用例图 关系 关联关系 ;依赖关系 ;泛化关系;关系...
  • 使用Arouter注意事项有哪些?如何让代码变得更加容易让人维护? 直接看我这篇博客:https://juejin.im/post/5c46e6fb6fb9a049a5713bcc 4.6 存在待解决问题 动态管理组件,所以给每个组件添加几个生命周期...
  • 软件测试规范

    2018-04-23 09:16:12
    软件测试目标 .................................................................................................................................. 2 三.软件测试流程 .......................................
  • 第1章搭建单域环境2小时49分钟29节 1-1IT运维职位需要掌握技能04:40 1-2计算机组织形式-工作组07:38 1-3计算机组织形式--域04:45 1-4域中计算机和域控制器之间信任关系05:...12-2对活动目录有哪些维护工作04:00 1
  • 传统经济学的结构可以用若干不同方式来表示,其中一个最常用的分类如表2.1.1所示。 表2.1.1 传统经济学的学科分类 ─────────────────────────────────────────...
  • iPhone开发秘籍(第2版)--源代码

    热门讨论 2012-12-11 13:51:22
    CruiseYoung提供详细书签电子书籍目录 http://blog.csdn.net/fksec/article/details/7888251 该资料是《iPhone开发秘籍:第2版》源代码 对应书籍资料见: iPhone开发秘籍:第2版(iphone开发必备佳作,在...

空空如也

空空如也

1 2
收藏数 26
精华内容 10
关键字:

中断的分类有哪些