并发编程笔记
1.上下文切换
CPU通过给每个线程分配CPU时间片来实现多线程的机制。由于时间片非常短,所以cpu通过不断的切换线程来让我们感觉是多个线程正在同时执行。当切换线程的时候cpu会保存上一个任务的状态,以便切换回这个任务的时候,可以加载这个任务的状态。因此任务从保存到再加载的过程就是一次上下文切换。
减少上下文切换的方法:
无锁并发编程、CAS算法、使用最少线程、使用协程
Volatile 的实现原则
1)Lock前缀指令引起处理器缓存写回到内存 该过程处理器可以独占任何共享内存,原因是会锁住总线或者缓存锁
2)一个处理器的缓存回写到内存会导致其他处理器的缓存无效。 其它处理器通过嗅探在总线上传播的数据检查是不是过期了。过期则设置为无效状态,当处理器对这个数据进行修改操作的时候会重新从系统内存中读到处理器缓存中。
大概就是耍流氓,自己把数据锁了,还要求别人使用的时候都得跟自己一样。但是,对于i++这种非原子性的操作,valatile就无效了,不能保证线程安全。由于i++分为三步操作,第一,读取旧值,第二,i+1,第三把新值写到回主内存。但是在第一步和第二部之间,如果有第二个线程进来,由于内存可见性,也读取了旧值。两个线程都对同一变量进行+1,而原本应该合起来是+2。因此线程不安全。
Synchronized
普通同步方法 锁是当前实例对象。
静态同步方法 锁是当前类的Class对象
同步方法块 锁是Synchonized括号里配置的对象
在JVM中,代码块同步是基于进入和退出Monitor对象来实现。 通过monitorenter和monitorexit指令实现的。任何对象都有monitor与之关联,当被持有后就获得对应的monitor所有权。
线程间的通信
1.使用volatile和synchronized关键字。保证线程对变量访问的可见性,syn还保证了排他性。
2.等待/通知机制
while(value != desire){
Thread.sleep(1000);
}
doSomething();
直到别的线程改变了共享变量 上面线程跳出循坏执行。
3.等待唤醒机制
通过wait()方法和notify()和notifyAll()方法 wait()方法会释放锁
4.管道输入/输出流
package thread; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; public class Piped { public static void main(String[] args) throws Exception { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); out.connect(in); Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); int receive = 0; try { while((receive = System.in.read()) != -1) { out.write(receive); } }finally { out.close(); } } static class Print implements Runnable{ private PipedReader in; public Print(PipedReader in) { this.in = in; } public void run() { int receive = 0; try { while((receive = in.read()) != -1) { System.out.print((char) receive); } } catch (Exception e) { // TODO: handle exception } } } }
5.Thread.join()方法
仅当调用join的线程执行完后,调用处的线程才能执行
6.ThreadLocal
线程的本地变量,以ThreadLocal对象为键,任意对象为值的存储结构
待续...