Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2019-07-29:如何保证线程安全? #108

Open
MoJieBlog opened this issue Jul 29, 2019 · 14 comments
Open

2019-07-29:如何保证线程安全? #108

MoJieBlog opened this issue Jul 29, 2019 · 14 comments
Labels

Comments

@MoJieBlog
Copy link
Collaborator

No description provided.

@ADrunkenLiBai
Copy link

什么是线程安全?

@DaveBoy
Copy link

DaveBoy commented Jul 29, 2019

其实android里面线程安全还是相对较少的,记了那么多多线程的东西 都没怎么用过 这就很气

@ADrunkenLiBai
Copy link

其实android里面线程安全还是相对较少的,记了那么多多线程的东西 都没怎么用过 这就很气

那你把你记得分享下吧,这也算是用处

@xianglongmeng
Copy link

有没有大佬来介绍介绍多线程里面的原子量的用法,还有原理

@zhaoerlei1989
Copy link

当多个线程要共享一个实例对象的值得时候,那么在考虑安全的多线程并发编程时就要保证下面3个要素:

原子性(Synchronized, Lock)

有序性(Volatile,Synchronized, Lock)

可见性(Volatile,Synchronized,Lock)

当然由于synchronized和Lock保证每个时刻只有一个线程执行同步代码,所以是线程安全的,也可以实现这一功能,但是由于线程是同步执行的,所以会影响效率。

下面是对3个要素的详细解释:

原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。

 

在Java中,基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

 

可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取共享变量时,它会去内存中读取新值。

普通的共享变量不能保证可见性,因为普通共享变量被修改后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

更新主存的步骤:当前线程将其他线程的工作内存中的缓存变量的缓存行设置为无效,然后当前线程将变量的值跟新到主存,更新成功后将其他线程的缓存行更新为新的主存地址
其他线程读取变量时,发现自己的缓存行无效,它会等待缓存行对应的主存地址被更新之后,然后去对应的主存读取最新的值。
有序性:即程序执行的顺序按照代码的先后顺序执行。
在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
可以通过volatile关键字来保证一定的“有序性”。
当在处理并发编程的时候,只要程序满足了原子性,可见性和有序性,那么程序就不会发生脏数据的问题。

@MicroKibaco
Copy link

如何保证线程安全?

  • 线程安全的本质?
    在多个线程访问共同资源时,在某一个线程对资源进行写操作中途(写入已经开始,还没结束),其他线程对这个写了一般资源进行了读操作,或者基于这个写了一半操作进行写操作,导致数据问题

所以:保证线程安全有以下操作

  • synchronized
    保证方法内部或代码块内部资源互斥访问,同一时间,由同一Monitor监视代码,最终只能有一个线程在访问

  • volatile
    保证加了 volatile 关键字的字段的操作具有同步性,以及对 long 和 double的操作原子性(long double 原子性这个简单说一下就行).因此 volatile 可以看做简单版本的 synchronized,但是volatile只能保证基本数据类型有效果,无法修改类似 User.name 或 ++ 原子性问题

  • java.util.concurrent.atomic 包:

下面有 AtomicInteger AtomicBoolean 等类,作用和 volatile 基本一致,可以看做通用版本的 volatile

 
AtomicInteger atomicInteger = new AtomicInteger(0);
...
atomicInteger.getAndIncrement();
  • Lock / ReentrantReadWriteLock

加锁机制,但是比 synchronized 麻烦,不推荐直接使用,最好用更复杂的锁 ReadWriteLock

 
Lock lock = new ReentrantLock();
...
lock.lock(); try {
x++;
} finally {
    lock.unlock();
}

@MoJieBlog
Copy link
Collaborator Author

其实android里面线程安全还是相对较少的,记了那么多多线程的东西 都没怎么用过 这就很气

还是蛮多的。项目全局搜索了下,仅仅synchronized这一个关键字,我项目就出现了100多次,44个文件。这还不说其他方式。
项目中用到的synchronized关键字

@ADrunkenLiBai
Copy link

其实android里面线程安全还是相对较少的,记了那么多多线程的东西 都没怎么用过 这就很气

还是蛮多的。项目全局搜索了下,仅仅synchronized这一个关键字,我项目就出现了100多次,44个文件。这还不说其他方式。
项目中用到的synchronized关键字

你是不是单利类写了这么多个

@xiaofeiMophsic
Copy link

线程安全没有明确的定义,一般来讲,多线程情况下程序能表现正确(预期)的行为,那么就认为程序是线程安全的。看下《java并发编程实践这本书》会得到答案。

@chenqi5256969
Copy link

保证线程安全需要保证:
1.原子性:一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行
2.可见性,一个线程对值做出更改,其他线程需要能看到这个值以及被改变
3.有序性,程序的执行要和代码的顺序一致。
对于线程安全,一般的做法是进行同步操作,在某一个时间点,只让一个线程来进行操作

@feelschaotic
Copy link

feelschaotic commented Dec 23, 2019

@yline
Copy link

yline commented Jan 15, 2020

谈谈个人想法:
1,线程安全的根本原因在于:进程间内存隔离,线程间内存共享;因此,在线程操作时其它线程可能会修改内存,从而导致模块执行的效果和预先设定不一致。

2,因此线程安全的保证,换句话说就是:保证当前内存在操作时,无法被其它线程执行

3,可采取的操作:锁同步、某些操作只允许在单一线程执行(例如主线程更新UI采取的就是这种策略)

@MoJieBlog
Copy link
Collaborator Author

其实android里面线程安全还是相对较少的,记了那么多多线程的东西 都没怎么用过 这就很气

还是蛮多的。项目全局搜索了下,仅仅synchronized这一个关键字,我项目就出现了100多次,44个文件。这还不说其他方式。
项目中用到的synchronized关键字

你是不是单利类写了这么多个

不仅仅是单例。好多场景。比如同一个页面有N个接口共同完成。再比如多线程List根据position移除item。

@Jmengfei
Copy link

Jmengfei commented Mar 29, 2021

线程安全根本原因是:进程间内存隔离,线程间内存共享。如何保证主要有两个方面,

  • 线程间同步:线程间同步机制有很多种,主要通过来实现。具体可以看线程间如何同步
  • 线程间隔离:这个是不同于线程间同步机制,主要代表有ThreadLocal。具体可看ThreadLocal是如何保证线程安全及工作原理

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests