- 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
由于上下文切换也是个相对比较耗时的操作,所以在"java并发编程的艺术"一书中有过一个实验,并发累加未必会比串行累加速度要快。 可以使用Lmbench3测量上下文切换的时长 vmstat测量上下文切换次数
线程安全
多线程编程中最难以把握的就是临界区线程安全问题,稍微不注意就会出现死锁的情况,一旦产生死锁就会造成系统功能不可用。
public class DeadLockDemo {
private static String resource_a = “A”;
private static String resource_b = “B”;
public static void main(String[] args) {
deadLock();
}
public static void deadLock() {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resource_a) {
System.out.println(“get resource a”);
try {
Thread.sleep(3000);
synchronized (resource_b) {
System.out.println(“get resource b”);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread threadB = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resource_b) {
System.out.println(“get resource b”);
synchronized (resource_a) {
System.out.println(“get resource a”);
}
}
}
});
threadA.start();
threadB.start();
}
}
在上面的这个demo中,开启了两个线程threadA, threadB,其中threadA占用了resource_a, 并等待被threadB占用的resource _b。threadB占用了resource _b正在等待被threadA占用的resource _a。因此threadA,threadB出现线程安全的问题,形成死锁。同样可以通过jps,jstack证明这种推论:
“Thread-1”:
waiting to lock monitor 0x000000000b695360 (object 0x00000007d5ff53a8, a java.lang.String),
which is held by “Thread-0”
“Thread-0”:
waiting to lock monitor 0x000000000b697c10 (object 0x00000007d5ff53d8, a java.lang.String),
which is held by “Thread-1”
Java stack information for the threads listed above:
===================================================
“Thread-1”:
at learn.DeadLockDemo$2.run(DeadLockDemo.java:34)
• waiting to lock <0x00000007d5ff53a8(a java.lang.String)
• locked <0x00000007d5ff53d8(a java.lang.String)
at java.lang.Thread.run(Thread.java:722)
“Thread-0”:
at learn.DeadLockDemo$1.run(DeadLockDemo.java:20)
• waiting to lock <0x00000007d5ff53d8(a java.lang.String)
• locked <0x00000007d5ff53a8(a java.lang.String)
at java.lang.Thread.run(Thread.java:722)
Found 1 deadlock.
如上所述,完全可以看出当前死锁的情况。
那么,通常可以用如下方式避免死锁的情况:
- 避免一个线程同时获得多个锁;
- 避免一个线程在锁内部占有多个资源,尽量保证每个锁只占用一个资源;
- 尝试使用定时锁,使用lock.tryLock(timeOut),当超时等待时当前线程不会阻塞;
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
所以,如何正确的使用多线程编程技术有很大的学问,比如如何保证线程安全,如何正确理解由于JVM内存模型在原子性,有序性,可见性带来的问题,比如数据脏读,DCL等问题。而在学习多线程编程技术的过程中也会让你收获颇丰。
易混淆的概念
阻塞与非阻塞
阻塞与非阻塞的重点在于进/线程等待消息时候的行为,也就是在等待消息的时候,当前进/线程是挂起状态,还是非挂起状态。
阻塞
:调用在发出去后,在消息返回之前,当前进/线程会被挂起,直到有消息返回,当前进/线程才会被激活;
非阻塞
:调用在发出去后,不会阻塞当前进/线程,而会立即返回。
同步与异步
同步
:当一个同步调用发出去后,调用者要一直等待调用结果的返回后,才能进行后续的操作。
异步
:当一个异步调用发出去后,调用者不用管被调用方法是否完成,都会继续执行后面的代码。 异步调用,要想获得结果,一般有两种方式: