Java多线程之同步问题

Java多线程的重要性不言而喻,在多线程中有一个重要的问题就是共享资源的问题,synchronized关键字就是其中一个,这篇文章简要介绍下synchronized同步代码块的一些知识点。

synchronized的作用

  1. 多线程情况下互斥访问共享资源
  2. 共享资源的修改及时可见
  3. 有效解决重排序问题

synchronized锁对象

synchronized锁对象主要有如下三种:

  1. 普通同步方法,锁的是当前的实例对象
  2. 静态同步方法,锁的是当前的Class对象
  3. 同步代码块锁的是括号里面的对象

下面以多线程抢票案例,将逐个举例

(1)普通同步方法

public class MutilaTion {
    public static void main(String[] args) throws InterruptedException {
        BuyTicket buyTicket = new BuyTicket();
        // 四个线程开始抢票
        for (int i = 0; i < 4; i++){
            new Thread(buyTicket).start();
        }
    }

}
class BuyTicket implements Runnable {
    private int ticket = 10;


    public synchronized void sale() {
        if(ticket > 0){
            ticket--;
            System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
        }
    }

    @Override
    public void run() {
        while(ticket > 0){
            try {
                sale();
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }
}

代码执行结果如下:
在这里插入图片描述

现在我们开始分析代码:首先我们定义买票类,然后再main方法里面使用该买票类实例化四个线程,由于我们重写run方法,只有当票数小于等于0的时候才能结束线程;四个线程进行抢票,在run方法中调用了同步方法sale;因为sale方法被synchronized修饰,一旦进入该方法,BuyTicket类的实例对象(buyTicket对象)就被锁住,只有sale方法执行完毕然后才释放锁,一旦释放锁后,就有CPU来决定接下来执行哪个线程,保证了每个线程取得的ticket属性是互斥的,每次的操作都是原子性的。

现在我们将上述for循环修改,改为如下

for (int i = 0; i < 4; i++){
            new Thread(new BuyTicket()).start();
        }

程序执行结果如下:
在这里插入图片描述
分析结果可知,synchronized关键字用在普通方法上锁定的是类的实例化对象,同一对象才会阻塞,不同对象则不存在阻塞。

(2)静态同步方法

现在我们修改代码为static静态方法

private static int ticket = 10;


    public static synchronized void sale() {
        if(ticket > 0){
            ticket--;
            System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
        }
    }

for循环为

public static void main(String[] args) throws InterruptedException {
        
        // 四个线程开始抢票
        for (int i = 0; i < 4; i++){
            new Thread(new BuyTicket()).start();
        }
    }

执行结果如下:
在这里插入图片描述
由结果可知,程序是存在同步阻塞的,static代码块锁定的是Class类对象。

(3)同步代码块

synchronized(类名.class)为类锁,则无论是都相同对象调用都为同步阻塞.
synchronized(this)为对象锁,不通对象调用时不会阻塞,相同对象调用时为同步阻塞。

private int ticket = 10;


    public void sale() {
        synchronized (this){
            if(ticket > 0){
                ticket--;
                System.out.println("线程:" + Thread.currentThread().getId() + "购票,剩余" + ticket + "张票");
            }
        }
    }
public static void main(String[] args) throws InterruptedException {
        BuyTicket buyTicket = new BuyTicket();
        // 四个线程开始抢票
        for (int i = 0; i < 4; i++){
            new Thread(buyTicket).start();
        }
    }

执行结果:
在这里插入图片描述
若修改for循环为

public static void main(String[] args) throws InterruptedException {

        // 四个线程开始抢票
        for (int i = 0; i < 4; i++){
            new Thread(new BuyTicket()).start();
        }
    }

执行结果为
在这里插入图片描述
可知锁定的是类实例对象
synchronized(类名.class)同上,就不贴代码了。

总结

对象锁:同一个对象实例会同步阻塞,不同对象实例则不会。
类锁:无论是否同一对象均为同步阻塞。
同一个类多个方法被synchronized修饰/代码块/静态方法,则在各个方法之间也是同步阻塞的。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值