目录
一、通过卖票系统观察多线程的安全隐患
二、synchronized的基本知识
1.使用synchronized的原因
2.synchronized的作用
3.synchronized的基本格式
a.synchronized加在方法名前
b.synchronized用在方法中
4. Java锁机制
5.synchronized注意事项
三、使用synchronized修改卖票系统代码
一、通过卖票系统观察多线程的安全隐患
public class Tickets {
    int num;
    public Tickets(int num) {
        this.num = num;
    }
    public int getNum() {
        return this.num;
    }
    public void setNum(int num) {
        this.num = num;
    }
}
 
public interface TicketService {
    public void sale();
} 
public class SaleTickets implements TicketService {
    private Tickets tickets;
    public SaleTickets() {
    }
    public SaleTickets(Tickets tickets) {
        this.tickets = tickets;
    }
    public void sale() {
        int num = tickets.getNum();
        System.out.println(Thread.currentThread().getName() + "正在出库第" + num + "张票,剩余" + --num + "张票");
        tickets.setNum(num);
    }
} 
public class Window {
    public static void main(String[] args) {
        Tickets tickets = new Tickets(50);
        TicketService saleTickets = new SaleTickets(tickets);
        new Thread(() -> {
            while (tickets.getNum() > 0) {
                saleTickets.sale();
            }
        },"窗口1").start();
        new Thread(() -> {
            while (tickets.getNum() > 0) {
                saleTickets.sale();
            }
        },"窗口2").start();
    }
}
 
运行结果:

运行结果中出现重复出售票数的现象,因此需要了解synchronized关键词的操作来解决这一问题
二、synchronized的基本知识
1.使用synchronized的原因
当多个线程共享一个数据时,如果处理不当,很容易出现线程的安全隐患,比如丢失修改、不可重复读、读脏数据等,所以多线程编程时经常要解决线程同步问题。
2.synchronized的作用
当某个对象用synchronized修饰时,表明该对象在任何时刻都只能由一个线程访问。当一个线程进人synchronized方法后,能保证在任何其他线程访问这个方法之前完成自己的执行。如果一个线程试图访问一个已经启动的synchronized方法,则这个线程必须等待,直到已启动线程执行完毕,再释放这个synchronized方法。
3.synchronized的基本格式
a.synchronized加在方法名前
 public synchronized void Method1() {// (访问权限修饰符+synchronized+返回值类型+方法名)
        /* 方法体 */
    } 
b.synchronized用在方法中
 public void Method2() {// (访问权限修饰符+返回值类型+方法名)
        synchronized (this) {
            /* 代码块 */
        }
    } 
4. Java锁机制
Java使用锁机制保证同步代码块或方法的“原子性”,即保持整体性,不可分隔。Java中每个对象都有一个内置锁,只有当对象具有同步代码时,内置锁才会起作用。当进人一个同步方法时,线程自动获得方法所在类的当前实例相关的锁,即给this对象加锁。当同步代码块或同步方法执行完毕后,同步对象上的锁就被解除,该线程就有机会获取该对象的锁。
5.synchronized注意事项
a.如果synchronized用在类声明中,则表明该类中的所有方法都是synchronized的。
b.锁不属于线程,而是属于对象,一个线程可以拥有多个对象的锁,而只有同一个对象之间的锁才会互斥。
三、使用synchronized修改卖票系统代码
并发环境下,对每个请求创建了多个线程去处理。这段对统计变量操作的代码,曝露在多线程环境下,却没有任何同步处理,从而导致重复卖票问题。
public class Tickets {
    int num;
    public Tickets(int num) {
        this.num = num;
    }
    public int getNum() {
        return this.num;
    }
    public void setNum(int num) {
        this.num = num;
    }
}
 
public interface TicketService {
    public void sale();
} 
public class SaleTickets implements TicketService {
    private Tickets tickets;
    public SaleTickets() {
    }
    public SaleTickets(Tickets tickets) {
        this.tickets = tickets;
    }
    public void sale() {
        synchronized (this) {
            int num = tickets.getNum();
            if (num > 0) {
                System.out.println(Thread.currentThread().getName() + "正在出库" + num + "张票,剩余" + --num + "张票");
                tickets.setNum(num);
            }
        }
    }
} 
public class Window {
    public static void main(String[] args) {
        Tickets tickets = new Tickets(50);
        TicketService saleTickets = new SaleTickets(tickets);
        new Thread(() -> {
            while (tickets.getNum() > 0) {
                saleTickets.sale();
            }
        },"窗口1").start();
        new Thread(() -> {
            while (tickets.getNum() > 0) {
                saleTickets.sale();
            }
        },"窗口2").start();
    }
}
 
运行结果:

此时运行结果中没有出现重复出售票的现象



















