java多线程资源竞争

本文通过一个售票系统的例子展示了Java多线程环境下资源竞争的问题,即多个线程对同一变量进行操作导致的数据不一致。并提出了三种解决方案:同步代码块(synchronized)、同步方法(synchronized)和使用锁(ReentrantLock),每种方法都能有效避免资源竞争,确保线程安全。通过示例代码的执行结果,展示了这些方法如何防止超卖现象的发生。
摘要由CSDN通过智能技术生成
  1. 先看一下java多线程资源竞争的案例
/* 当多个线程访问同一个变量时,这个变量被称为共享变量,而当多个线程操作同一个变量时,就会出现资源竞争的问题,
* 举例, 有个变量num为100, 每个线程对其的操作就是减去1(想想一下卖票系统,100个票,有三个售票员[三个线程]),有一个
*  在cpu哪里累死累活抢到了使用权,拿到了100,还没来的及去减1, 完了,cpu被第二只线程抢了过去,一看num还是100,然后执行了100-1
*  num就这样变为了99,这时第一个cpu终于抢到了cpu的使用权,去吧num减1,这一减不要紧,num也变成了99,这样,100这支票被卖出去了两
*  此,那这问题就出现了,票卖重了,这是谁的锅? 或者当卖到最后一张票时,还很容易造成超卖现象
* */

import javax.sql.rowset.spi.SyncFactory;

public class Demo26Class implements Runnable {
    private int num1;
    Object obj = new Object();

    Demo26Class(){

    }

    Demo26Class(int num){
        this.num1 = num;
    }

    @Override
    public void run() {

        while (true) {

            if (num1 > 0){
                // 添加延迟时间,可以更好的看出资源竞争
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售出车票第" + num1-- + "张");
            }
        }
    }
}

// 调用线程共享, 注意,资源竞争不是说创建很多对象,而是一个对象中的多个线程对资源的竞争,比如下面代码中objClass1的对象
// 而不是在new一个对象objClass2,然后再start

public class Demo27Class {

    public static void main(String[] args) {
        int num = 20;
        Demo26Class objClass1 = new Demo26Class(num);
        new Thread(objClass1, "售票车窗1").start();
        new Thread(objClass1, "售票车窗2").start();
        new Thread(objClass1, "售票车窗3").start();
    }
}

执行结果:

售票车窗2售出车票第19张
售票车窗3售出车票第18张
售票车窗1售出车票第20张
售票车窗2售出车票第17张
售票车窗1售出车票第15张
售票车窗3售出车票第16张
售票车窗2售出车票第13张
售票车窗3售出车票第12张
售票车窗1售出车票第14张
售票车窗1售出车票第10张
售票车窗3售出车票第11张
售票车窗2售出车票第9张
售票车窗1售出车票第8张
售票车窗3售出车票第6张
售票车窗2售出车票第7张
售票车窗2售出车票第5张
售票车窗1售出车票第5张
售票车窗3售出车票第5张
售票车窗1售出车票第3张
售票车窗3售出车票第4张
售票车窗2售出车票第2张
售票车窗1售出车票第1张
售票车窗3售出车票第0张
售票车窗2售出车票第-1
  1. 解决方案一:同步代码块 synchronized()
/* 当多个线程访问同一个变量时,这个变量被称为共享变量,而当多个线程操作同一个变量时,就会出现资源竞争的问题,
* 举例, 有个变量num为100, 每个线程对其的操作就是减去1(想想一下卖票系统,100个票,有三个售票员[三个线程]),有一个
*  在cpu哪里累死累活抢到了使用权,拿到了100,还没来的及去减1, 完了,cpu被第二只线程抢了过去,一看num还是100,然后执行了100-1
*  num就这样变为了99,这时第一个cpu终于抢到了cpu的使用权,去吧num减1,这一减不要紧,num也变成了99,这样,100这支票被卖出去了两
*  此,那这问题就出现了,票卖重了,这是谁的锅? 或者当卖到最后一张票时,还很容易造成超卖现象
* */

import javax.sql.rowset.spi.SyncFactory;

public class Demo26Class implements Runnable {
    private int num1;
    Object obj = new Object();

    Demo26Class(){

    }

    Demo26Class(int num){
        this.num1 = num;
    }

    @Override
    public void run() {

        while (true) {
            // obj 是一个对象
            synchronized(obj){
                if (num1 > 0){
                    // 添加延迟时间,可以更好的看出资源竞争
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "售出车票第" + num1-- + "张");
                }
            }
        }
    }
}

执行结果:

售票车窗1售出车票第20张
售票车窗1售出车票第19张
售票车窗1售出车票第18张
售票车窗1售出车票第17张
售票车窗1售出车票第16张
售票车窗1售出车票第15张
售票车窗1售出车票第14张
售票车窗1售出车票第13张
售票车窗1售出车票第12张
售票车窗1售出车票第11张
售票车窗1售出车票第10张
售票车窗1售出车票第9张
售票车窗1售出车票第8张
售票车窗1售出车票第7张
售票车窗1售出车票第6张
售票车窗1售出车票第5张
售票车窗1售出车票第4张
售票车窗3售出车票第3张
售票车窗2售出车票第2张
售票车窗2售出车票第1
  1. 解决方案二:同步函数 synchronized, 这里以静态方法演示
