Day06-线程安全问题

线程安全问题

例子:创建三个窗口卖票,总票数为100张,使用实现Runnable接口的方式

  1. 问题:卖票过程中,出现了重票\错票 - - > 出现了线程的安全问题

  2. 问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票

  3. 如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来.直到线程a操作完ticket时,线程才可以开始操作ticket.这种情况即使线程a出现了阻塞,也不能被改变

  4. 在Java中,我们通过同步机制,来解决线程的安全问题

  5. 同步的方式,解决了线程的安全问题. - ->好处

    操作同步代码时,只能有一个线程参与,其他线程等待.相当于一个单线程的过程,效率低 - - >局限性

方式一:同步代码块

synchronized(同步监视器){
   
   需要被同步的代码
   
}
说明:1. 操作共享数据的代码,即为需要被同步的代码 --> 不能包含代码多了,也不能包含代码少了
   2. 共享数据:多个线程共同操作的变量.比如:ticket就是共享数据
   3. 同步监视器,俗称:锁.任何一个类的对象,都可以充当锁.
       要求:读几个线程必须要共用同一把锁
      补充:在实现Runnable接口多线程的方式中,我们可以考虑使用this充当同步监视器
          在继承Thread类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器

方式二:同步方法

如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步的.

(static) synchronized + 同步方法{

}

关于同步方法的总结:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的说明

  2. 非静态的同步方法,同步监视器是:this

    静态的同步方法,同步监视器是:当前类本身

方式三: LOCK锁 - - - JDK5.0新增

  1. 实例化ReentrantLock

  2. 调用锁定方法lock()

  3. 调用解锁方法unlock()

补充:synchroized 与lock的异同?

同:二者都可以解决线程安全问题

异:synchroized机制在执行完相应的同步代码以后,自动的释放同步监视器

lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock())

优先使用LOCK->同步代码块(已经进入了方法体,分配了相应资源)->同步方法(在方法之外)

死锁

  1. 死锁的理解:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁

  2. 说明:出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

    我们使用同步时,要避免出现死锁.

线程通行的三个方法

线程通行的例子:使用两个线程打印1-100,线程1,线程2,交替打印

涉及到的三个方法:

wait():一旦执行此方法,当前线程就会进入阻塞状态,并释放同步监视器

notify():一旦执行此方法,就会唤醒被wait的一个线程.如果有多个线程被wait,就唤醒优先级高的那个.

notifyall():一旦执行此方法,就会唤醒所有被wait的一个线程.

注意点:

  1. 这三个方法只能使用在同步代码块或者同步方法中,不能用LOCK的方法

  2. 这三个方法的调用者必须是同步代码块或同步方法中的同步监视器.否则没回出现IllegalMonitorStateException异常

  3. 这三个方法是定义在java.lang.Object类中

Slepp()和Wait()的异同

1.相同点:一旦执行方法.都可以使得当前的线程进入阻塞状态

2不同点:1) 两个方法声明的位置不同:Thread类中声明sleep() , Object中声明wait()

2) 调用的要求不同: sleep()可以在任何需要的场景下调用. wait()必须使用在同步代码中

3) 关于是否释放同步监视器:如果两个方法都使用在同步代码块或者同步方法中,sleep()不会释放锁,wait() 会释放锁



posted @ 2021-02-07 23:44  伟成李  阅读(35)  评论(0)    收藏  举报