各位老铁们,大家好,今天由我来为大家分享boolean类型不适合作为锁使用,以及b=null是否正确的相关问题知识,希望对大家有所帮助。如果可以帮助到大家,还望关注收藏下本站,您的支持是我们最大的动力,谢谢大家了哈,下面我们开始吧!
pandas和numpy有什么关系
NumPy中的ndarray用于处理多维数值型数组,重点在于进行数值运算,无索引
*Pandas中的Series类似于DataFrame的子集,DataFrame中的每一列都可以看作是一个Series,有索引,方便进行数据的查询,筛选,所以Pandas重点在于进行数据分析
在数学与统计方法上,NumPy中的ndarray只能进行数值型统计,而Pandas中的DataFrame既可以进行数值型,也可以进行非数值型统计。基于可以容纳不同的数据类型而定
1.NumPy
数值型,重点在于进行矩阵运算
N维数组容器,Numpy是以矩阵为基础的数学计算模块。
Numpy专门针对ndarray的操作和运算进行了设计,所以数组的存储效率和输入输出性能远优于Python中的嵌套列表,数组越大,Numpy的优势就越明显。Numpy系统是Python的一种开源的数值计算扩展。这种工具可用来存储和处理大型矩阵,比Python自身的嵌套列表(nestedliststructure)结构要高效的多(该结构也可以用来表示矩阵(matrix))。
ndarray
所有元素的类型相同,存储元素时内存可以连续;Python里list中的元素类型任意,只能通过寻址方式找到下一个元素
ndarray矩阵结构与matlab或者C++或者fortran都很不一样,没有行优先或者列优先的概念
ndarray支持并行化运算(向量化运算),类似于Matlab
Numpy底层使用C语言编写,内部解除了GIL(全局解释器锁),其对数组的操作速度不受Python解释器的限制,效率远高于纯Python代码
2.Pandas
多数据类型,重点在于进行数据分析
pansdas是基于Numpy的一种工具,该工具是为了解决数据分析任务而创建的。Pandas纳入了大量库和一些标准的数据模型,提供了高效地操作大型数据集所需的工具。pandas提供了大量快速便捷地处理数据的函数和方法。使Python成为强大而高效的数据分析环境的重要因素之一。
1
Series
参看书:Series是一种类似于一维数组的对象,它由一组数据(各种NumPy数据类型)以及一组与之相关的数据标签(即索引)组成。**小规模数据**
1
类似于一维数组,索引对象的数据类型是一致的
有了索引标签,方便实际问题中进行信息提取与筛选
python字典类型的数据可以直接给Series对象
Series可以运用ndarray或字典的几乎所有索引操作和函数,融合了字典和ndarray的优点。
属性 说明
values 获取数组
index 获取索引
name values的name
index.name 索引的name
DataFrame
DataFrame就是按照column和index组织起来的数据集合,类似于excel表格,也类似于基本的database结构。DataFrame是一个表格型的数据结构,它含有一组有序的列,每列可以是不同的值类型(数值、字符串、布尔值等)。DataFrame既有行索引也有列索引,它可以被看做由Series组成的字典(共用同一个索引)。
1
DataFrame范例
year state pop debt
one 2000 Ohio 1.5 16.5
two 2001 Ohio 1.7 16.5
three 2002 Ohio 3.6 16.5
four 2001 Nevada 2.4 16.5
five 2002 Nevada 2.9 16.5
six 2003 Nevada 3.2 16.5
Series类似于DataFrame的子集,从上表可以看出,每一列都对应这一个Series
macrodroid如何实现解锁屏幕
可以实现解锁屏幕因为MacroDroid可以在Android设备上创建自动化任务,通过设置触发器和操作来控制设备的各种行为。要实现解锁屏幕的功能,可以设置一个触发器,例如屏幕关闭或者设备锁屏,然后设置一个操作,例如输入屏幕解锁密码或者指纹解锁等,从而实现自动解锁屏幕的功能。此外,MacroDroid还可以与其他应用程序集成,例如用于音乐控制、通话拒绝等,从而进一步改善设备的自动化控制体验。
java怎么避免死锁
1、尽量使用tryLock(longtimeout,TimeUnitunit)的方法(ReentrantLock、ReentrantReadWriteLock),设置超时时间,超时可以退出防止死锁。
2、尽量使用java.util.concurrent(jdk1.5以上)包的并发类代替手写控制并发,比较常用的是ConcurrentHashMap、ConcurrentLinkedQueue、AtomicBoolean等等,实际应用中java.util.concurrent.atomic十分有用,简单方便且效率比使用Lock更高。
3、尽量降低锁的使用粒度,尽量不要几个功能用同一把锁。
4、尽量减少同步的代码块。
百度搜索圈T社区免费行业视频教程
www.aiquanti.com
volatile关键字是什么
主要从以下三点讲解volatile关键字:
volatile关键字是什么?volatile关键字能解决什么问题?使用场景是什么?volatile关键字实现的原理?volatile关键字是什么?在Sun的JDK官方文档是这样形容volatile的:
TheJavaprogramminglanguageprovidesasecondmechanism,volatilefields,thatismoreconvenientthanlockingforsomepurposes.Afieldmaybedeclaredvolatile,inwhichcasetheJavaMemoryModelensuresthatallthreadsseeaconsistentvalueforthevariable.也就是说,如果一个变量加了volatile关键字,就会告诉编译器和JVM的内存模型:这个变量是对所有线程共享的、可见的,每次JVM都会读取最新写入的值并使其最新值在所有CPU可见。volatile可以保证线程的可见性并且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用内存屏障来实现的。
通过这段话,我们可以知道volatile有两个特性:
保证可见性、不保证原子性
禁止指令重排序原子性和可见性原子性是指一个操作或多个操作要么全部执行并且执行的过程不会被任何因素打断,要么都不执行。性质和数据库中事务一样,一组操作要么都成功,要么都失败。看下面几个简单例子来理解原子性:
i==0;//1
j=i;//2
i++;//3
i=j+1;//4
在看答案之前,可以先思考一下上面四个操作,哪些是原子操作?哪些是非原子操作?
答案揭晓:
1——是:在Java中,对基本数据类型的变量赋值操作都是原子性操作(Java有八大基本数据类型,分别是byte,short,int,long,char,float,double,boolean)
2——不是:包含两个动作:读取i值,将i值赋值给j
3——不是:包含了三个动作:读取i值,i+1,将i+1结果赋值给i
4——不是:包含了三个动作:读取j值,j+1,将j+1结果赋值给i
也就是说,只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。
注:由于以前的操作系统是32位,64位数据(long型,double型)在Java中是8个字节表示,一共占用64位,因此需要分成两次操作采用完成一个变量的赋值或者读取操作。随着64位操作系统越来越普及,在64位的HotSpotJVM实现中,对64位数据(long型,double型)做原子性处理(由于JVM规范没有明确规定,不排除别的JVM实现还是按照32位的方式处理)。
在单线程环境中我们可以认为上述步骤都是原子性操作,但是在多线程环境下,Java只保证了上述基本数据类型的赋值操作是原子性的,其他操作都有可能在运算过程中出现错误。为此在多线程环境下为了保证一些操作的原子性引入了锁和synchronized等关键字。
上面说到volatile关键字保证了变量的可见性,不保证原子性。原子性已经说了,下面说下可见性。
可见性其实和Java内存模型的设定有关:Java内存模型规定所有的变量都是存在主存(线程共享区域)当中,每个线程都有自己的工作内存(私有内存)。线程对变量的所有操作都必须在工作内存中进行,而不直接对主存进行操作。并且每个线程不能访问其他线程的工作内存。
举个简单栗子:
比如上面i++操作,在Java中,执行i++语句:
执行线程首先从主存中读取i(原始值)到工作内存中,然后在工作内存中执行运算+1操作(主存的i值未变),最后将运算结果刷新到主存中。
数据运算是在执行线程的私有内存中进行的,线程执行完运算后,并不一定会立即将运算结果刷新到主存中(虽然最后一定会更新主存),刷新到主存动作是由CPU自行选择一个合适的时间触发的。假设数值未更新到主存之前,当其他线程去读取时(而且优先读取的是工作内存中的数据而非主存),此时主存中可能还是原来的旧值,就有可能导致运算结果出错。
以下代码是测试代码:
packagecom.wupx.test;/**
*@authorwupx
*@date2019/10/31
*/
publicclassVolatileTest{
privatebooleanflag=false;
classThreadOneimplementsRunnable{
@Override
publicvoidrun(){
while(!flag){
System.out.println("执行操作");
try{
Thread.sleep(1000L);
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
System.out.println("任务停止");
}
}
classThreadTwoimplementsRunnable{
@Override
publicvoidrun(){
try{
Thread.sleep(2000L);
System.out.println("flag状态改变");
flag=true;
}catch(InterruptedExceptione){
e.printStackTrace();
}
}
}
publicstaticvoidmain(String[]args){
VolatileTesttestVolatile=newVolatileTest();
Threadthread1=newThread(testVolatile.newThreadOne());
Threadthread2=newThread(testVolatile.newThreadTwo());
thread1.start();
thread2.start();
}
}
上述结果有可能在线程2执行完flag=true之后,并不能保证线程1中的while能立即停止循环,原因在于flag状态首先是在线程2的私有内存中改变的,刷新到主存的时机不固定,而且线程1读取flag的值也是在自己的私有内存中,而线程1的私有内存中flag仍未false,这样就有可能导致线程仍然会继续while循环。运行结果如下:
执行操作执行操作
执行操作
flag状态改变
任务停止
避免上述不可预知问题的发生就是用volatile关键字修饰flag,volatile修饰的共享变量可以保证修改的值会在操作后立即更新到主存里面,当有其他线程需要操作该变量时,不是从私有内存中读取,而是强制从主存中读取新值。即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
指令重排序一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。
比如下面的代码
inti=0;
booleanflag=false;
i=1;//1
flag=true;//2
代码定义了一个int型变量,定义了一个boolean类型变量,然后分别对两个变量进行赋值操作。从代码顺序上看,语句1是在语句2前面的,那么JVM在真正执行这段代码的时候会保证语句1一定会在语句2前面执行吗?不一定,为什么呢?这里可能会发生指令重排序(InstructionReorder)。
语句1和语句2谁先执行对最终的程序结果并没有影响,那么就有可能在执行过程中,语句2先执行而语句1后执行。
但是要注意,虽然处理器会对指令进行重排序,但是它会保证程序最终结果会和代码顺序执行结果相同,那么它靠什么保证的呢?再看下面一个例子:
inta=10;//1intr=2;//2
a=a+3;//3
r=a*a;//4
这段代码执行的顺序可能是1->2->3->4或者是2->1->3->4,但是3和4的执行顺序是不会变的,因为处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction2必须用到Instruction1的结果,那么处理器会保证Instruction1会在Instruction2之前执行。
虽然重排序不会影响单个线程内程序执行的结果,但是多线程呢?下面看一个例子:
//线程1Stringconfig=initConfig();//1
booleaninited=true;//2
//线程2
while(!inited){
sleep();
}
doSomeThingWithConfig(config);
上面代码中,由于语句1和语句2没有数据依赖性,因此可能会被重排序。假如发生了重排序,在线程1执行过程中先执行语句2,而此时线程2会以为初始化工作已经完成,那么就会跳出while循环,去执行doSomeThingWithConfig(config)方法,而此时config并没有被初始化,就会导致程序出错。
从上面可以看出,指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。
那么volatile关键字修饰的变量禁止重排序的含义是:
当程序执行到volatile变量的读操作或者写操作时,在其前面的操作肯定已经全部进行,且对后面的操作可见,在其后面的操作肯定还没有进行
在进行指令优化时,不能将volatile变量之前的语句放在对volatile变量的读写操作之后,也不能把volatile变量后面的语句放到其前面执行举个栗子:
x=0;//1
y=1;//2
volatilez=2;//3
x=4;//4
y=5;//5
变量z为volatile变量,那么进行指令重排序时,不会将语句3放到语句1、语句2之前,也不会将语句3放到语句4、语句5后面。但是语句1和语句2、语句4和语句5之间的顺序是不作任何保证的,并且volatile关键字能保证,执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果是对语句3、语句4、语句5是可见的。
回到之前的例子:
//线程1
Stringconfig=initConfig();//1
volatilebooleaninited=true;//2
//线程2
while(!inited){
sleep();
}
doSomeThingWithConfig(config);
之前说这个例子提到有可能语句2会在语句1之前执行,那么就可能导致执行doSomThingWithConfig()方法时就会导致出错。
这里如果用volatile关键字对inited变量进行修饰,则可以保证在执行语句2时,必定能保证config已经初始化完毕。
volatile应用场景synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下三个条件:
对变量的写入操作不依赖变量的当前值,或者能确保只有单个线程更新变量的值
该变量不会与其他状态变量一起纳入不变性条件中在访问变量时不需要加锁上面的三个条件只需要保证是原子性操作,才能保证使用volatile关键字的程序在高并发时能够正确执行。建议不要将volatile用在getAndOperate场合,仅仅set或者get的场景是适合volatile的。
常用的两个场景是:
状态标记量
volatilebooleanflag=false;
while(!flag){
doSomething();
}
publicvoidsetFlag(){
flag=true;
}
volatilebooleaninited=false;
//线程1
context=loadContext();
inited=true;
//线程2
while(!inited){
sleep();
}
doSomethingwithconfig(context);
DCL双重校验锁-单例模式publicclassSingleton{
privatevolatilestaticSingletoninstance=null;
privateSingleton(){
}
/**
*当第一次调用getInstance()方法时,instance为空,同步操作,保证多线程实例唯一
*当第一次后调用getInstance()方法时,instance不为空,不进入同步代码块,减少了不必要的同步
*/
publicstaticSingletongetInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance==null){
instance=newSingleton();
}
}
}
returninstance;
}
}
使用volatile的原因在上面解释重排序时已经讲过了。主要在于instance=newSingleton(),这并非是一个原子操作,在JVM中这句话做了三件事情:
给instance分配内存
调用Singleton的构造函数来初始化成员变量将instance对象指向分配的内存库存空间(执行完这步instance就为非null了)但是JVM即时编译器中存在指令重排序的优化,也就是说上面的第二步和第三步顺序是不能保证的,最终的执行顺序可能是1-2-3,也可能是1-3-2。如果是后者,线程1在执行完3之后,2之前,被线程2抢占,这时instance已经是非null(但是并没有进行初始化),所以线程2返回instance使用就会报空指针异常。
volatile特性是如何实现的呢?前面讲述了关于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。
在《深入理解Java虚拟机》这本书中说道:
观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令。接下来举个栗子:
volatile的Integer自增(i++),其实要分成3步:
读取volatile变量值到local
增加变量的值把local的值写回,让其它的线程可见这3步的JVM指令为:
mov0xc(%r10),%r8d;Load
inc%r8d;Increment
mov%r8d,0xc(%r10);Store
lockaddl$0x0,(%rsp);StoreLoadBarrier
lock前缀指令实际上相当于一个内存屏障(也叫内存栅栏),内存屏障会提供3个功能:
它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成(满足禁止重排序)
它会强制将对缓存的修改操作立即写入主存(满足可见性)如果是写操作,它会导致其他CPU中对应的缓存行无效(满足可见性)volatile变量规则是happens-before(先行发生原则)中的一种:对一个变量的写操作先行发生于后面对这个变量的读操作。(该特性可以很好解释DCL双重检查锁单例模式为什么使用volatile关键字来修饰能保证并发安全性)
总结变量声明为volatile类型时,编译器与运行时都会注意到这个变量是共享的,不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。
在访问volatile变量时不会执行加锁操作,也就不会使执行线程阻塞,因此volatile变量是比sychronized关键字更轻量级的同步机制。
加锁机制既可以确保可见性和原子性,而volatile变量只能确保可见性。
想了解更多Java相关,百度搜索圈T社区www.aiquanti.com,免费视频教程。纯干货
好了,关于boolean类型不适合作为锁使用和b=null是否正确的问题到这里结束啦,希望可以解决您的问题哈!