文章

多线程同步与死锁

2020.12.6 ・ 共 1248 字,您可能需要 3 分钟阅读

Tags: 多线程, Java, 学习笔记

在多线程的处理过程之中,可以利用 Runnable 描述多个线程操作的资源,在在描述这些资源的时候,如果处理不当就会产生数据的错误操作。

一个简单的卖票程序,创建若干个线程对象来实现。

class MyThread implements Runnable {
  private int ticket = 10;

  public void run() {
    while (true) {
      if (this.ticket > 0) {
        try {
          Thread.sleep(100); // 模拟网络延迟
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "票数:" + this.ticket--);
      } else {
        System.out.println("票没了");
        break;
      }
    }
  }
}

public class Test {
  public static void main(String[] args) {
    MyThread mt = new MyThread();
    new Thread(mt, "A").start();
    new Thread(mt, "B").start();
    new Thread(mt, "C").start();
  }
}

创建三个线程对象,这三个对象会进行五张票的出售。此时会出现问题,线程并不同步。

同步问题产生的主要原因是都在对同一数据进行处理的时候,在其他因素的影响下,会产生不希望的后果,解决这一问题的关键是”锁“:当某一个线程执行操作的时候,其他的线程在外等待。

image-20201206151952838

如果想在程序之中实现锁的功能,就可以使用synchcronized关键字,利用此关键字可以定义同步方法或同步代码块,在同步代码块的操作里只允许一个线程执行。

synchcronized(同步对象) {
  同步代码操作;
}

一般进行同步对象处理的时候,可以采用当前对象进行同步。

class MyThread implements Runnable {
  private int ticket = 10;

  public void run() {
    while (true) {
      synchronized (this) { // 每一次只允许一个线程进行访问
        if (this.ticket > 0) {
          try {
            Thread.sleep(100); // 模拟网络延迟
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(Thread.currentThread().getName() + "票数:" + this.ticket--);
        } else {
          System.out.println("票没了");
          break;
        }
      }
    }
  }
}

public class Test {
  public static void main(String[] args) {
    MyThread mt = new MyThread();
    new Thread(mt, "A").start();
    new Thread(mt, "B").start();
    new Thread(mt, "C").start();
  }
}

加入同步处理之后,程序整体的执行性能会下降,同步会造成性能降低,异步才会达到性能的提升。

只需要在方法定义上使用synchronized关键字即可。

class MyThread implements Runnable {
  private int ticket = 10;

  public synchronized void sell() {
    if (this.ticket > 0) {
      try {
        Thread.sleep(100); // 模拟网络延迟
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "票数:" + this.ticket--);
    } else {
      System.out.println("票没了");
    }
  }

  public void run() {
    while (this.ticket > 0) {
      this.sell();
    }
  }
}

public class Test {
  public static void main(String[] args) {
    MyThread mt = new MyThread();
    new Thread(mt, "A").start();
    new Thread(mt, "B").start();
    new Thread(mt, "C").start();
  }
}

java 类库中会发现,系统中许多类的采用的同步处理采用的都是同步方法。

死锁是在进行多线程同步的处理之中有可能产生的一种问题。指的是若干个线程彼此互相等待的状态。

class Person {
  public synchronized void say(Person2 p2) {
    System.out.println("Person类提示");
    p2.get();
  }

  public synchronized void get() {
    System.out.println("P1得到");
  }
}

class Person2 {
  public synchronized void say(Person p) {
    System.out.println("Person2类提示");
    p.get();
  }

  public synchronized void get() {
    System.out.println("P2得到");
  }
}

public class Test implements Runnable {
  private Person p = new Person();
  private Person2 p2 = new Person2();

  public static void main(String[] args) {
    new Test();
  }

  public void run() {
    p.say(p2);
  }

  public Test() {
    new Thread(this).start();
    p2.say(p);
  }
}

造成死锁的主要原因是因为彼此都在互相等待。死锁是开发中出现的不确定状态,如果代码处理不当则会不定期出现死锁,属于正常开发中的调试问题。

若干个线程访问统一资源时要使用同步处理,不当处理的时候就会出现死锁。