-
线程之间通过共享内存和消息队列来进行消息传递;进程之间通过消息传递进行协作。
-
进程创建线程,多个线程共享创建它们的进程所分配的内存,同时,每个线程又独立地拥有自己的方法栈和寄存器。
-
创建线程:继承Thread类或实现Runable接口(推荐使用),然后通过start()方法来创建线程。
-
线程个数例题:
public class Moirai {
public static void main(String[] args) {
Thread clotho = new Thread(new Runnable() {
public void run() { System.out.println("spinning"); };
});
clotho.start();
new Thread(new Runnable() {
public void run() { System.out.println("measuring"); };
}).start();
new Thread(new Runnable() {
public void run() { System.out.println("cutting"); };
});
}
}
该例中最大线程数为3(其中一个是主线程)。
-
Race of interleaving:在多线程中,操作被切片,导致多个操作交错进行时产生错误的结果。例如,多线程下对同一个账户进行存钱操作,存钱被切成3个原子操作:获取账户(balance,初始为0),账户加1(1代表金额),写回账户。则下面的情况就会产生错误的结果:
-
解决交错时竞争的三个方法:
1) 不共享任何变量。
2) 只共享Immutable的变量,即只能查看变量,不能修改变量。
3) 共享线程安全的变量,针对这些变量的所有操作都是原子操作。(装饰模式)如:
private static Map<Integer,Boolean> cache = Collections.synchronizedMap(new HashMap<>());
注意:a.使用Collections.synchronizedMap(HashMap)后需要将指向HashMap的引用彻底删除。
b.即使在线程安全的集合类上,使用iterator也是不安全的。
c.即使是线程安全的collection类,仍然可能产生竞争。在其上的某个操作是线程安全的,但都多个操作放在一起,仍旧不安全。
-
真正线程安全的变量:只能读不能写的变量。
-
锁:可以锁住一条或多条语句,锁住的部分视为原子操作,同一时间只能被一个线程执行,如:
synchronized(lock){
balance = balance+1;
}
- Monitor pattern:所有对ADT的rep的访问都加锁,锁住的是整个对象。
public class SimpleBuffer implements EditBuffer{
private String text;
...
public SimpleBuffer(){
synchronized(this){
text = "";
checkRep();
}
}
public insert(int pos,String ins){
synchronized(this){
text = text.substring(0,pos)+ins+text.substring(pos);
checkRep();
}
}
...
}
或:
public class SimpleBuffer implements EditBuffer{
private String text;
...
public SimpleBuffer(){
text = "";
checkRep();
}//默认只能被一个线程使用
public synchronized insert(int pos,String ins){
text = text.substring(0,pos)+ins+text.substring(pos);
checkRep();
}
...
}
此模式存在严重的效率问题,解决方法是使用细力度的锁,使用多个锁,这样允许多个线程访问不会出现interleaving的操作。
-
Java中每一个对象都是锁。当一某一对象作为锁时,不能被多个线程访问的是锁锁住的那一部分,锁本身是可以被多个线程访问的,例如:
-
任何共享的mutable变量/对象必须被lock所保护;涉及到多个mutable变量的时候,它们必须被同一个lock所保护。
-
happens-before机制:在寄存器中对值进行改变时,强制使用最新的值来进行改变操作,该机制并不一定安全,例如一个线程在寄存器中改变了某个变量的值但是还没有写到内存中是,另一个线程也改变该变量的值,此时就会出现interleaving。
-
死锁:多个线程竞争lock,相互等待对方释放lock。例如:
T1: synchronized(a){ synchronized(b){ … } }
T2: synchronized(b){ synchronized(a){ … } } -
使用锁的顺序一致(a.存在效率问题,额外操作:排序,b. 得事先知道要使用哪些锁);使用粗力度的锁(将多个锁变成一个锁)。