2019-03-15:如何实现多线程中的同步?
Moosphan opened this issue · comments
NO 知啊
多线程同步跟异步这不是两种事物吗,不是应该线程池跟线程锁吗
我了解到的, 大概是这样的几种方式:
- volatile-某种简单的逻辑下, 是可以的;
- synchronized;
- reentrantLock;
- cas=compare and swap(set), 就是 unsafe 类;
- handler(有点勉强, 他的实现本身依赖上面的一些技术)
@Ssuiyingsen 回答的, 和这个问题, 有点偏离, 不建议这样回答(重点在于解决同步问题, 不是怎么跑起来多个线程);
线程同步:
1、wait/notify
2、加锁,reentrantLock 以及读写锁
3、synchronized关键字(本质也是加锁)
4、cas
多线程同步和异步不是一回事。
几种情况,
1.就是大家说的synchronized 他可以保证原子性,保证多个线程在操作同一方法时只有一个线程可以持有锁,并且操作该方法,
2.就是手动调用读写锁,
3.手动操作线程的wait和notify
4. volatile我记得是没有原子性的,他可以保证内存可见性,在多线程的情况下保证每个线程的数据都是最新的
线程间的同步实质是保证相城**享变量的数据同步
- volatile关键字,在get和set的场景下是可以的,由于get和set的时候都加了读写内存屏障,在数据可见性上保证数据同步。但是对于++这种非原子性操作,数据会出现不同步
- synchronized对代码块或方法加锁,结合wait,notify调度保证数据同步
- reentrantLock加锁结合Condition条件设置,在线程调度上保障数据同步
- CountDownLatch简化版的条件锁,在线程调度上保障数据同步
- cas=compare and swap(set), 在保证操作原子性上,确保数据同步
- 参照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.同步同步
- 只有一个线程写,其他都是读,用 volatile
- 多个线程写,用 Synchronized
- 多个线程写+设置超时+自行决定释放,用 ReentranLock
- 想提高效率,不单单一个线程进入临界区,用并发包,如:ConcurrentHashMap、LinkBlockingQueue、AtomicXXX。
- 读比写多,用 CopyOnWriteXXX
- 希望控制线程的并发数量,用 Semaphore 信号量
- 想让指定线程等待其他线程,用 CountDownLatch
占坑
- 同步方法
- 同步代码块
- 使用重入锁实现线程同步(ReentrantLock)
- 使用特殊域变量(volatile)实现同步(每次重新计算,安全但并非一致)
- 使用局部变量实现线程同步(ThreadLocal)以空间换时间
- 使用原子变量实现线程同步(AtomicInteger(乐观锁))
- 使用阻塞队列实现线程同步(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。这会极大的简化程序,使程序更加易读、简洁