1. synchronized
1.1 修饰方法
1.1.1 修饰实例方法
- 锁的是当前类实例对象
- 进入同步代码前要获得当前对象实例的锁
public class stu{
public synchronized void save(int money) {
account += money;
}
}
1.1.2 修饰静态方法
- 锁的是当前类对象,会作用于类的所有对象实例
- 因为静态成员不属于任何一个实例对象,是类成员( static 表明这是该类的一个静态资源,不管new了多少个对象,只有一份)。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
public class stu{
public static synchronized void save(int money) {
account += money;
}
}
1.2 修饰代码块
//锁的是该类的实例对象
synchronized(this){
}
//锁的是该类的类对象
synchronized(AccountingSync.class){
}
//锁的是新配置的实例对象
String lock="";
synchronized(lock){
}
1.3 问题
2. volatile
-
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制
-
volatile保证可见性,但不能保证原子性
-
volatile能禁止指令重排序,保证有序性
- 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行
- 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行
//自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存
public volatile int inc = 0;
public void increase() {
inc++;
}
改进方法
//1.使用synchronized
public int inc = 0;
public synchronized void increase() {
inc++;
}
//2.使用lock
public int inc = 0;
Lock lock = new ReentrantLock();
public void increase() {
lock.lock();
try {
inc++;
} finally{
lock.unlock();
}
}
//3.使用java.util.concurrent.atomic.AtomicInteger
public AtomicInteger inc = new AtomicInteger();
public void increase() {
inc.getAndIncrement();
}
3. final
不可变
- final+变量:用final关键字修饰的属性,对于Java编译器来说就是一个“常量”
- 具体的值在编译期间就已经被确定
- 在运行时不能再被修改
- final+方法
- 这个方法不能再被重写(即方法本身不能再被修改)
- 方法的调用过程采用内嵌机制,更为高效
- final+类
- 这个类不能再被任何子类继承
- 这个类内部的所有方法都默认是final方法