并发编程带来的问题
1.上下文切换问题
2.死锁问题
上下文切换
多线程不一定快
1.线程有创建和上下文切换的开销
如何减少上下文切换
减少上下文切换的方法有
无锁编程
、
CAS算法
使用最小线程
使用协程
1.无锁并发编程,多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照hash算法取模分段,不同的线程处理不同段的数据
2.CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁,其实也加了锁,只不过加锁于cpu上,系统开销可忽略不计
3.使用最小线程。避免创建不需要的线程,比如任务很少,但是创建很多线程来处理,这样会造成大量线程处于等待状态
4.协程,在单线程里实现多任务调度,并在单线程里维持多个任务间的切换
死锁
有如下代码:
package com.fx.pattern.cor.handler;
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
private void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println('1');
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println('2');
}
}
}
});
t1.start();
t2.start();
}
这段代码运行之后会引起死锁,t1线程与t2线程互相等待对方释放锁。
待程序运行之后,我们采用如下方式来查看jvm对程序的跟踪栈信息:
获取当前程序进程id
1.ps aux | grep DeadLockDemo
进程id如下图所示:
DeadLockTest PID.png
获取当前程序产生的堆栈信息
2.sudo -u root jstack -F 32125 > /Users/mark/Desktop/JAVA/dump
获取到的堆栈信息如下图所示:
死锁信息.png
idea中堆栈信息更加明确程序出现死锁的代码行数:
死锁分析.png
避免死锁的方法
1.避免一个线程同时获取多个锁
2.避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源
3.尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部锁机制。
4.对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
资源限制的挑战
什么是资源限制
资源限制是指在并发编程时,程序的执行速度受限于计算机硬件资源或软件资源
资源限制引发的问题
并发编程,将代码执行速度加快的原则是将代码中串行执行的部分变成并发执行,但将串行执行的代码演变成并发执行,需要考虑到资源限制,资源受限的情况下,串行到并发的演变反而会使程序执行变得更慢,因为增加了上下文切换和资源调度的时间。
如何解决资源限制问题
硬件资源限制,考虑使用集群并行执行程序,既然单机资源有限,那就让程序在多机上运行。
软件资源限制,考虑使用资源池将资源复用,比如使用连接池将数据库和Socket连接复用,或者在调用对方webService接口获取数据时,只建立一个连接。
在资源限制情况下进行并发编程
1.根据不同的资源限制调整程序的并发度。涉及数据库连接数的sql操作,如果sql语句执行非常快,但是线程数量比数据库连接大很多,则某些线程会被阻塞,等待数据库连接。
博客搬家: 大坤的个人博客 欢迎评论哦~