欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

(一)并发编程的挑战

程序员文章站 2022-05-13 14:59:38
...

并发编程的目的是为了充分利用硬件资源,让程序跑的更快,但并不意味着开启更多的线程就能够实现这个目的,还需要考虑上下文切换、死锁的问题。

1.1 上下文切换

CPU通过时间片分配的方式来同时执行多个任务,一个任务执行一个时间片以后保存当前任务信息,然后加载另一个任务信息并切换到另一个任务,这就是一次上下文切换的过程。
减少上下文切换次数方法:

  • 无所并发编程,多线程竞争锁会引起上下文切换,可以采取分段加锁;
  • CAS算法,Atomic包使用CAS来更新数据;
  • 使用最小线程,避免创建不必要线程;
  • 协程,单线程实现多任务调度;

1.2 死锁

public class DeadLockDemo {
	private static String A = "A";
	private static String B= "B";
	
	public static main(String[] args) {
		new DeadLockDemo().deadLock();
	}
	
	private void deadLock() {
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				// 获取锁 A
				synchronized (A) {
					try {
						// 休眠2秒再去获取锁B
						Thread.currentThread().sleep(2000);
					} catch(InterruptedException ie) {
						e.printStackTrace();
					}
					// 获取锁B,此时线程t2可能已经获取到了锁B,所以在此等待锁B
					synchronized (B) {
						System.out.println(1);
					}
				}
			}
		});
	Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				// 获取锁B
				synchronized (B) {
					// 获取锁A,此时线程t1已经获取到了锁A,所以在此等待锁A
					synchronized (A) {
						System.out.println(2);
					}
				}
			}
		});
		t1.start();
		t2.start();
	}
}

以上示例演示了造成死锁的过程:获取锁A的线程等待锁B,获取锁B的线程等待锁A,导致两线程同时等待,无法继续执行。
避免死锁的几个方法:

  • 避免一个线程同时获取多个锁;
  • 避免一个线程同时占用多个资源;
  • 使用锁时,使用lock.tryLock(timeout)机制,超时获取不到则返回;
  • 数据库锁,加锁和解锁必须在同一线程里;

1.3 资源限制

网络带宽、CPU、文件等资源限制,考虑使用“池化技术”,比如线程池、数据库连接池、Socket池。