美文网首页
1. 并发编程的挑战

1. 并发编程的挑战

作者: ygxing | 来源:发表于2020-02-25 09:20 被阅读0次

    1. 上下文切换

    即使单核CPU也支持多线程运行代码, CPU会给每个线程分配CPU时间片, 时间片一般是几十毫秒, 然后CPU通过时间片分配算法不停地切换线程, 循环执行任务, 让我们感觉多个线程是同时执行的

    CPU在切换任务前会保存上一个任务的状态, 以便下次切回这个任务的时候, 可以再加载到这个任务的状态, 任务从保存到再加载的过程就是一次上下文切换

    1.1 多线程一定快吗

    线程有创建和上下文切换的开销, 当线程执行的任务过于简单, 那么线程的创建和上下文开销超过了任务的执行时间, 那么多线程可能会更慢

    资源限制导致多线程更慢, 例如: 网络IO, 磁盘IO等

    1.2 如何减少上下文切换
    1. 无锁并发编程
      • 例如将数据ID按照Hash取模分段, 不同的线程处理不同段的数据
    2. CAS算法
      • Atomic包使用了CAS算法更新数据, 而不需要加锁
    3. 使用最少的线程和协程
      • 使用线程池
      • 任务很少的时候, 不需要使用很多线程处理, 线程多的话, 大量线程处于等待状态
      • 协程: 单线程实现多任务调度, 单线程里维护多个任务间的切换

    2. 死锁

    //死锁代码演示
    public class DeathLocker {
      
      //被线程1锁住, 线程2需要获取的对象
      private Object lock1 = new Object();
    
      //被线程2锁住, 线程1需要获取的对象
      private Object lock2 = new Object();
    
      public static void main(String[] args) {
        new DeathLocker().lock();
      }
    
      //死锁
      public void lock() {
        //线程1
        Thread thread1 = new Thread(new Runnable() {
          @Override
          public void run() {
            //锁住lock1
            synchronized (lock1) {
              System.out.println("thread1 lock...");
              try {
                Thread.sleep(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              //sleep一秒, 获取lock2
              synchronized (lock2) {
                System.out.println("thread1 get lock2...");
              }
            }
          }
        });
    
        //线程2
        Thread thread2 = new Thread(new Runnable() {
          @Override
          public void run() {
            //锁住线程2
            synchronized (lock2) {
              System.out.println("thread2 lock...");
              try {
                Thread.sleep(1000);
              } catch (InterruptedException e) {
                e.printStackTrace();
              }
              //sleep一秒, 获取lock1
              synchronized (lock1) {
                System.out.println("thread2 get lock1...");
              }
            }
          }
        });
    
        //启动线程
        thread1.start();
        thread2.start();
      }
    }
    

    运行结果:

    thread1 lock...
    thread2 lock...
    

    上面的代码是简单的死锁演示

    如何避免死锁
    1. 避免一个线程同时获取多个锁
    2. 避免一个线程内同时占用多个资源, 尽量保证每个锁只占用一个资源
    3. 尝试使用定时锁, 使用lock.tryLock(timeout)代替使用内部锁机制
    4. 对于数据库锁, 加锁和解锁必须在同一个数据库连接中, 否则会出现解锁失败的情况

    相关文章

      网友评论

          本文标题:1. 并发编程的挑战

          本文链接:https://www.haomeiwen.com/subject/gflzqhtx.html