Java基础总结二

Java关键字(特点及关键字作用):

(1)被Java语言赋予特殊含义的单词(53个含两个保留字)

(2)关键字都是小写

(3)goto和const作为保留字存在,目前并不使用

(4)关键字在高级的程序开发软件中会识别关键字和 非关键字,关键字会出现特定的颜色,那么在这里需要注意的是:下图中的main不是关键字!

public static void main(String [] args){}
访问修饰符关键字
  本类中 同一个包下 不同包下的子类 不同包下的其他类
private Yes(可访问)      
默认 Yes(可访问) Yes(可访问)    
protected Yes(可访问) Yes(可访问) Yes(可访问)  
public Yes(可访问) Yes(可访问) Yes(可访问) Yes(可访问)
数字类型的关键字
byte 字节型 8bit
char 字符型

16bit

boolean 布尔型  
short 短整型 16bit
int 整型 32bit
float 浮点型 32bit
long 长整型 64bit
double 双精度 64bit
true  
false
null  
其他常见关键字
instanceof A instanceof B instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
static 静态 属性和方法都可以用static修饰,类名.属性。类名.方法。只有内部类可以使用static关键字修饰,调用直接使用类名.内部类名进行调用。static 可以独立存在。静态块。
final 最终的、不可被改变的 方法、类、变量都可以用final来修饰,final修饰的类是不能被继承的,final修饰的方法是不能被子类重写的。final修饰的变量称为常量。
super 父类存储空间的标识 可以理解为父类的引用,通过这个关键字可以访问父类的成员。super.成员变量。super.成员方法
this 当前类的对象引用

this.成员变量。this.成员方法。

native 本地 一个Native Method就是一个java调用非java代码的接口。当我们对代码的效率有要求时,除了优化本身的Java代码,可能会在程序中使用C、或者C++实现该方法。
strictfp 精确浮点 strictfp 关键字可应用于类、接口或方法。使用 strictfp 关键字声明一个方法时,该方法中所有的float和double表达式都严格遵守FP-strict的限制,符合IEEE-754规范。
synchronized 同步 后面的Java总结多线程中记录
transient 短暂的[ˈtrænziənt] 一个对象只要实现了Serilizable接口,这个对象就可以被序列化。但是开发中有些部分不需要被序列化,此时将transien添加到不需要序列化的属性前面,这个属性就不会被序列化。在Java中,对象的序列化可以通过实现两种接口来实现,若实现的是Serializable接口,则所有的序列化将会自动进行,若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
volatile 易变的 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。禁止进行指令重排序。具体参考https://www.cnblogs.com/dolphin0520/p/3920373.html

下面记录一下自己通过对volatile的了解学习到的相关知识点:

(1)计算机在执行程序时,每条指令都是在CPU中执行的,而执行指令过程中,势必涉及到数据的读取和写入。由于程序运行过程中的临时数据是存放在主存(物理内存)当中的,这时就存在一个问题,由于CPU执行速度很快,而从内存读取数据和向内存写入数据的过程跟CPU执行指令的速度比起来要慢的多,因此如果任何时候对数据的操作都要通过和内存的交互来进行,会大大降低指令执行的速度。因此在CPU里面就有了高速缓存。

i=i+1;

当线程执行这个语句时,会先从主存当中读取i的值,然后复制一份到高速缓存当中,然后CPU执行指令对i进行加1操作,然后将数据写入高速缓存,最后将高速缓存中i最新的值刷新到主存当中。 

对于单线程来讲,上述代码是没有任何问题的,但是对于多线程来讲,就有可能会产生多个线程对同一个i值进行加1操作,最后就只是这个i值加了1,并没有编程每个线程都给i值加了1.。这个时候就出现了缓存一致性问题。为了解决这个问题。就有两种方式:分别是通过在总线加LOCK锁的方式和通过缓存一致性原则。所谓在总线加锁就是阻塞了其他的CPU对内存等部件的访问,即可以理解为多核CPU变成了单核CPU,极大的降低了效率。而对于缓存一致性原则,最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

(2)并发编程的三个概念

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

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

有序性:即程序执行的顺序按照代码的先后顺序执行。

JVM在真正执行这段代码的时候会保证写在前面的语句一定会在后面的语句前执行吗?不一定,为什么呢?这里可能会发生指令重排序(Instruction Reorder)。指令重排序,一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。对于指令重排序需要注意的是:处理器在进行重排序时是会考虑指令之间的数据依赖性,如果一个指令Instruction 2必须用到Instruction 1的结果,那么处理器会保证Instruction 1会在Instruction 2之前执行。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

也就是说,要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。

(3)Java内存模型为我们提供了哪些保证以及在java中提供了哪些方法和机制来让我们在进行多线程编程时能够保证程序执行的正确性。

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

x = 10;        //语句1
y = x;         //语句2
x++;           //语句3
x = x + 1;     //语句4

上述代码中只有语句1是原子性操作,其他三个语句都不是原子性操作。语句1是直接将数值10赋值给x,也就是说线程执行这个语句的会直接将数值10写入到工作内存中。语句2实际上包含2个操作,它先要去读取x的值,再将x的值写入工作内存,虽然读取x的值以及 将x的值写入工作内存 这2个操作都是原子性操作,但是合起来就不是原子性操作了。同样的,x++和 x = x+1包括3个操作:读取x的值,进行加1操作,写入新的值。

可见性:对于可见性,Java提供了volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。另外,通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

有序性:在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。在Java里面,可以通过volatile关键字来保证一定的“有序性”。另外可以通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。另外,Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。


注:以上文章仅是个人学习过程总结,若有不当之处,望不吝赐教。

猜你喜欢

转载自blog.csdn.net/m0_37265215/article/details/81352603