http://tutorials.jenkov.com/java-concurrency/concurrency-models.html
单核-单任务-多任务
“并发”并不仅仅指多线程,还可以是多任务,分布式系统。
多线程的好处:
更好的系统资源利用;相近的设计;更好地响应速度。
多线程的代价:
复杂的设计;上下文切换开销;线程自身的资源消费。
并发模型
并发模型指定系统中的线程怎么合作完成给定的任务,不同的模型有不同的划分任务,线程间通信和合作的方式。
Parallel Workers
Assembly Line
Functional Parallelism
Concurrency vs. Parallelism
并发(concurrency):同时处理多个(不相关的)task,比如在单核cpu上,做不到同时“执行”多个task,但可以同时“处理”多个task。并行(parallelism):将一个task分成多个subtask同时执行,在单核cpu上是无法并行的。
一个应用可以只并发不并行,只并行不并发,既不并行也不并发,既并发也并行。
并发是逻辑上的同时进行,并行侧重物理上的同时进行。
(distributed)分布式在并行处理的基础上,强调正在执行任务的物理设备,如处理器、内存等等硬件,在物理上是分开的。
Creating and Starting Java Threads
调用Thread的start()方法会最终调用run()方法。
public void run() {
if (target != null) {
target.run();
}
}
所以,重写Thread的run方法或者在Thread的构造方法中传入Runnable实例都可以。
注意,执行调用Thread的start方法才会开启新的线程。如果直接调用run()方法,run中的代码也会被执行,但就不在新的线程中执行了,而是在创建该Thread实例的线程中执行。
public class ThreadExample { public static void main(String[] args){ System.out.println(Thread.currentThread().getName()); for(int i=0; i<10; i++){ new Thread("" + i){ public void run(){ System.out.println("Thread: " + getName() + " running"); } }.start(); } } }注意,上面例子中,尽管是按1,2,3等的顺序start的Thread的,但被执行的顺序不一定是这个顺序,jvm或者操作系统决定到底哪个线程被执行,与它们start的顺序无关。
Race Conditions and Critical Sections
一个程序内,多个线程各跑各的并不会产生问题,只有访问共享资源(内存,文件,数据库,webservice)时,严格说是同时“读”时,才会导致问题。race condition:当2个线程访问同样的资源,对资源的访问顺序能导致不同的结果,这种情形被称为“竞争条件”。
critical section:导致race condition的代码,称为critical section。
在critical section上使用恰当的线程同步机制可以避免race condition。
Thread Safety and Shared Resources
被多线程访问的代码是安全的代码称为线程安全的。线程安全的代码不会发生race condition。local variable ,在当前线程栈中,是线程安全的。
local object reference , 只在创建它的线程中使用,传递是线程安全的,如果传递给了别的线程,就不安全了。
If a resource is created, used and disposed within the control of the same thread, and never escapes the control of this thread, the use of that resource is thread safe.注意,要区分一个对象是资源本身还是只是对资源的引用,如果只是对资源的引用,那即使对象是安全的,实际上还是不安全的。
Thread Safety and Immutability
race condition发生在当多个线程访问共享资源,且更新资源的情况下,那如果资源是不可更改的,即 immutability的,那就自然而然线程安全了。
public class ImmutableValue{ private int value = 0; public ImmutableValue(int value){ this.value = value; } public int getValue(){ return this.value; } }因为value的值是通过构造函数传递进去的,没有set方法,其值是不可被改变的。
public class ImmutableValue{ private int value = 0; public ImmutableValue(int value){ this.value = value; } public int getValue(){ return this.value; } public ImmutableValue add(int valueToAdd){ return new ImmutableValue(this.value + valueToAdd); } }add方法是通过创建并返回了一个新的对象,并不是在原来对象基础上add的。
public class Calculator{ private ImmutableValue currentValue = null; public ImmutableValue getValue(){ return currentValue; } public void setValue(ImmutableValue newValue){ this.currentValue = newValue; } public void add(int newValue){ this.currentValue = this.currentValue.add(newValue); } }注意,通过immutable达到线程安全时,如果类是immutable,但对类的使用不一定是线程安全的,如上例,可以通过关键字synchronized来搞定这种情况。
Java Memory Model
基础类型的local variable存放在thread stack中
对象存放在共享的heap中,对象的引用存放在thread stack中
调用对象的方法,方法中的local variable存放在thread stack中
对象的member variable,不管是primary type 还是object,都随对象存放在heap中
只有local variable存放在thread stack中
能产生上图的代码:
public class MyRunnable implements Runnable() { public void run() { methodOne(); } public void methodOne() { int localVariable1 = 45; MySharedObject localVariable2 = MySharedObject.sharedInstance; //... do more with local variables. methodTwo(); } public void methodTwo() { Integer localVariable1 = new Integer(99); //... do more with local variable. } }
public class MySharedObject { //static variable pointing to instance of MySharedObject public static final MySharedObject sharedInstance = new MySharedObject(); //member variables pointing to two objects on the heap public Integer object2 = new Integer(22); public Integer object4 = new Integer(44); public long member1 = 12345; public long member1 = 67890; }
(以后写代码看代码,要想象下它们在内存中的位置)
Hardware Memory Architecture
当左边的cpu改变了obj.count的值后,没有flush back到 main memory,那右边的cpu就不知道这个值改变了,也就是对其是不可见的。可以使用volatile关键字,使直接从main memory读和直接写。
上图发生了race condition,可以使用synchronized代码块避免,synchronized代码块可以保证同一时刻只有一个线程能访问critical section,而且是直接从main memory读取,当thread退出critical section时,将所有更新的variable flush back to main memory,不管变量所否是volatile的。