一、基本概念
synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。
synchronized关键字是线程安全的,提供同步机制,是一种重量级锁。
二、使用方法
- 修饰代码块: 作用范围就是代码块
public static void SynObj() {
Object o = new Object();
synchronized (o) {
//todo
}
}
对于同步方法块,锁是Synchonized括号里配置的对象
synchronized修饰代码块,底层提供monitorEnter和monitorExit来达到获取锁和释放锁的过程
- 修饰普通方法: 作用于对象
public synchronized void comMethod() {
//todo
}
对于普通同步方法,锁是当前实例对象。
- 修饰静态方法: 作用于当前类
public synchronized static void staMethod() {
//todo
}
对于静态同步方法,锁是当前类的Class对象。
synchronized修饰普通方法和静态方法,不是通过monitorEnter和monitorExit机制来处理,而是flag上有ACC_SYNCHRONIZED的标识,
底层都是通过获取monitor对象来获取锁,monitor对象是由操作系统提供的mutex锁机制来完成线程获取对象和释放对象。
三、synchronized同步的缺点
- synchronized关键字同步的时候,等待的线程将无法控制,只能死等。
- synchronized关键字同步的时候,不保证公平性,因此会有线程插队的现象。
四、synchronized与volatile的区别
- volatile本质是告诉JVM当前变量在寄存器中的值是不确定的,需要从主存中读取。synchronized则是锁定当前变量,只有当前线程可以访问该变量,其它线程被阻塞。
- volatile仅能使用在变量级别,synchronized则可以使用在变量、方法。
- volatile仅能实现变量修改的可见性,而synchronized则可以保证变量修改的可见性和原子性。(定义long或double时,如果使用volatile关键字(简单的赋值与返回操作),就会获得原子性)
- volatile不会造成线程阻塞,synchronized会造成线程阻塞。
- 使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域。
五、使用练习
模拟火车票售卖场景,三个窗口售卖A-B地的火车票,火车票共100张
编号1——100 打印示例:窗口1售卖火车票66
public class SellTickets implements Runnable{
private int tickets = 100;
private Object object = new Object();
Random random = new Random();
@Override
public void run() {
while (true) {
synchronized (object){
if (tickets > 0) {
try {
Thread.sleep(random.nextInt(300));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售卖火车票" + tickets);
tickets--;
} else {
break;
}
}
}
}
}
public class Test3 {
public static void main(String[] args) {
SellTickets sellTickets = new SellTickets();
Thread thread1 = new Thread(sellTickets,"窗口1");
Thread thread2 = new Thread(sellTickets,"窗口2");
Thread thread3 = new Thread(sellTickets,"窗口3");
thread1.start();
thread2.start();
thread3.start();
}
}
部分运行结果