Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2019-03-15:如何实现多线程中的同步?

Moosphan opened this issue · comments

NO 知啊

多线程同步跟异步这不是两种事物吗,不是应该线程池跟线程锁吗

我了解到的, 大概是这样的几种方式:

  1. volatile-某种简单的逻辑下, 是可以的;
  2. synchronized;
  3. reentrantLock;
  4. cas=compare and swap(set), 就是 unsafe 类;
  5. handler(有点勉强, 他的实现本身依赖上面的一些技术)

@Ssuiyingsen 回答的, 和这个问题, 有点偏离, 不建议这样回答(重点在于解决同步问题, 不是怎么跑起来多个线程);

线程同步:
1、wait/notify
2、加锁,reentrantLock 以及读写锁
3、synchronized关键字(本质也是加锁)
4、cas

commented

多线程同步和异步不是一回事。
几种情况,
1.就是大家说的synchronized 他可以保证原子性,保证多个线程在操作同一方法时只有一个线程可以持有锁,并且操作该方法,
2.就是手动调用读写锁,
3.手动操作线程的wait和notify
4. volatile我记得是没有原子性的,他可以保证内存可见性,在多线程的情况下保证每个线程的数据都是最新的

线程间的同步实质是保证相城**享变量的数据同步

  1. volatile关键字,在get和set的场景下是可以的,由于get和set的时候都加了读写内存屏障,在数据可见性上保证数据同步。但是对于++这种非原子性操作,数据会出现不同步
  2. synchronized对代码块或方法加锁,结合wait,notify调度保证数据同步
  3. reentrantLock加锁结合Condition条件设置,在线程调度上保障数据同步
  4. CountDownLatch简化版的条件锁,在线程调度上保障数据同步
  5. cas=compare and swap(set), 在保证操作原子性上,确保数据同步
  6. 参照UI线程更新UI的思路,使用handler把多线程的数据更新都集中在一个线程上,避免多线程出现脏读

可以用 synchronized 关键词 作用在代码块上 或者静态非静态方法都可以。
或者 wait/notifyAll。
参数可以加入 volatile参数 让其做到 内存可见性。

多线程同步和异步不是一回事。
几种情况,
1.就是大家说的synchronized 他可以保证原子性,保证多个线程在操作同一方法时只有一个线程可以持有锁,并且操作该方法,
2.就是手动调用读写锁,
3.手动操作线程的wait和notify
4. volatile我记得是没有原子性的,他可以保证内存可见性,在多线程的情况下保证每个线程的数据都是最新的

volitile主要是在CPU指令禁止重排和多线程访问变量时变量发生变化时,各线程的值是同时变化的。

更多的情况下,如果仅仅是一个变量的同步性,我喜欢原子类AtomicInteger AtomicBoolean等等,这样写还简单

thread.join也算吗

多线程下的线程同步,保证代码在多线程下实现 原子性 可见性 有序性
1、volatile 确保了可见性、有序性,但不能保证原子性 对于++这种操作就不能保证同步
2、synchronized 保证了可见性 有序性和原子性

1.继承Thread类,重写run函数方法
2.实现Runnable接口,重写run函数方法
3.实现Callable接口,重写call函数方法
4.HandlerThread
5.AsyncTask很老的一种= =
6.Synchronized 同步

只有6是正确的,其他都是如何异步

1.继承线程类,重构运行函数方法
2.实现Runnable接口,重构运行函数方法
3.实现Callable接口,调用调用函数方法
4.HandlerThread
5.AsyncTask很老的一种= =
6.同步同步

commented
  1. 只有一个线程写,其他都是读,用 volatile
  2. 多个线程写,用 Synchronized
  3. 多个线程写+设置超时+自行决定释放,用 ReentranLock
  4. 提高效率,不单单一个线程进入临界区,用并发包,如:ConcurrentHashMap、LinkBlockingQueue、AtomicXXX。
  5. 读比写多,用 CopyOnWriteXXX
  6. 希望控制线程的并发数量,用 Semaphore 信号量
  7. 想让指定线程等待其他线程,用 CountDownLatch

占坑

  1. 同步方法
  2. 同步代码块
  3. 使用重入锁实现线程同步(ReentrantLock)
  4. 使用特殊域变量(volatile)实现同步(每次重新计算,安全但并非一致)
  5. 使用局部变量实现线程同步(ThreadLocal)以空间换时间
  6. 使用原子变量实现线程同步(AtomicInteger(乐观锁))
  7. 使用阻塞队列实现线程同步(BlockingQueue (常用)add(),offer(),put()
    参考链接:https://www.cnblogs.com/cxxjohnson/p/8536257.html

Java里面进行多线程通讯的主要方式就是共享内存的方式,共享内存主要关注的点有两个:可见有序原子性。Java的内存模型解决了可见性和有序性的问题,而锁解决的是原子性的问题。理想情况下我们希望做到**"同步""互斥"**。主要通过以下几种方式:

  • synchronized: 一般的做法是把修改共享变量的方法加上synchronized。
  • volatile:使用特殊域变量实现同步。volatile禁止了指令的重排序和变量可见性,只针对单次有效。类似i++的话是两次计算,所以无效。这是一种稍弱的同步机制。比synchronized更轻量级
  • ReentrantLock:显示的获得,释放锁,灵活度高。是同步非阻塞,采用的是乐观并发策略。可以简单理解为synchronized的灵活版本。synchronized是同步阻塞, 采用的是悲观并发策略。
  • Semaphore:信号量-控制同时访问的线程数量。这个也可以形成同步机制,一般用于控制对某组资源的访问权限。
  • BlockingQueue:阻塞队列。也是实现的一种,他有很多实现类,例如ArrayBlockingQueue, LinkedBlockingQueue, PriorityBlockingQueue...
    网上有很多说ThreadLocal可以实现,其实是有偏差的。ThreadLocal是给每一个线程都复制了一个副本到ThreadLocalMap中,各自互不干扰。因为操作的是各自不同的对象,所以不存在同步的问题。ThreadLocal隔离了多个线程的数据共享, 从根本上它是不存在数据共享的,也就不需要同步机制了。但是不可否认它是解决线程安全的很好的思路。

如果需要进行多个线程之间进行通信,则使用同步机制;
如果需要隔离多个线程之间的共享冲突,可以使用threadLocal。这会极大的简化程序,使程序更加易读、简洁