17 synchronized关键字使用 synchronized方法、synchronized块

synchronized方法、synchronized块

线程的同步

并发:同一个对象被多个线程同时操作。

解决方案:让多个线程排队操作对象。

使用队列和锁解决多线程的并发问题。
同进程的多线程共享同一块存储空间,当多个线程同时访问某块内存空间时,就存在并发问题。为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized;当线程获得对象的排它锁,该线程独占资源,其他线程必须等待其使用后释放锁即可。

同时也伴随着一些问题:

  • 一个线程持有锁导致其它线程所有需要此锁的线程挂起
  • 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,导致性能问题
  • 也可能存在优先级高的线程等待优先级低的线程释放锁,而导致性能问题。

不安全的线程

通过下面例子说明,线程是不安全。

重复执行下面方法,执行结果可能会有多种情况。

示例1:

java 复制代码
//每次执行结果都不是我们预想的结果
public class UnsafeBuyTicket {
    public static void main(String[] args) {
        BuyTicket buyTicket = new BuyTicket();
        new Thread(buyTicket,"张三").start();
        new Thread(buyTicket,"李四").start();
        new Thread(buyTicket,"王五").start();

    }
}

class BuyTicket implements Runnable {

    private int ticketNums = 10;
    boolean flag = true;

    @Override
    public void run() {
        while (flag) {
            buy();
        }

    }
    //synchronized 同步方法 锁的是this 即BuyTicket对象
    private /**synchronized*/ void buy() {
        if (ticketNums <= 0) {
            flag = false;
            return;
        }
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "买到票" + ticketNums--);
    }
}
java 复制代码
张三买到票9
李四买到票8
王五买到票10
李四买到票7
王五买到票6
张三买到票7
张三买到票5
李四买到票4
王五买到票5
李四买到票3
张三买到票3
王五买到票3
王五买到票2
张三买到票2
李四买到票2
李四买到票1
王五买到票1
张三买到票1

示例2

java 复制代码
public class UnsafeBank {
    public static void main(String[] args) {
        Account funds = new Account("家庭基金", 100);
        new TakeMoney(funds,60,"you").start();
        new TakeMoney(funds,80,"youWife").start();
    }


}

class Account{
    String name;
    int money;

    public Account(String name, int money) {
        this.name = name;
        this.money = money;
    }
}

class TakeMoney extends Thread{
    Account account;
    int takeMoney;//取多钱钱
    int nowMoney;//你口袋里面的钱

    public TakeMoney(Account account, int takeMoney, String name) {
        super(name);
        this.account = account;
        this.takeMoney = takeMoney;
    }

    //synchronized run 是不行的
    @Override
    public void run() {
        //synchronized (account) {
            if ((account.money - takeMoney) < 0) {
                System.out.println(Thread.currentThread().getName() + "余额不足。");
                return;
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            account.money = account.money - takeMoney;
            nowMoney = nowMoney + takeMoney;
            System.out.println(account.name + "余额为:" + account.money);
            System.out.println(this.getName() + "口袋里面的钱:" + nowMoney);
        //}
    }
}
java 复制代码
//执行结果
家庭基金余额为:-40
家庭基金余额为:-40
you口袋里面的钱:60
youWife口袋里面的钱:80

示例3

java 复制代码
public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<String>();
        for (int i = 0; i < 10000; i++) {
            new Thread(() -> {
                //synchronized (list) {
                    list.add(Thread.currentThread().getName());
               // }
            }).start();
        }
        Thread.sleep(3000);//确保上面线程能够执行完毕后在打印list.size
        System.out.println(list.size());
    }
}
java 复制代码
//执行结果
9999

针对上面示例,添加synchronized关键字,使其变为线程安全的。

synchronized方法、synchronized块

关键字synchronized可以修饰方法或者以同步块的形式来进行使用,它主要确保多个线程在同一个时刻,只能有一个线程处于方法或者同步块中,它保证了线程对变量访问的可见性和排他性
synchronized方法控制对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得该方法对象的锁才能执行,否则会阻塞线程,方法执行时独占该锁,知道方法执行完毕才会释放锁。
同步块:synchronized(object){}

synchronized修饰的代码块,会在object对象上加一个监视器。而同步方法会在当前对象this上增加一个监视器。

synchronized同步代码块一般加载共享资源对象上。

java 复制代码
 //示例1 将buy方法改为同步方法
 private synchronized void buy()
 //示例2 试图按照示例1的方案 将run方法改为同步方法,验证是不行的,因为加上run方法的synchronized将对TakeMoney类添加的监视器,但最终操作的确是account对象,
 //此示例 多线程操作的共共享资源是account,并非TakeMoney
 //示例3 在共享资源list增加synchronized关键字,为其添加监视器
相关推荐
Swift社区1 小时前
从 JDK 1.8 切换到 JDK 21 时遇到 NoProviderFoundException 该如何解决?
java·开发语言
DKPT2 小时前
JVM中如何调优新生代和老生代?
java·jvm·笔记·学习·spring
phltxy2 小时前
JVM——Java虚拟机学习
java·jvm·学习
seabirdssss3 小时前
使用Spring Boot DevTools快速重启功能
java·spring boot·后端
喂完待续3 小时前
【序列晋升】29 Spring Cloud Task 微服务架构下的轻量级任务调度框架
java·spring·spring cloud·云原生·架构·big data·序列晋升
benben0443 小时前
ReAct模式解读
java·ai
轮到我狗叫了4 小时前
牛客.小红的子串牛客.kotori和抽卡牛客.循环汉诺塔牛客.ruby和薯条
java·开发语言·算法
Volunteer Technology5 小时前
三高项目-缓存设计
java·spring·缓存·高并发·高可用·高数据量
栗子~~5 小时前
bat脚本- 将jar 包批量安装到 Maven 本地仓库
java·maven·jar