目录
一、通过卖票系统观察多线程的安全隐患
二、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修饰时,表明该对象在任何时刻都只能由一个线程访问。当一个线程进人synchronized方法后,能保证在任何其他线程访问这个方法之前完成自己的执行。如果一个线程试图访问一个已经启动的synchronized方法,则这个线程必须等待,直到已启动线程执行完毕,再释放这个synchronized方法。
public synchronized void Method1() {// (访问权限修饰符+synchronized+返回值类型+方法名)
/* 方法体 */
}
public void Method2() {// (访问权限修饰符+返回值类型+方法名)
synchronized (this) {
/* 代码块 */
}
}
Java使用锁机制保证同步代码块或方法的“原子性”,即保持整体性,不可分隔。Java中每个对象都有一个内置锁,只有当对象具有同步代码时,内置锁才会起作用。当进人一个同步方法时,线程自动获得方法所在类的当前实例相关的锁,即给this对象加锁。当同步代码块或同步方法执行完毕后,同步对象上的锁就被解除,该线程就有机会获取该对象的锁。
a.如果synchronized用在类声明中,则表明该类中的所有方法都是synchronized的。
b.锁不属于线程,而是属于对象,一个线程可以拥有多个对象的锁,而只有同一个对象之间的锁才会互斥。
并发环境下,对每个请求创建了多个线程去处理。这段对统计变量操作的代码,曝露在多线程环境下,却没有任何同步处理,从而导致重复卖票问题。
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();
}
}
运行结果:
此时运行结果中没有出现重复出售票的现象