一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
一、上下文切换
1、并发编程真的快吗?什么是上下文切换?
答案是不一定,根据测试结果,当数据小于百万的时候并发并没有串行快,这是为什么那?单核处理器的多线程并发,其实就是CPU个每个线程分配时间片,当时间片执行完就需要切换另一个线程的时间片来执行,时间片是非常小的单位,所以我们会觉得是并发!而他们发生切换的时候需要保存当前状态,然后在切换,而保存在切换之后的重新加载就是上下文切换,这也是比较浪费时间的!
public class Demo01 {
/**
* count = 10000001:
* concurrency:31ms,b:-50000005
* serial:39ms,b:-50000005,a:50000005
*
* count = 1000001;
* concurrency:17ms,b:-500005
* serial:12ms,b:-500005,a:500005
*
* count = 100001;
* concurrency:11ms,b:-500005
* serial:8ms,b:-500005,a:500005
*/
private static final long count = 1000001;
public static void concurrency() throws InterruptedException {
long startTime = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (int i = 0; i < count; i++){
a += 5;
}
}
});
thread.start();
int b = 0;
for (int i = 0; i < count; i++){
b -= 5;
}
thread.join();
long time = System.currentTimeMillis() - startTime;
System.out.println("concurrency:" + time + "ms,b:" + b);
}
public static void serial(){
long startTime = System.currentTimeMillis();
int a = 0;
for (int i = 0; i < count; i++){
a += 5;
}
int b = 0;
for (int i = 0; i < count; i++){
b -= 5;
}
long time = System.currentTimeMillis() - startTime;
System.out.println("serial:" + time + "ms,b:" + b + ",a:" + a);
}
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}
}
2、如何减少上下文切换?
- 使用无锁并发编程,没有锁的话就会减少线程之间的竞争,从而减少上下文切换!
- 使用CAS算法,使用CAS更新值无需加锁!
- 使用最少线程,尽量减少线程的时候从而避免上下文切换!
- 协程,可以试试单线程维护多个任务的切换!
二、死锁
1、什么是死锁?
线程之间都拿着对方需要的资源,并且都不是释放资源,从而导致死锁!例如两个共享资源A和B,第一个线程先拿到A后锁住A,准备拿B,但是在第一个线程准备拿B的时候,第二个线程已经拿到B并且锁住,然后准备去拿资源A,这时候就发生了死锁了!这就是死锁!
public class Demo02 {
private static String a = "a";
private static String b = "b";
public static void main(String[] args) {
Thread thread_A = new Thread(new Runnable() {
@Override
public void run() {
synchronized (a){
try {
TimeUnit.SECONDS.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (b){
System.out.println(Thread.currentThread().getName());
}
}
}
},"Thread_A");
Thread thread_B = new Thread(new Runnable() {
@Override
public void run() {
synchronized (b){
synchronized (a){
System.out.println(Thread.currentThread().getName());
}
}
}
}, "Thread_B");
thread_A.start();
thread_B.start();
}
}
2、如何避免死锁
- 避免一个线程获取多个锁
- 避免一个线程同时占用多个资源
- 使用定时锁,例如
lock.tryLock(); - 使用数据库锁,加解锁都是在一个数据库连接当中!
感谢大家的阅读,我是Alson_Code,一个喜欢把简单问题复杂化,把复杂问题简单化的程序猿! ❤