JavaSE(19)——多线程知识点

多线程知识点

  1. 多线程使用的问题

  2. 线程安全问题

    多个线程操作共享变量产生的问题

  3. 线程性能问题

    在共享方法上使用synchronized关键字可以解决线程安全问题,但是带来了严重的性能问题

  4. 解决方法

    使用多线程时,一定要保证线程是安全的。解决线程安全问题的方法:

    • 无状态 (没有共享变量)
    • 使用final使该引用变量不可变 (如果该对象也引用了其他对象,那么无论是发布还是使用都需要加锁)
    • 加锁 (内置锁,显式Lock锁)
    • 使用JDK提供的线程安全类
      • 原子性 (可以使用AtomicLong等类来实现原子操作)
      • 容器 (ConcurrentHashMap等)
      • ……

原子性与可见性

  1. 原子性

    在多线程中很多时候都是因为某个操作不是原子性的,使数据混乱出错。如果操作的数据是原子性的,那么就可以很大程度上避免线程安全问题。

    • count++ ,先读取,后加一,再赋值。如果该操作是原子性的,那么就可以实现线程安全(原子性)

    原子性就是执行某一个操作是不可分割的。

    • 如Count++ 操作,它就不是一个原子性的操作,而是分成了三个步骤来实现这个操作的。
    • JDK中有atomic包实现了原子操作
  2. 可见性

    对于可见性,java提供了一个关键字:Volatile

    • 可以任务,volatile是一种轻量级的同步机制
    • volatile仅仅用来保证该变量对所有线程的

    volatile仅仅用来保证该变量对有多线程的可见性,但不保证原子性。

    • 保证该变量对所有线程的可见性
      • 在多线程的环境下,当这个变量修改时,所有的线程都会知道该变量被修改了,也就是所谓的“可见性”
    • 不保证原子性
      • 修改变量实质上是在JVM中分了好几步,而在这几步内,它是不安全的

    使用volatile修饰的变量保证了三点:

    • 一旦你完成写入,任何访问这个字段的线程将会得到最新的值
    • 在你写入前,会保证所有之前发生的事已经发生,并且任何更新过的数值也是可见的,因为内存屏障会把之前的写入值都刷新到缓存。
    • volatile可以防止重排序。如果声明了volatile,那么CPU、编译器就会知道这个变量是共享的,不会被缓存在寄存器或者其他不可见的地方。

    一般来说,volatile大多用于标志位上(判断操作),满足以下条件才应该使用volatile修饰变量:

    • 修改变量时不依赖变量的当前值 (因为volatile是不保证原子性的)
    • 该变量不会纳入到不变性条件中 (该变量是可变的)
    • 在访问变量的时候不需要加锁 (加锁就没必要使用volatile这种轻量级同步机制了)

线程封闭

在多线程的环境下,只要我们不使用成员变量 (不共享数据),那么就不会出现线程安全的问题了。

在一个web应用中,有很多的servlet,但servlet并不加锁,所有的数据都是在方法上操作的,每个线程都拥有自己的变量,互不干扰

在方法上操作,只要保证不再方法上发布对象 (每个变量的作用于仅仅停留在当前的方法上),那么线程就是安全的。

在线程封闭上还有另一种方法,就是使用ThreadLocal。

这个类的API可以保证每个线程自己独占一个变量

不变性

前面描述的共享变量都是可变的,正是因为可变,才会出现线程安全问题。如果状态是不可变的,那么随便多少个线程访问都是没有问题的。

java提供了final修饰符,但final仅仅是不能修改该变量的引用,但是引用里边的数据是可以改的。

final HashMap<Person> hashMap = new HashMap<>(); 

就像这样,这个HashMap,用final修饰了。但是它仅仅保证了该对象引用hashMap变量所指向是不可变的,但是hashMap内部的数据是可变的,也就是说,可以add,remove等操作集合。

因此,仅仅只能够说明hashMap是一个不可变的对象引用

不可变的对象引用在使用的时候还是需要加锁

  • 或者把Person也设计出是一个线程安全的类
  • 因为内部的状态是可变的,不加锁或者person不是线程安全类,操作都是有危险的。

要将对象设计成不可变对象,那么需要满足三个条件:

  • 对象创建后状态不能修改
  • 对象所有的域都是final修饰的
  • 对象是正确创建

线程安全性委托

很多时候需要实现线程安全不需要自己设计和加锁

可以使用JDK提供的**JUC对象(java.util.concurrent)**来完成线程安全设计

总结

正确使用多线程能够提高我们应用程序的效率,同时给我们会带来非常多的问题,这些都是我们在使用多线程之前需要注意的地方。

无论是不变性、可见性、原子性、线程封闭、委托这些都是实现线程安全的一种手段。要合理地使用这些手段,我们的程序才可以更加健壮!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值