开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 31 天,点击查看活动详情
在面试时候被问了这么一个问题:你怎么理解的多线程? 我想应该这样回答
1. 什么是线程?
线程其实就算操作系统进行调度的最小单位。好抽象啊,能不能具体一点?我们可以这样想,一个进程=一个运行app,而一个运行app里面需要很多线程并发才能使用,这类app叫多线程程序。简⽽⾔之:⼀个程序运⾏后⾄少有⼀个进程,⼀个进程中可以包含多个线程
2. 什么是多线程?
比如我们的软件qq,在运行时候可以进行购物,可以进行视频通话等。单线程是:不管做什么都需要按照顺序,不能同时进行。但是多线程是:你可以一边打视频一边购物,一边聊天等,并没有严格的先后顺序。
3. 关于线程安全
提到多线程,随之而来的问题就是线程安全(不论怎么访问,程序都是我们预想的正确结果)问题。
怎么实现线程安全?
- 互斥同步:同步就是严格线程进入监视器,其他线程必须等他完成退出监视器才能使用。比如Synchronized,JUC包下面的重入锁。
Synchronized加锁可以放在三个地方,第一个是方法前,锁住当前实例,静态方法前面,锁的当前class对象,代码块前面(需要字节声明对象),锁住括号里面匹配的对象。
Synchronized的底层是采用Java对象头来存储锁信息的,并且还支持锁升级。 Java对象头包含三部分,分别是Mark Word用来存储对象的hashCode及锁信息,Class Metadata Address用来存储对象类型的指针,Array length则用来存储数组对象的长度。如果对象不是数组类型,则没有Array length信息。
juc:JUC是java.util.concurrent的缩写,这个包是JDK 1.5提供的并发包,包内主要提供了支持并发操作的各种工具。这些工具大致分为如下5类:原子类、锁、线程池、并发容器、同步工具。
原子类:并发包下提供了atomic子包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式
锁:lock和Synchronize
线程池:
并发容器:第一类是以降低锁粒度来提高并发性能的容器,它们的类名以Concurrent开头,如ConcurrentHashMap。第二类是采用写时复制技术实现的并发容器,它们的类名以CopyOnWrite开头,如CopyOnWriteArrayList。第三类是采用Lock实现的阻塞队列,内部创建两个Condition分别用于生产者和消费者的等待
同步工具: Semaphore类代表信号量,可以控制同时访问特定资源的线程数量;CountDownLatch类则允许一个或多个线程等待其他线程完成操作;CyclicBarrier可以让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续运行。
- 非阻塞同步CAS
使用Synchronize锁时候,只有一个线程可以获得对象的锁,其他线程就会进入阻塞,阻塞状态会引起线程的挂起和唤醒,使用出现了非阻塞同步
CAS 比较并交换,比较典型的使用场景有原子类、AQS、并发容器
- 无同步方式
将共享数据的可见范围限制在一个线程中。这样无需同步也能保证线程之间不出现数据争用问题。 经常使用的就是ThreadLocal类
ThreadLocal,即线程变量,它将需要并发访问的资源复制多份,让每个线程拥有一份资源。由于每个线程都拥有自己的资源副本,从而也就没有必要对该变量进行同步了。ThreadLocal提供了线程安全的共享机制,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。
线程安全的容器有哪些?
javautil包下大部分线程不安全,比如linklist,hashmap等,但是也有安全的比如hashtable,vector,但是他们是基于Synchronized实现的,性能很差,不推荐使用
那么怎么才能使用安全的集合?
1.通过Collections下面的SynchronizedXXX方法,这些集合类保证成线程安全的。
2.以Concurrent开头的类(降低锁粒度提供性能)
3.以CopyOnWrite开头的类(采用写时复制)
4.lock锁
Collections还提供了如下三类方法来返回一个不可变的集合,这三类方法的参数是原有的集合对象,返回值是该集合的“只读”版本。通过Collections提供的三类方法,可以生成“只读”的Collection或Map。 emptyXxx():返回一个空的不可变的集合对象 singletonXxx():返回一个只包含指定对象的不可变的集合对象 unmodifiableXxx():返回指定集合对象的不可变视图
总结:
思路,问多线程,先从线程概念入手,拿到多线程是什么,然后围绕多线程的实现,来引出多线程的不安全,转为怎么才能得到线程安全,然后通过线程安全引出容器安全。