首页 > 其他分享 >线程同步

线程同步

时间:2022-09-30 06:22:20浏览次数:55  
标签:account 同步 Thread int 线程 new public

线程同步

介绍

多个线程操作同一个资源

在这里插入图片描述

线程同步

现实生活中我们会遇到“同—个资源,多个人都想使用”的问题,比如食堂排队打饭,每个人都想吃饭,最天然的解決办法就是:排队,一个个来。

处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象这时候我们就需要线程同步。线程同步其实就是一种等待机制,多个线程同时访问此对象时,线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

队列和锁

在这里插入图片描述

线程同步:

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突问题。为了保证在方法中访问的数据是正确,在访问时加入锁机制synchronized,当一个线程获得对象的排它锁,就独占这个对象,属于独占资源,其他线程必须等待这个线程使用后释放对象的排它锁后才可访问这个对象。存在以下问题:

  1. 一个线程持有锁会导致其他所有需要此锁的线程挂起;

  2. 在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;

  3. 如果一个优先级高的线程等待个优先级低的线程释放锁会导致优先级倒置,引起性能问题。

不安全的线程案例

不安全买票

package com.gcbeen.thread;

// 不安全买票
public class TestUnsafeBuyTicket {

    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) {
            try {
                buy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    // 买票
    private void buy() {
        // 判断是否有票
        if (ticketNums <= 0) {
            flag = false;
            return;
        }
        // 延迟
        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 买票
        System.out.println(Thread.currentThread().getName()
                + "拿到" + ticketNums--);
    }
}


// 李四拿到10
// 张三拿到9
// 王五拿到8
// 王五拿到7
// 李四拿到7
// 张三拿到7
// 李四拿到6
// 王五拿到5
// 张三拿到4
// 李四拿到3
// 张三拿到1
// 王五拿到2
// 李四拿到0

不安全取钱

package com.gcbeen.thread;

public class TestUnsafeBank {

    public static void main(String[] args) {
        Account account = new Account(100, "养老基金");
        Drawing drawing = new Drawing(account, 60, "夸克");
        Drawing same = new Drawing(account, 60, "same");
        drawing.start();
        same.start();
    }

}

class Account {
    int money;  // 余额
    String cardName;    // 卡名

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

class Drawing extends Thread {
    Account account;    // 账户
    int drawingMoney;   // 取余额
    int nowMoney;   // 个人手里的钱

    public Drawing(Account account, int drawingMoney, String name) {
        // super(name) =  父类构造方法(name)
        super(name);
        this.account = account;
        this.drawingMoney = drawingMoney;
    }

    // 取钱
    @Override
    public void run() {
        // 判断是否有钱
        if (account.money - drawingMoney < 0) {
            System.out.println(Thread.currentThread().getName() + "余额不足,不能进行取钱");
            return;
        }
        try {
            Thread.sleep(100); // 放大问题的发生性
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 卡内金额 = 余额-个人手里的钱
        account.money = account.money - drawingMoney;
        // 个人手里的钱
        nowMoney = nowMoney + drawingMoney;
        System.out.println(account.cardName + "余额为:" + account.money);
        // this.getName()==Thread.currentThread().getName()
        System.out.println(this.getName() + "手里的钱:" + nowMoney);
    }
}


// 养老基金余额为:40
// 养老基金余额为:40
// same手里的钱:60
// 夸克手里的钱:60

线程不安全的集合

package com.gcbeen.thread;

import java.util.ArrayList;
import java.util.List;

// 线程不安全的集合
public class TestUnsafeList2 {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(Thread.currentThread().getName());
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(Thread.currentThread().getName());
            }
        }).start();

        new Thread(() -> {
            for (int i = 0; i < 10000; i++) {
                list.add(Thread.currentThread().getName());
            }
        }).start();
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }

}
// 可能输出情况
// 情况一
// 29148
// 情况二
// 数组越界异常
// Exception in thread "Thread-0" java.lang.ArrayIndexOutOfBoundsException: 14053
//    at java.util.ArrayList.add(ArrayList.java:465)
//    at com.gcbeen.thread.TestUnsafeList2.lambda$main$0(TestUnsafeList2.java:14)
//    at java.lang.Thread.run(Thread.java:748)
// 29294

标签:account,同步,Thread,int,线程,new,public
From: https://www.cnblogs.com/gcbeen/p/16743653.html

相关文章

  • 线程 synchroized
    线程synchroizedsynchroized同步方法由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提岀一套机制,这套机制就是synchronized关......
  • 线程的实现-线程的创建(三种方式)
    线程实现线程的创建(三种方式)1.继承Thread类(重要)自定义线程类继承Thread类;重写run()方法,编写线程执行体;创建线程对象,调用start()方法启动线程。packagecom.gcbee......
  • 委托/事件/线程传参简单理解
    写了很多代码,但几乎都没写过委托/事件/线程传参方面应用的代码因此自己总很容易理解后又遗忘今天又重温了一下因此以最简单的方式的代码方式写下来帮助理解1.线程传参[简单......
  • swoole基础进阶之进程篇1 进程、线程、协程
    视频https://www.bilibili.com/video/BV1oJ411U7bc/或https://edu.51cto.com/center/course/lesson/index?id=412750......
  • 线程简介
    线程简介一、多任务现实中太多这样同时做多件事情的例子了,看起来是多个任务都在做,其实本质上我们的大脑在同一时间依旧只做了一件事情。二、多线程原来是一条车道,车......
  • 多线程
    packagecom.bkc.bpmp.modules.external.service;importjava.util.concurrent.*;importjava.util.Date;importjava.util.List;importjava.util.ArrayList;/***Java线程......
  • python 线程
    1.1线程的背景知识importthreading#导入线程相关的模块t=threading.current_thread()#返回当前线程print(t)#主线程执行print(t.getName())#线程名字print......
  • python5种线程锁
    # 线程安全线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会......
  • 原生 Redis 跨数据中心双向同步优化实践
    原生Redis跨数据中心双向同步优化实践一、背景公司基于业务发展以及战略部署,需要实现在多个数据中心单元化部署,一方面可以实现多数据中心容灾,另外可以提升用户请求访问......
  • 原生 Redis 跨数据中心双向同步优化实践
    原生Redis跨数据中心双向同步优化实践一、背景公司基于业务发展以及战略部署,需要实现在多个数据中心单元化部署,一方面可以实现多数据中心容灾,另外可以提升用户请求访问......