Thread的五种状态:
1, new<---------new Thread()
2, runnable<----thread.start()
3, running<-----cpu轮流调度
4, blocked<-----(3种: wait pool,sleep,lock pool(等待syn-lock,等待I/O event))
5, dead<--------run方法执行完毕,或异常
Thread相关的方法
1.sleep(), Thread类的方法,不会释放“锁标志”。剩下的都是Object类的
2.wait()方法
释放锁,由notifyAll()唤醒,wait,notifyAll是Object的方法,必须在synchronized语句块内,否则IllegalMonitorStateException
3.yield方法
暂停当前正在执行的线程对象。当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行。
yield()只能使同优先级或更高优先级的线程有执行的机会。
4.join方法
等待调用join方法的线程结束,再继续执行。如:t.join();//主要用于等待t线程运行结束,若无此句,main则会执行完毕,导致结果不可预测。
5.notifyAll
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前的线程放弃此对象上的锁定,才能继续执行被唤醒的线程。
线程通信,
进程通信机制:socket, 文件, shared memory(如何,只有线程才行吧?)信号处理程序(signal handlers)、信号量(semaphores)
顺序编程:每一条指令的执行,都有对下一条指令的明确定义。进程有序。
线程通信机制:共享内存空间地址(共享对象,变量,通信最容易) + 进程通信机制。
线程两大问题:1线程之间不可见、2线程之间无序。要使得可见+有序(原子),volatile使得可见,单不是原子,可以作为状态控制使用。
线程的优点:并发执行(不是顺序执行)
1、使用多CPU
2、模型的简化
3、对异步事件的简单处理
4、用户界面的更加响应性
线程的风险:
1、安全风险:结果不正常,安全意味着:“什么坏事都没有发生”
2、 liveness(活性,活跃度)风险:liveness意味着:“好事(期望的事)最终肯定会被执行”,
liveness失败,进程中也有如:if(true){}else{liveness failure}
线程增加了发生的几率如:死锁,饥饿,活锁。
3、性能风险:期望的事情尽快的被执行。如:响应速度、吞吐量、可伸缩性。线程会带来context switch的开销。
考虑分离锁(把一个锁拆分成多个)
2.1线程安全的三种途径:
1、不跨线程使用共享状态变量:独享地操作本地(基于栈的)变量。
2、使状态变量不可变
3、使用同步。
设计线程安全的类:封装、不可变行、不可变约束。
3.1原子性:(相对于复合操作)
race condition:竞争条件(竞争,争夺共享资源的情况,has several race conditions )
使用潜在的过期值,决定下一步的操作,,称为check-then-act,(read-wirte)这不是原子操作。不可靠。
锁:synchronized提供“独占锁”,保证原子性
,volatile,threadLoacl呢?什么锁?
重进入:线程试图获得它自己占有的锁时,请求会成功,请求基于per-thread,而不是per-invocation
public calss A{
public synchronized void doSomething(){...}
}
public calss B extends A{
public synchronized void doSomething(){super.doSomethiong();...}
}
如果不可以重进入,B对象调用doSomething时将会死锁
chapter3 共享对象
3.1Visibility(可见性,线程A可以始终看见其他线程的对数据执行的结果。)
一个线程,和一个主线程即可。
线程不是顺序执行(NoVisibility),会重排序recordering,可以通过synchronized 保证顺序。
synchronized 可以保证可预见性和原子性。
volatile变量
1、当多个线程使用同一个变量时,每个线程都在其java stack缓冲中有一个这个变量的拷贝,对这个变量的改变实际上是对这个复制品进行改变。而另一个线程在使用这个变量时还可能一无所知。
2、volatile变量,不会去假设这个变量的值,每次都重新读取这个变量的值,对变量进行改变时直接作用于主内存。
volatile只能保证可见性,不能保证“读-改-写”的原子性,不能使i++原子化。
volatile变量不会被jvm缓存。一般用来做完成、中断的标记。需满足以下条件。线程A的i++,可能被线程B的i++干扰。
1、写入变量时不依赖变量的当前值,或者确保只有一个线程操作,i++就依赖当前值,它不需要操作堆内存。
2、The variable does not participate in invariants with other state variables; ????????
3、Locking is not required for any other reason while the variable is being accessed.??????????
volatile变量的几个例子(C语言上的):
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用不懂得volatile内容将会带来灾难。
3.2. Publication and Escape(发布和逸出)
Publishing an object means making it available to code outside of its current scope, such as :
1、by storing a reference to it where other code can find it,
2、returning it from a nonprivate method, or passing it to a method in another class.
escaped:
publishing objects before they are fully constructed can compromise thread safety
线程组是一个可以统一管理的线程集合,默认创建的线程,都属于相同的线程组。
竞争条件:
accounts[0] += amount;//不是原子操作
1、将accounts[0]加载到寄存器(java没有寄存器吧?叫pc,只是个名字不同而已)
2、增加amount
3、将结果写回accounts[0]
//aload_0,getfield,iload_2,dup2,执行它们的线程可以在任何一个指令点上被中断。
C语言-计算机原理
装载-写入内存--?