/* 当多个线程访问同一个变量时,这个变量被称为共享变量,而当多个线程操作同一个变量时,就会出现资源竞争的问题,
* 举例, 有个变量num为100, 每个线程对其的操作就是减去1(想想一下卖票系统,100个票,有三个售票员[三个线程]),有一个
*  在cpu哪里累死累活抢到了使用权,拿到了100,还没来的及去减1, 完了,cpu被第二只线程抢了过去,一看num还是100,然后执行了100-1
*  num就这样变为了99,这时第一个cpu终于抢到了cpu的使用权,去吧num减1,这一减不要紧,num也变成了99,这样,100这支票被卖出去了两
*  此,那这问题就出现了,票卖重了,这是谁的锅? 或者当卖到最后一张票时,还很容易造成超卖现象
* */

import javax.sql.rowset.spi.SyncFactory;

public class Demo26Class implements Runnable {
    private static int num1 = 20;

    @Override
    public void run() {
        demoDef1();

    }

    public static synchronized void demoDef1(){
        while (true) {
            if (num1 > 0){
                // 添加延迟时间,可以更好的看出资源竞争
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售出车票第" + num1-- + "张");
            }

        }
    }
}

// 调用线程共享, 注意,资源竞争不是说创建很多对象,而是一个对象中的多个线程对资源的竞争,比如下面代码中objClass1的对象
// 而不是在new一个对象objClass2,然后再start

public class Demo27Class {

    public static void main(String[] args) {

        Demo26Class objClass1 = new Demo26Class();
        new Thread(objClass1, "售票车窗1").start();
        new Thread(objClass1, "售票车窗2").start();
        new Thread(objClass1, "售票车窗3").start();


    }
}

售票车窗1售出车票第20张
售票车窗1售出车票第19张
售票车窗1售出车票第18张
售票车窗1售出车票第17张
售票车窗1售出车票第16张
售票车窗1售出车票第15张
售票车窗1售出车票第14张
售票车窗1售出车票第13张
售票车窗1售出车票第12张
售票车窗1售出车票第11张
售票车窗1售出车票第10张
售票车窗1售出车票第9张
售票车窗1售出车票第8张
售票车窗1售出车票第7张
售票车窗1售出车票第6张
售票车窗1售出车票第5张
售票车窗1售出车票第4张
售票车窗1售出车票第3张
售票车窗1售出车票第2张
售票车窗1售出车票第1
  1. 解决方案三:建立锁(推荐使用)
/* 当多个线程访问同一个变量时,这个变量被称为共享变量,而当多个线程操作同一个变量时,就会出现资源竞争的问题,
* 举例, 有个变量num为100, 每个线程对其的操作就是减去1(想想一下卖票系统,100个票,有三个售票员[三个线程]),有一个
*  在cpu哪里累死累活抢到了使用权,拿到了100,还没来的及去减1, 完了,cpu被第二只线程抢了过去,一看num还是100,然后执行了100-1
*  num就这样变为了99,这时第一个cpu终于抢到了cpu的使用权,去吧num减1,这一减不要紧,num也变成了99,这样,100这支票被卖出去了两
*  此,那这问题就出现了,票卖重了,这是谁的锅? 或者当卖到最后一张票时,还很容易造成超卖现象
* */

import javax.sql.rowset.spi.SyncFactory;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Demo26Class implements Runnable {
    private static int num1 = 20;
    // 创建一把锁
    Lock lock1 = new ReentrantLock();

    @Override
    public void run() {

        while (true) {
            // 上锁
            lock1.lock();
            if (num1 > 0){
                // 添加延迟时间,可以更好的看出资源竞争
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "售出车票第" + num1-- + "张");
            }
            // 解锁
            lock1.unlock();
        }
    }

}

// 调用线程共享, 注意,资源竞争不是说创建很多对象,而是一个对象中的多个线程对资源的竞争,比如下面代码中objClass1的对象
// 而不是在new一个对象objClass2,然后再start

public class Demo27Class {

    public static void main(String[] args) {

        Demo26Class objClass1 = new Demo26Class();
        new Thread(objClass1, "售票车窗1").start();
        new Thread(objClass1, "售票车窗2").start();
        new Thread(objClass1, "售票车窗3").start();


    }
}

执行结果:

售票车窗1售出车票第20张
售票车窗1售出车票第19张
售票车窗1售出车票第18张
售票车窗1售出车票第17张
售票车窗1售出车票第16张
售票车窗1售出车票第15张
售票车窗1售出车票第14张
售票车窗1售出车票第13张
售票车窗1售出车票第12张
售票车窗1售出车票第11张
售票车窗1售出车票第10张
售票车窗1售出车票第9张
售票车窗1售出车票第8张
售票车窗1售出车票第7张
售票车窗1售出车票第6张
售票车窗1售出车票第5张
售票车窗1售出车票第4张
售票车窗1售出车票第3张
售票车窗1售出车票第2张
售票车窗1售出车票第1
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值