学习Java的同学注意了!!!
学习进程中遇到甚么问题或想获得学习资源的话,欢迎加入Java学习交换群,群号码:183993990 我们1起学Java!
使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行;当运算结束后再从缓存同步回内存当中,这样处理器就无需等待缓慢的内存读写了。
缓存1致性:多处理器系统中,由于同享同1主内存,当多个处理器的运算任务都设计到同1块内存区域时,将可能致使各自的缓存数据不1致的情况,则同步回主内存时需要遵守1些协议。
乱序履行优化:为了使得处理器内部的运算单位能尽可能被充分利用。
目标是定义程序中各个变量的访问规则。(包括实例字段、静态字段和构成数组的元素,不包括局部变量和方法参数)
内存间交互操作:
Lock(锁定):作用于主内存中的变量,把1个变量标识为1条线程独占的状态。
Read(读取):作用于主内存中的变量,把1个变量的值从主内存传输到线程的工作内存中。
Load(加载):作用于工作内存中的变量,把read操作从主内存中得到的变量的值放入工作内存的变量副本中。
Use(使用):作用于工作内存中的变量,把工作内存中1个变量的值传递给履行引擎。
Assign(赋值):作用于工作内存中的变量,把1个从履行引擎接收到的值赋值给工作内存中的变量。
Store(存储):作用于工作内存中的变量,把工作内存中的1个变量的值传送到主内存中。
Write(写入):作用于主内存中的变量,把store操作从工作内存中得到的变量的值放入主内存的变量中。
Unlock(解锁):作用于主内存中的变量,把1个处于锁定状态的变量释放出来,以后可被其它线程锁定。
规则:
运算结果其实不依赖变量确当前值、或确保只有单1的线程修改变量的值。
变量不需要与其他的状态变量共同参与不变束缚。
原子性:基本数据类型的访问读写是具有原子性的,synchronized块之间的操作也具有原子性。
可见性:指当1个线程修改了同享变量的值,其他线程能够立即得知这个修改。synchronized(规则8)和final可以保证可见性。Final修饰的字段在构造器中1旦被初始化完成,并且构造器没有把this的援用传递出去,那末在其他线程中就可以看见final字段的值。
有序性:volatile本身包括了制止指令重排序的语义,而synchronized则是由规则5取得的,这个规则决定了持有同1个所的两个同步块只能串行地进入。
Java内存模型中定义的两项操作之间的偏序关系,如果操作A先行产生于操作B,其实就是说在产生操作B之前,操作A产生的影响能被操作B视察到。
程序次序规则:在1个线程内,依照代码控制流顺序,在前面的操作先行产生于后面的操作。
管程锁定规则:1个unlock操作先行产生于后面对同1个锁的lock操作。
Volatile变量规则:对1个volatile变量的写操作先行产生于后面对这个变量的读操作。
线程启动规则:Thread对象的start()方法先行产生于此线程的每一个操作。
线程终止规则:线程中的所有操作都先行产生于对此线程的终止检测。
线程中断规则:对线程的interrupt()方法的调用先行产生于被中断线程的代码检测中断事件的产生。
对象终结过则:1个对象的初始化完成先行产生于它的finalize()方法的开始。
传递性:如果操作A先行产生于操作B,操作B现象产生于操作C,那末就能够得出操作A先行产生于操作C的结论。
时间上的前后顺序与先行产生原则之间基本上没有太大的关系。
使用内核线程实现:
内核线程Kernel Thread:直接由操作系统内核支持的线程,这类线程由内核类完成线程切换,内核通过操纵调度器对线程进行调度,并负责将线程的任务映照到各个处理器上。
轻量级进程Light Weight Process:每一个轻量级进程都由1个内核线程支持。
局限性:各种进程操作都需要进行系统调用(系统调用代价相对较高,需要在用户态和内核态中来回切换);轻量级进程要消耗1定的内核资源,1次1个系统支持轻量级进程的数量是有限的。
使用用户线程实现:
用户线程:完全建立在用户空间的线程库上,系统内核不能直接感知到线程存在的实现。用户线程的建立、同步、烧毁和调度完全在用户态中完成,不需要内核的帮助。所有的线程操作都需要用户程序自己处理。
混合实现:
将内核线程和用户线程1起使用的方式。操作系统提供支持的轻量级进程则作为用户线程和内核线程之间的桥梁。
Sun JDK,它的Windows版和Linux版都是使用1对1的线程模型来实现的,1条Java线程映照到1条轻量级进程当中。
线程调度是指系统为线程分配处理器使用权的进程:协同式、抢占式。
协同式:线程的履行时间由线程本身控制,线程把自己的工作履行完了以后,要主动通知系统切换到另外一个线程上。坏处:线程履行时间不可控制。
抢占式:每一个线程将由系统来分配履行时间,线程的切换不由线程本身来决定。Java使用该种调用方式。
线程优先级:在1些平台上(操作系统线程优先级比Java线程优先级少)不同的优先级实际会变得相同;优先级可能会被系统自行改变。
线程状态:
新建NEW:
运行RUNNABLE:
无穷期等待WAITING:等得其他线程显式地唤醒。
没有设置Timeout参数的Object.wait();没有设置Timeout参数的Thread.wait()。
限期等待TIMED_WAITING:在1定时间以后会由系统自动唤醒。
设置Timeout参数的Object.wait();设置Timeout参数的Thread.wait();Thread.sleep()方法。
阻塞BLOCKED:等待获得1个排它锁,等待进入1个同步区域。
结束TERMINATED:
线程安全:当多个线程访问1个对象时,如果不用斟酌这些线程在运行时环境下的调度和交换履行,也不需要进行额外的同步,或调用方进行任何其他的调和操作,调用这个对象的行动都可以取得正确的结果,那这个对象就是线程安全的。
不可变:只要1个不可变的对象被正确地构建出来。使用final关键字修饰的基本数据类型;如果同享数据是1个对象,那就需要保证对象的行动不会对其状态产生任何影响(String类的对象)。方法:把对象中带有状态的变量都申明为final,如Integer类。有:枚举类型、Number的部份子类(AtomicInteger和AtomicLong除外)。
绝对线程安全:
相对线程安全:对这个对象单独的操作是线程安全的。1般意义上的线程安全。
线程兼容:需要通过调用端正确地使用同步手段来保证对象在并发环境中安全地使用。
线程对峙:不管调用端是不是采取了同步措施,都没法在多线程环境中并发使用的代码。有:System.setIn()、System.setOut()、System.runFinalizersOnExit()
Synchronized关键字:编译后会在同步块前后分别构成monitorenter和monitorexit这两个字节码指令。这两个指令都需要1个援用类型的参数来指明要锁定和解锁的对象。如果没有明确指定对象参数,那就根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。
在履行monitorenter指令时,首先尝试获得对象的锁,如果没有被锁定或当前线程已具有了该对象的锁,则将锁计数器加1,相应的履行moniterexit时,将锁计数器减1,当计数器为0时,锁就被释放了。如果获得对象锁失败,则当前线程就要阻塞等待。
ReentrantLock相对synchronized的高级功能:
等待可中断:当持有锁的线程长时间不释放锁时,正在等待的线程可以选择放弃等待,改成处理其他事情。
公平锁:多个线程在等待同1个锁时,必须依照申请锁的事件顺序来1次获得锁;而非公平锁在被释放时,任何1个等待锁的线程都有机会取得锁。Synchronized中的锁是非公平锁,ReentrantLock默许也是非公平锁。
锁绑定多个条件:1个ReentrantLock对象可以同时绑定多个Condition对象。
基于冲突检测的乐观并发策略:先进行操作,如果没有其他线程争用同享数据,那操作就成功了;如果同享数据有争用,产生了冲突,那就再进行其他的补偿措施(1般是不断的尝试,直到成功为止)。
AtomicInteger等原子类中提供了方法实现了CAS指令。
可重入代码:可以在代码履行的任什么时候刻中断它,转而去履行另外一段代码,而在控制权返回后,原来的程序不会出现任何毛病。特点:不依赖存储在堆上的数据和公用的系统资源、用到的状态量都由参数传入,不调用非可重入的方法等。如果1个方法,它的返回结果是可以预测的,只要出入了相同的数据,就可以返回相同的结果,那它就满足可重入性的要求。
线程本地存储:如果1段代码中所需要的数据必须与其它代码同享,那就看看这些同享数据的代码是不是能保证在同1个线程中履行。
ThreadLocal:线程级别的局部变量,为每一个使用该变量的线程提供1个独立的变量副本,每一个线程修改副本时不影响其他线程对象的副本。ThreadLocal实例通常作为静态私有字段出现在1个类中。
为了让线程等待,让线程履行1个忙循环(自旋)。需要物理机器有1个以上的处理器。自旋等待虽然避免了线程切换的开消,带它是要占用途理器时间的,所以如果锁被占用的时间很短,自旋等待的效果就会非常好,反之自旋的线程只会白白消耗处理器资源。自旋次数的默许值是10次,可使用参数-XX:PreBlockSpin来更改。
自适应自旋锁:自旋的时间不再固定,而是由前1次在同1个锁上的自旋时间及锁的具有者的状态来决定。
指虚拟机即时编译器在运行时,对1些代码上要求同步,但是被检测到不可能存在同享数据竞争的锁进行清除(逃逸分析技术:在堆上的所有数据都不会逃逸出去被其它线程访问到,可以把它们当做栈上数据对待)。
如果虚拟机探测到有1串零碎的操作都对同1个对象加锁,将会把加锁同步的范围扩大到全部操作序列的外部。
HotSpot虚拟机的对象的内存布局:对象头(Object Header)分为两部份信息吗,第1部份(Mark Word)用于存储对象本身的运行时数据,另外一个部份用于存储指向方法区对象数据类型的指针,如果是数组的话,还会由1个额外的部份用于存储数组的长度。
32位HotSpot虚拟机中对象未被锁定的状态下,Mark Word的32个Bits空间中25位用于存储对象哈希码,4位存储对象分代年龄,2位存储锁标志位,1位固定为0。
HotSpot虚拟机对象头Mark Word
存储内容 |
标志位 |
状态 |
对象哈希码、对象分代年龄 |
01 |
未锁定 |
指向锁记录的指针 |
00 |
轻量级锁定 |
指向重量级锁的指针 |
10 |
膨胀(重量级锁) |
空,不记录信息 |
11 |
GC标记 |
偏向线程ID,偏向时间戳、对象分代年龄 |
01 |
可偏向 |
在代码进入同步块时,如果此同步对象没有被锁定,虚拟机首先将在当前线程的栈帧中建立1个名为锁记录(Lock Record)的空间,用于存储所对象目前的Mark Word的拷贝。然后虚拟机将使用CAS操作尝试将对象的Mark Word更新为履行Lock Record的指针。如果成功,那末这个线程就具有了该对象的锁。如果更新操作失败,虚拟机首先会检查对象的Mark Word是不是指向当前线程的栈帧,如果是就说明当前线程已具有了这个对象的锁,否则说明这个对象已被其它线程抢占。如果有两条以上的线程争用同1个锁,那轻量级锁就不再有效,要膨胀为重量级锁。
解锁进程:如果对象的Mark Word依然指向着线程的锁记录,那就用CAS操作把对象当前的Mark Word和和线程中复制的Displaced Mark Word替换回来,如果替换成功,全部进程就完成。如果失败,说明有其他线程尝试过获得该锁,那就要在释放锁的同时,唤醒被挂起的线程。
轻量级锁的根据:对绝大部份的锁,在全部同步周期内都是不存在竞争的。
传统锁(重量级锁)使用操作系统互斥量来实现的。
目的是消除在无竞争情况下的同步原语,进1步提高程序的运行性能。锁会偏向第1个取得它的线程,如果在接下来的履行进程中,该锁没有被其它线程获得,则持有锁的线程将永久不需要再进行同步。
当锁第1次被线程获得的时候,虚拟机将会把对象头中的标志位设为01,同时使用CAS操作把获得到这个锁的线程的ID记录在对象的Mark Word当中,如果成功,持有偏向锁的线程以后每次进入这个锁相干的同步块时,都可以不进行任何同步操作。
当有另外一个线程去尝试获得这个锁时,偏向模式就宣布结束。根据所对象目前是不是处于被锁定的状态,撤消偏向后恢复到未锁定或轻量级锁定状态。
操作系统的两种运行级别,intel cpu提供-Ring33种运行模式。
Ring0是留给操作系统代码,装备驱动程序代码使用的,它们工作于系统核心态;而Ring3则给普通的用户程序使用,它们工作在用户态。运行于处理器核心态的代码不受任何的限制,可以自由地访问任何有效地址,进行直接端口访问。而运行于用户态的代码则要遭到处理器的诸多检查,它们只能访问映照其地址空间的页表项中规定的在用户态下可访问页面的虚拟地址,且只能对任务状态段(TSS)中I/O许可位图(I/O Permission Bitmap)中规定的可访问端口进行直接访问。
在其他线程调用此对象的notify()或notifyAll()方法,或超过指定时间量前,当前线程T等待(线程T必须具有该对象的锁)。线程T被放置在该对象的休息区中,并释放锁。在被唤醒、中断、超时的情况下,从对象的休息区中删除线程T,并重新进行线程调度。1旦线程T取得该对象的锁,该对象上的所有同步申明都被恢复到调用wait()方法时的状态,然后线程T从wait()方法返回。如果当前线程在等待之前或在等待时被任何线程中断,则会抛出 InterruptedException。在按上述情势恢复此对象的锁定状态时才会抛出此异常。在抛出此异常时,当前线程的中断状态被清除。
只有该对象的锁被释放,其实不会释放当前线程持有的其他同步资源。
唤醒在此对象锁上等待的单个线程。此方法只能由具有该对象锁的线程来调用。
在指定的毫秒数内让当前正在履行的线程休眠(暂停履行),此操作遭到系统计时器和调度程序精度和准确性的影响。监控状态仍然保持、会自动恢复到可运行状态,不会释放对象锁。如果任何线程中断了当前线程。当抛出InterruptedException异常时,当前线程的中断状态被清除。让出CPU分配的履行时间。
thread.join():在1个线程对象上调用,使当前线程等待这个线程对象对应的线程结束。
Thread.yield():暂停当前正在履行的线程对象,并履行其他线程。
thread.interrupt()
中断线程,停止其正在进行的1切。中断1个不处于活动状态的线程不会有任何作用。
如果线程在调用Object类的wait()方法、或join()、sleep()方法进程中受阻,则其中断状态将被清除,并收到1个InterruptedException。
Thread.interrupted():检测当前线程是不是已中断,并且清除线程的中断状态(回到非中断状态)。
thread.isAlive():如果线程已启动且还没有终止,则为活动状态。
thread.setDaemon():需要在start()方法调用之前调用。当正在运行的线程都是后台线程时,Java虚拟机将退出。否则当主线程退出时,其他线程依然会继续履行。
如果1个静态方法被申明为synchronized,则同等于在这个方法上调用synchronized(类.class)。当1个线程进入同步静态方法中时,其他线程不能进入这个类的任何静态同步方法。
下一篇 颜色选择器