优化思路:避免死锁、减少锁粒度、锁分离等
一、线程开销
1.处理线程任务外,还要维护多线程环境的特有信息,如:线程本身的元数据,线程的调度,线程上下文的切换等,更多的在于多线程的调度。
二、避免死锁
死锁是多线程特有的问题,在死锁时,线程间互相等待资源,而又不释放锁定的资源,导致一直等待。
一般需要满足以上条件:
1.互斥条件:一个资源只能有一个进程使用
2.请求与保持条件:对已获得的资源不释放
3.不剥夺条件:进程已获得资源,在进程执行完之前,不能强行剥夺
4.循环等待条件:若干线程形成一种头尾相接的循环等待资源
就像形成头尾相连的四个小车一样
package mythreadpool;
public class DeadThread implements Runnable {
public String username;
public Object lock1 = new Object();
public Object lock2 = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
if (username.equals("a")) {
synchronized (lock1) {
try {
System.out.println("username = " + username);
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
synchronized (lock2) {
System.out.println("按lock1->lock2的顺序执行代码");
}
}
}
if (username.equals("b")) {
synchronized (lock2) {
try {
System.out.println("username = " + username);
System.out.println(Thread.currentThread().getName());
Thread.sleep(3000);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
synchronized (lock1) {
System.out.println("按lock2->lock1顺序执行代码");
}
}
}
}
public void setFlag(String username) {
this.username = username;
}
public static void main(String[] args) {
DeadThread dt1 = new DeadThread();
dt1.setFlag("a");
Thread t1 = new Thread(dt1);
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
dt1.setFlag("b");
Thread t2 = new Thread(dt1);
t2.start();
}
}
以上线程1获得了lock1,接着去请求lock2;线程2获得lock2,接着请求lock1。由于线程2持有lock2不能释放,而线程1去请求lock2无法获得则一直等待;而线程1持有lock1,线程2去请求lock1无法获得则一直等待,这样导致两个线程一直等待争抢资源,循环等待
死锁检测:
1.使用Jconsole,检测线程死锁,可以看到死锁代码行
2.使用线程dump,可以打印输出locked,查找到死锁情况
破死锁,可以破坏导致死锁的四个条件的任何一个。
三、减少锁的持有时间
锁的作用范围越小越精确,比如:
synchronized (lock1) {
try {
othercode1();
synchronized method();
othercode2();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
假设othercode1和othercode2 耗时较长 ,且无需同步,则需要将synchronized加在 需要同步的代码块上。
四、减少锁的粒度
在ConcurrentMap类使用分段锁,将整个大对象拆分成多个segments,然后对每个segment加锁,从而实现高并发,减少整体锁的时间。
五、使用读写锁代替独占锁
读写锁利用了读写锁分类的思想,从而针对读多写少的情况进行性能的优化
六、原子操作
原子操作如 AtomicInteger等采用CAS思想设计,CompareAndSet,是一种无锁的实现