多个线程处理同一个对象时,并且某些线程还想修改这个对象,就需要线程同步,线程同步其实是一种等待机制,多个想要获取这个资源的线程要进入对象的等待池形成队列,前面的线程使用完,下个线程才能获取资源。
下面这是一种线程不安全的例子
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "线程 1").start();
new Thread(buyTicket, "线程 2").start();
new Thread(buyTicket, "线程 3").start();
}
}
class BuyTicket implements Runnable {
private int ticketNum = 100;
boolean flag = true;
@Override
public void run() {
while (flag) {
if (ticketNum < 0) {
flag = false;
}
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNum--);
}
}
}
由于多个线程共享同一块存储空间,为保证数据的正确性,在访问时加入了锁机制synchronized,当一个线程获取了对象的排他锁,就会独占资源,其他线程必须等待,使用完成之后释放锁,供其他对象使用,会存在一些问题:
- 一个线程持有锁会导致其他需要此锁的线程挂起。
- 加锁和释放锁会导致较多的上下文切换和调度延迟,引起性能问题。
- 如果优先级低的锁优先获取了锁,会导致优先级倒置,引起性能问题。
synchronized包括两种用法:方法和synchronized代码块。
synchronized方法控制“对象的”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞。
这次测试下在方法上加上synchronized关键字。
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket, "线程 1").start();
new Thread(buyTicket, "线程 2").start();
new Thread(buyTicket, "线程 3").start();
}
}
class BuyTicket implements Runnable {
private int ticketNum = 100;
boolean flag = true;
@Override
public void run() {
while (flag) {
buy();
}
}
private synchronized void buy() {
System.out.println(Thread.currentThread().getName() + "拿到" + ticketNum--);
if (ticketNum < 0) {
flag = false;
}
}
}
这次运行就正常了



