线程安全问题
关于线程安全问题,就是指在高并发的情况下多个线程操作同一个资源的时候,就会出现该资源被多次修改导致出现与预期不一致的结果。
为了处理这种问题,就必须使用同步,所谓同步就是指多个操作在同一个时间段内只能又一个线程线程进行,其他线程要等待此线程完成之后才可以继续执行。需要使用到 synchronized 关键字。
synchronized关键字
关键字 synchronized 有两种用法:
- 在方法中定义。
- 定义方法:
public synchronized void method()
- 定义方法:
- 使用 synchronize 块(同步块)。
- 同步块:
public void method(){
synchronized(表达式){
}
}
代码示例
同步代码块
使用 synchronized
关键字来锁定类中所有的同步非静态方法,需要使用 this
作为 synchronized
块的参数传入 synchronized
中。
class MySyncBlockThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
for (int i = 0; i < 11; i++) {
// 同步块必须锁一个对象,一般我们锁当前对象
// 当前操作每次只允许一个对象进入
synchronized (this) {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + this.ticket--);
}
}
}
}
}
public class SyncBlock {
public static void main(String[] args) {
MySyncBlockThread mt = new MySyncBlockThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
控制台输出:
票贩子A->10
票贩子C->9
票贩子C->8
票贩子B->7
票贩子C->6
票贩子A->5
票贩子C->4
票贩子C->3
票贩子C->2
票贩子B->1
同步方法
class MySyncBlockThread implements Runnable {
private int ticket = 10;
@Override
public void run() {
for (int i = 0; i < 11; i++) {
sale();
}
}
// 同步方法
public synchronized void sale() {
if (this.ticket > 0) {
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "->" + this.ticket--);
}
}
}
public class SyncBlock {
public static void main(String[] args) {
MySyncBlockThread mt = new MySyncBlockThread();
new Thread(mt, "票贩子A").start();
new Thread(mt, "票贩子B").start();
new Thread(mt, "票贩子C").start();
}
}
控制台输出:
由于系统调度的不一样,每次打印的结果也不一样
票贩子A->10
票贩子A->9
票贩子A->8
票贩子A->7
票贩子A->6
票贩子C->5
票贩子C->4
票贩子C->3
票贩子C->2
票贩子C->1
同步代静态方法
同步静态方法,不管你有多少个类实例,同时只有一个线程能获取锁进入这个方法。
同步静态方法是类级别的锁,一旦任何一个线程进入这个方法,其他所有线程将无法访问这个类的任何同步类锁的方法。
// 用在静态方法
private synchronized static void staticMethod() {
System.out.println("staticMethod");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}