在编程中,如果遇到并发安全的情况,有哪些方案可以来实现线程安全呢?以下是几个常见的方案(本文没有局限某种编程语言,而是说的整体思想):
1、单线程
想要实现线程安全,最简单的方式就是干脆不支持多线程,只用单线程来执行,那么就可以从根本上杜绝线程安全的问题了。比如Redis,就是这种思想,在命令执行时,只依赖单线程进行。
2、互斥锁
如果一定要用多线程,比较有效的方式就是排队,那么加锁是一种比较常见的排队方式,无论是synchronized、reentrantLock这种单机锁,还是Redis实现的分布式锁,还是数据库中的乐观锁、悲观锁,本地思想都是通过加互斥锁的方式让多个并发请求排队执行。
✅synchronized和reentrantLock区别?
3、读写分离
除了加锁以外,还有一种做法,那就是读写分离,比如Java并发包中有一种COW机制,即写时复制,主要就是读和写作分离的思想,因为读操作并发是没什么影响的,而写操作的话,只需要让他不发生并发就行了。
比如,CopyOnWriteArrayList使用了一种叫写时复制的方法,当有新元素add到CopyOnWriteArrayList时,先从原有的数组中拷贝一份出来,然后在新的数组做写操作,写完之后,再将原来的数组引用指向到新数组。
4、原子操作
原子操作是不可中断的操作,要么全部执行成功,要么全部失败。在多线程环境中,可以使用原子操作来实现对共享资源的安全访问,例如Java中的AtomicInteger等操作。
原子操作底层一般都是依赖的操作系统的CAS指令,思想也就是Compare And Swap
5、不可变模式
并发问题之所以发生,有个重要原因就是因为有共享变量,试想一下,如果只有读的情况,那么永远也不会出现线程安全的问题,因为多线程读永远是线程安全的,但是多线程读写一定会存在线程安全的问题。
那既然这么说是不是通过只读就能解决并发问题呢?其实最简单的办法就是让共享变量只有读操作,而没有写操作。这个办法如此重要,以至于被上升到了一种解决并发问题的设计模式:不变性(Immutability)模式。
如Java中的String就是不可变模式的一种体现,他的好处就是永远不会出现并发问题。
6、数据不共享
像前面说的,如果没有共享数据,那么就不会有线程安全问题了,除了不可变模式,还有一种我们常用的手段来避免并发问题。那就是用ThreadLocal