1.transient
作用于变量上,防止属性被序列化。
- 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
- transient关键字只能修饰变量,而不能修饰方法和类。注意,局部变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
- 一个静态变量不管是否被transient修饰,均不能被序列化。
- 若实现Externalizable接口,则没有任何东西可以自动序列化,需要重写writeExternal方法,在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
2.instanceof
instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。
3.volatile
一旦一个共享变量(类成员变量,类静态变量)被volatile修饰之后,那么就具备了两层语义
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。因为值会被强制立即写入主存。
- 禁止进行指令重排序。
- 不保证操作的原子性
public class Test {
public volatile int inc = 0;
public void increase() {
inc++;
}public static void main(String[] args) {
final Test test = new Test();
for(int i=0;i<10;i++){
new Thread(){
public void run() {
for(int j=0;j<1000;j++)
test.increase();
};
}.start();
}
while(Thread.activeCount()>1) //保证前面的线程都执行完
Thread.yield();
System.out.println(test.inc);
}
}
不保证操作的原子性:事实上运行它会发现每次运行结果都不一致,都是一个小于10000的数字。volatile关键字能保证可见性没有错,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。
自增操作的三个子操作(它包括读取变量的原始值、进行加1操作、写入工作内存)可能会分割开执行。
假如某个时刻变量inc的值为100,
①线程1对变量进行自增操作,线程1先读取了变量inc的原始值100,然后线程1被阻塞了;②然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时100,然后进行加1操作,并把101写入工作内存,最后写入主存。这样两个线程执行的都是100+1。
volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。
禁止指令重排序:有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
4.synchronized
由于同一进程里面多线程共享堆,方法区内存,所以在多线程环境下,很有可能会出现同一个数据对象呗多个线程同时访问,这样会出现线程同步问题。
- synchronized关键字可以处理多线程的同步问题。synchronized可以修饰实例变量,对象引用,函数,代码块。
- 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
- 每个对象只有一个锁与之对应。
synchronized关键字的作用域:
- 某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
- 某个类的范围,synchronized static aStaticMethod{}防止多个线程同时访问这个类中的synchronized static 方法。它可以对这个类的所有对象实例起作用。
Class Foo {
// 同步的static 函数
public synchronized static void methodAAA() {
//….
}
public void methodBBB() {
synchronized(Foo.class) // class literal(类名称字面常量)
}
}
代码中的methodBBB()方法是把class literal作为锁的情况,它和同步的static函数产生的效果是一样的,取得的锁很特别,是当前调用这个方法的对象所属的类(Class,而不再是由这个Class产生的某个具体对象了)。
可以推断:如果一个类中定义了一个synchronized 的 static 函数A,也定义了一个 synchronized 的 instance函数B,那么这个类的同一对象Obj在多线程中分别访问A和B两个方法时,不会构成同步,因为它们的锁都不一样。B方法的锁是Obj这个对象,而B的锁是Obj所属的那个Class。
例子: 1 同一个类单一对象锁
public class Resource1 {
public void f() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in f()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void g() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in g()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void h() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in h()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
final Resource1 rs = new Resource1();
new Thread() {
public void run() {
rs.f();
}
}.start();
new Thread() {
public void run() {
rs.g();
}
}.start();
rs.h();
}
}
结果:
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-0:synchronized in f()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
Thread-1:synchronized in g()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
main:synchronized in h()
三个线程(包括main线程)试图进入某个类的三个不同的方法的同步块中,虽然这些同步块处在不同的方法中,但由于是同步到同一个对象(当前对象 synchronized (this)),所以对它们的方法依然是互斥的。
2 同一个类多个对象锁
public class Resource2 {
private byte[] lock1 = new byte[0];
private byte[] lock2 = new byte[0];
public void f() {
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in f()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void g() {
synchronized (lock1) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in g()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void h() {
synchronized (lock2) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName()
+ ":synchronized in h()");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
final Resource2 rs = new Resource2();
new Thread() {
public void run() {
rs.f();
}
}.start();
new Thread() {
public void run() {
rs.g();
}
}.start();
rs.h();
}
}
结果:
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
Thread-0:synchronized in f()
main:synchronized in h()
Thread-1:synchronized in g()
3 ReentrantLock
ReentrantLock是一个可重入且独占式锁,具有和synchronized监视器锁具有相同的基本行为和语义,但是要比synchronized更灵活,更强大,增加了轮询,超时,中断等高级功能。该锁还支持取锁时公平和非公平选择。
公平锁的获取也就是等待时间最长的线程最优先获取锁(FIFO)。但公平锁往往没有非公平锁的效率高。
可重入:指任意线程在获取到锁之后能够再次获取该锁而不会被锁阻塞,ReentrantLock是通过自定义同步器来实现锁的获取与释放。
获取锁:线程获取锁:1首先判断同步状态是否为0,如果是0,则表示该锁还没有被线程持有,则线程获取到锁;2 如果同步状态不是0,需要去识别获取锁的线程是否为当前占据锁的线程,如果是,则再次获取成功(非公平锁)。
5.final
-
final修饰类的时候,表示这个类不能被继承。
-
final修改方法的时候,这个方法不能被重写。一个类的private方法会隐式的指定为final方法。父类被final修饰,资料类不能被重写,
-
final修饰成员变量,表示这个变量必须要初始化,且只能初始化一次。如果这个变量是一个基本类型,则表示这变量的值不能改变,如果是对象引用,则表示这个引用的地址不会变,但是对应的对象里面的内容是可以改变的。
6.static
被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象。static可以用来修饰成员变量和成员方法,当然也可以是静态代码块。主要作用是在于创建独立于具体对象的域变量或者方法。即时没有创建对象,也能使用属性和方法。
在类加载的时候就会加载static修饰的部分代码,存放在方法区,即和类实例存放在同一个地方,可以直接通过类实例使用。