线程安全类

当多个线程访问某个类时,不管运行时环境采用何种调度方式或者这些进程将如何交替执行,并且在主调代码中不需要额外同步或协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的

线程安全包含的特性

原子性:提供了互斥访问,同一时刻只能有一个线程对它进行操作

可见性:一个线程对主内存的修改可以及时被其他线程观察到

有序性:一个线程观察其他线程的指令执行顺序,由于指令重排序的存在,该观察结果一般杂乱无序。

原子性

保证原子性的手段通常有以下几种

多线程 - (二)synchronized_线程安全

当需要同步时,可能我们首先想到的就是synchronized,但是在竞争特别激烈时,synchronized可能并不是很好的选择,synchronized的使用方式大概有以下几种

  • 修饰静态方法
  • 修饰普通方法
  • 修饰代码块

修饰静态方法

此时的锁是当前类的字节码,类名.class

public static synchronized void task() {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch

修饰普通方法

此时的锁是当前对象,this

public synchronized void task() {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch

修饰代码块

此时的锁是任意对象

public void task() {

// this锁
synchronized (this) {
for (int i = 0; i < 5; i++) {
System.out.println("当前线程名:" + Thread.currentThread().getName() + ",循环的次数:" + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

// 任意对象
Object obj = new Object();
synchronized (obj){

}

// 字节码
synchronized (this.getClass()){

}

// 字节码
synchronized

注意

锁内部代码发生异常,锁自动释放

synchronized会保证共享变量的可见性(保证同步)
java内存模型中关于synchronized有两条规定
1、线程解锁前,必须把共享变量的最新值刷新到主内存
2、线程加锁时将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(加锁与解锁必须是同一把锁)

不要使用String的常量加锁,会出现死循环问题

// 不要这么使用
synchronized ("字符串常量"){

}

synchronized拥有锁重入功能
也就是在使用synchronized时,当一个线程得到一个对象的锁之后,再次请求此对象时是可以再次得到该对象的锁的

public synchronized void methodA(){
methodB();
}

public synchronized void methodB(){
methodC();
}

public synchronized void methodC(){

}