熟悉JVM反射机制。
(1)反射的定义
Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
(2)反射的理解
java中实现多态的形式主要有三种:向上转型,抽象类实现,接口类实现。
这三种方式实例化的对象数据类型都需要在运行时才能够确定,反射机制诞生之前,我们不能直接获取这些对象的内部信息。但是反射机制诞生之后,我们可以在运行时,实时动态的获取这些对象的具体的数据类型内部信息。
(3)反射的应用
运行时动态获取任意一个类或任意一个对象的内部信息。(包括编译阶段不能确定数据类型的对象和类)
实现解耦操作,用于框架的实现。
互斥同步。
(1)线程同步互斥基本概念
两个或两个以上的进程或线程在运行过程中协同步调,按预定的先后次序运行,实现的基础就是通过互斥。
(2)线程同步互斥实现方式
Java 提供了两种锁机制来控制多个线程对共享资源的互斥访问,第一个是 JVM 实现的 synchronized(阻塞同步锁),而另一个是 JDK 实现的 ReentrantLock(非阻塞同步锁)。 其中synchronized 是重量级锁,ReentrantLock是轻量级锁。
- synchronized锁住的对象与monitor相关联,其中monitor的两条指令:monitorenter和monitorexit 的字节码文件依赖于底层的操作系统的Mutex Lock来实现的 ,由于使用Mutex Lock需要将当前线程挂起并从用户态切换到内核态来执行,这种切换的代价是非常昂贵的 。所以该锁一般应用在极少锁竞争环境。
- ReentrantLock是基于CAS指令+AQS阻塞队列来实现 。
互斥同步:阻塞同步
synchronized(阻塞同步锁)
互斥同步:非阻塞同步
ReentrantLock(非阻塞同步锁)(lock的子类)(基于CAS实现的乐观锁)
- CAS就是比较并交换,最常见的应用场景就是,就是在数据库中实现乐观锁,我们一般会在数据库表中字段增加一个version字段,如果要求更高一点会使用时间戳。读取出数据时,将此version字段一同读出。同时,将提交数据的版本数据与数据库表对应记录的当前版本信息进行比对,如果提交的数据版本号等于数据库表当前版本号,则予以更新,并将版本号增1,否则认为是过期数据。
- J.U.C 包里面的整数原子类 AtomicInteger,其中的 compareAndSet() 和 getAndIncrement() 等方法都使用了 Unsafe 类的 CAS 操作。
注:基于冲突检测的乐观并发策略 :CAS 指令需要有 3 个操作数,分别是内存地址 V、旧的预期值 A 和新值 B。当执行操作时,只有当 V 的值等于 A,才将 V 的值更新为 B。
synchronized 与ReentrantLock之间的区别
①ReentrantLock显示地获得,释放锁,synchronized隐式获得释放锁
②ReentrantLock可响应中断,synchronized是不可以响应中断的
③ReentrantLock是JDK级别的,synchronized是JVM级别的
④ReentrantLock可以实现公平锁
⑤ReentrantLock通过Condition可以绑定多个条件
⑥底层实现不一样,synchronized是同步阻塞,使用的是悲观并发策略,lock是同步非阻塞,采用的是乐观并发策略。
⑦Lock是一个接口,而synchronized是java中的关键字,synchronized是内置的语言实现
⑧synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象, 因此使用 Lock 时需要在 finally 块中释放锁。
不采用同步措施进行同步
- 将共享资源存放在一个方法的局部变量之中,因为局部变量存储在虚拟机栈中,属于线程私有的 ,不会出现线程安全问题 。
- 看看这些共享数据的代码是否能保证在同一个线程中执行。如果能保证,我们就可以把共享数据的可见范围限制在同一个线程之内,这样,无须同步也能保证线程之间不出现数据争用的问题。(一般是本地线程)
死锁
定义:也就是两个线程在各自拥有锁的情况下,又去尝试获取对方的锁,从而造成的一直阻塞的情况。
原因:
①互斥条件:一个资源只能被一个线程占有,当这个资源被占用后其他线程就只能等待。
②不可剥夺条件:当一个线程不主动释放资源时,此资源一直被拥有线程占有。
③请求并持有条件:线程已经拥有一个资源后仍然不满足,又尝试请求新的资源。
④环路等待条件:产生死锁一定是发生了线程资源环路链。
办法:
改变死锁中的任意一个或多个条件就可以解决死锁问题,其中被修改的条件只有后两个:请求并持有条件和环路等待条件。
- 修改请求并持有条件:获得了一把锁之后不再去请求获取另一把锁
- 破坏环路等待条件: 首先给线程资源请求编号,规定每个线程,必须按编号递增的顺序请求资源,同一线程一次性获取完所有资源。
内存屏障原理。
内存屏障︰是一种屏障指令,它使得CPU或编译器对屏障指令的前和后所发出的内存操作执行一个排序的约束。也叫内存栅栏或栅栏指令。
内存屏障的能力∶
- 1∶阻止屏障两边的指令重排序。
- 2∶写数据的时候加了屏障的话,强制把写缓冲区的数据刷回到主内存中。
- 3∶读数据的时候加了屏障的话,让工作内存/CPU高速缓存当中缓存的数据失效,重新到主内存中获取新的数据。
基本分类∶
- 1:读屏障:Load Barrier : 在读指令之前插入读屏障,让工作内存/CPU高速缓存当中缓存的数据失效,重新到主内存中获取新的数据。
- 2:写屏障: Store Barrier :在写指令之后插入写屏障,强制把写缓冲区的数据刷回到主内存中。
最常使用的指令:StoreLoad:强制把写缓冲区的数据刷回到主内存中,让CPU高速缓存当中缓存的数据失效,重新到主内存中获取新的数据。
volatile
- 防止重排序:一个对象被volatile修饰,以为着所有对该对象操作的指令,都将按照默认顺序执行,不能按照语义相同就重新排序。
- 可见性:在某个线程内的cpu缓存区内,如果被volatile修饰的变量被修改,那么就会触发StoreLoad指令,将缓存区内的变量立即写回主内存,并立即使其他线程内cpu缓存内该变量的值失效。
- 原子性:只能保证单次读写的原子性,不支持多个操作的原子性(也就是不支持事务)
端口
端口是TCP/IP协议族中,应用层进程与传输层协议实体间的通信接口。
TCP/IP协议采用了全局分配(静态分配端口)和本地分配(动态分配端口)相结合的分配方法 。
保留端口的范围是0—1023,又称为众所周知的端口或熟知端口(Well-Known Port) 。
其余的端口号,1024-65535,称为自由端口号,采用本地分配,又称为动态分配的方法。
NAT技术与内网穿透
服务器一般使用的是公网 ip ,因此客户端可以通过NAT技术,将局域网内的ip地址层层转化为公网IP。
但是如果服务器是局域网IP,客户端就无法找到,因为NAT技术只能将ip地址层层转化为公网ip。
因此内网穿透诞生,将局域网ip与公网ip进行映射,因此客户端就可以同过NAT技术将ip地址层层转化为公网ip。
内网穿透方式
- 第一种是直接拥有公网的ip并直接进行映射;
- 第二种是有公网服务器的,采用frp等方式来进行穿透(这种方式不需要有公网的ip,但是需要一个服务器);
(1)第一种方式存在两种形式:
第一种:光猫不能进行端口映射,那么将光猫设置为桥接模式,主机连接光猫,直接拨号上网,使用的ip直接为外网ip。
第二种:光猫可以进行端口映射,那么配置光猫进行接口映射,那么间接使用外网IP。
(2)第二种方式属于直接使用远程服务器进行映射。(和翻墙一个道理)
);
(1)第一种方式存在两种形式:
第一种:光猫不能进行端口映射,那么将光猫设置为桥接模式,主机连接光猫,直接拨号上网,使用的ip直接为外网ip。
第二种:光猫可以进行端口映射,那么配置光猫进行接口映射,那么间接使用外网IP。
(2)第二种方式属于直接使用远程服务器进行映射。(和翻墙一个道理)