13. Java 关键字:transient,instanceof,volatile,synchronized,final,static,const

1.transient

作用于变量上,防止属性被序列化。

  1. 一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
  2. transient关键字只能修饰变量,而不能修饰方法和类。注意,局部变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
  3. 一个静态变量不管是否被transient修饰,均不能被序列化。
  4. 若实现Externalizable接口,则没有任何东西可以自动序列化,需要重写writeExternal方法,在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。

2.instanceof

instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例。instanceof通过返回一个布尔值来指出,这个对象是否是这个特定类或者是它的子类的一个实例。

3.volatile

一旦一个共享变量(类成员变量,类静态变量)被volatile修饰之后,那么就具备了两层语义

  1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。因为值会被强制立即写入主存。
  2. 禁止进行指令重排序。
  3. 不保证操作的原子性

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

由于同一进程里面多线程共享堆,方法区内存,所以在多线程环境下,很有可能会出现同一个数据对象呗多个线程同时访问,这样会出现线程同步问题。

  1. synchronized关键字可以处理多线程的同步问题。synchronized可以修饰实例变量,对象引用,函数,代码块。
  2. 无论synchronized关键字加在方法上还是对象上,它取得的锁都是对象,而不是把一段代码或函数当作锁――而且同步方法很可能还会被其他线程的对象访问。
  3. 每个对象只有一个锁与之对应。

synchronized关键字的作用域:

  1. 某个对象实例内,synchronized aMethod(){}可以防止多个线程同时访问这个对象的synchronized方法(如果一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法)。这时,不同的对象实例的 synchronized方法是不相干扰的。也就是说,其它线程照样可以同时访问相同类的另一个对象实例中的synchronized方法;
  2. 某个类的范围,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

  1. final修饰类的时候,表示这个类不能被继承。

  2. final修改方法的时候,这个方法不能被重写。一个类的private方法会隐式的指定为final方法。父类被final修饰,资料类不能被重写,

  3. final修饰成员变量,表示这个变量必须要初始化,且只能初始化一次。如果这个变量是一个基本类型,则表示这变量的值不能改变,如果是对象引用,则表示这个引用的地址不会变,但是对应的对象里面的内容是可以改变的。

 6.static

被static修饰的成员变量叫做静态变量,也叫做类变量,说明这个变量是属于这个类的,而不是属于是对象。static可以用来修饰成员变量和成员方法,当然也可以是静态代码块。主要作用是在于创建独立于具体对象的域变量或者方法。即时没有创建对象,也能使用属性和方法。

在类加载的时候就会加载static修饰的部分代码,存放在方法区,即和类实例存放在同一个地方,可以直接通过类实例使用。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值