线程安全代码到底是怎么编写的?
从根源上思考
关你什么屁事
维护公共场所秩序
线程私有资源,没有线程安全问题 共享资源,线程间以某种秩序使用共享资源也能实现线程安全。
什么是线程安全
int func() {
int a = 1;
int b = 1;
return a + b;
}
线程私有资源
线程间共享数据
用于动态分配内存的堆区,我们用C/C++中的malloc或者new就是在堆区上申请的内存 全局区,这里存放的就是全局变量 文件,我们知道线程是共享进程打开的文件
只使用线程私有资源
int func() {
int a = 1;
int b = 1;
return a + b;
}
线程私有资源+函数参数
int func(int num) {
num++;
return num;
}
int func(int* num) {
++(*num);
return *num;
}
int global_num = 1;
int func(int* num) {
++(*num);
return *num;
}
// 线程1
void thread1() {
func(&global_num);
}
// 线程2
void thread1() {
func(&global_num);
}
使用全局资源
int global_num = 100; //初始化一次,此后没有其它代码修改其值
int func() {
return global_num;
}
int global_num = 100;
int func() {
++global_num;
return global_num;
}
线程局部存储
__thread int global_num = 100;
int func() {
++global_num;
return global_num;
}
global_num是全局变量 global_num是线程私有的
函数返回值
int func() {
int a = 100;
return a;
}
int* func() {
static int a = 100;
return &a;
}
class S {
public:
static S& getInstance() {
static S instance;
return instance;
}
private:
S() {}
// 其它省略
}
调用非线程安全代码
int global_num = 0;
int func() {
++global_num;
return global_num;
}
int funcA() {
mutex l;
l.lock();
func();
l.unlock();
}
int func(int *num) {
++(*num);
return *num;
}
void funcA() {
int a = 100;
func(&a);
}
如何实现线程安全
不使用任何全局资源,只使用线程私有资源,这种通常被称为无状态代码 线程局部存储,如果要使用全局资源,是否可以声明为线程局部存储,因为这种变量虽然是全局的,但每个线程都有一个属于自己的副本,对其修改不会影响到其它线程 只读,如果必须使用全局资源,那么全局资源是否可以是只读的,多线程使用只读的全局资源不会有线程安全问题。 原子操作,原子操作是说其在执行过程中是不可能被其它线程打断的,像C++中的std::atomic修饰过的变量,对这类变量的操作无需传统的加锁保护,因为C++会确保在变量的修改过程中不会被打断。我们常说的各种无锁数据结构通常是在这类原子操作的基础上构建的 。 同步互斥,到这里也就确定了你必须要以某种形式使用全局资源,那么在这种情况下公共场所的秩序必须得到维护,那么怎么维护呢?通过同步或者互斥的方式,这是一大类问题。
总结
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️
评论