01Java并发编程的艺术之并发编程的挑战
一、上下文切换
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,一个喜欢把简单问题复杂化,把复杂问题简单化的程序猿! ❤