首页 > 编程语言 >结合 操作系统、Java多线程 学习并发编程

结合 操作系统、Java多线程 学习并发编程

时间:2023-03-31 18:22:05浏览次数:42  
标签:count 执行 Java lock 编程 线程 thread1 static 多线程

为什么我们需要考虑并发?不考虑的话会出现什么问题?

并发的多个程序(进程/线程)会对计算机资源进行争夺,如果不加以控制会出现混乱、严重影响程序运行效率,甚至错误

首先是对CPU时间片的争夺

对于多线程编程而言,由于创建线程后,线程的执行顺序是由调度程序控制的,也就是说各个线程的执行顺序并没有一个确定的预期,显然在很多情况下这会影响到我们的编程逻辑,所以我们首先需要一些方法能够实现对线程执行顺序时机的控制

其次,是对共享内存访问(读写)的问题

例如我们使用两个线程访问同一个共享的全局变量对其做大量的自加操作,由于:

  1. 首先自加操作并不是原子的

事实上包含了三条指令

  1. 从内存中读取变量的值
  2. 对变量值加一
  3. 将操作后的变量值写回
  1. 于是,例如thread1在读取count的值比如0后,+1,但是还没写回内存之前,发生了时钟中断thread1让出CPU并将状态保存到 TCB,thread2得到了时间片并开始执行,读取count的值,由于thread1的更改还没有写回内存,于是thread2读到的还是0

那么,最终的结果就是,原本预期是被加了两次结果是2count值,最终得到的却是1
也就是说如果不对并发的内存访问加以控制,那么最终得到的count值和有可能是小于我们的预期的

这其实也是我遇到的一个面试题,面试官希望我举一个例子说明什么时候会出现并发问题

下面是一段简单的示例代码,用来说明上面的两种问题

public class CreateThread {

    static int count = 0;// 一个全局共享变量

    /**
     * 一个线程内部类
     */
    static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        thread1.start();
        thread2.start();
        thread1.join(); // 等待 thread1 执行完成
        thread2.join(); // 等待 thread2 执行完成
        System.out.println(count);
    }
}
讲解一下

首先对于问题1,在程序中的体现在于:
我希望在两个子线程执行结束后,然后再打印最终的count变量,而不是子线程还在执行甚至还没执行就打印了count值,于是我使用了join()方法,它会使得主线程在子线程执行结束后再执行

对于问题2,在程序中的体现在于:
两个子线程在执行过程中分别访问并对count变量做了自加操作,各自10000次,我们预期结果应该是20000(当然我们知道这是不对的),但是结果得到了比如:17691、16976、12030

在上面的代码中,执行for (int i = 0; i < 10000; i++) count++;这段代码的多个线程可能导致竞争状态,即临界区——访问共享变量(资源)的代码片段,一定不能由多个线程同时执行

那么我们如何实现原子操作?

另一种说法是:事务

首先我们可以使用锁机制,锁是一种互斥概念,同一把锁同一时间只能被一个线程持有,而其他线程必须等待
Java语言中对锁的支持全面,大致有以下几种

  1. 我们最熟悉的synvhronized关键字
  2. Lock接口
    ReentrantLock
    main()方法不变,我们将代码改成这样:
    private static final ReentrantLock lock = new ReentrantLock();

    static int count = 0;// 一个全局共享变量

    /**
     * 一个线程内部类
     */
    static class MyThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 10000; i++) {
                lock.lock();
                try {
                    count++;
                } finally {
                    lock.unlock();
                }
            }
        }
    }

测试可打断特性

public class ReentrantTest {
    private static final ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args){
        Thread t1 = new Thread(() -> {
            try {
                /*
                如果没有竞争此方法会获取lock对象锁
                有竞争就进入阻塞队列,但是可以被其他线程用interrupt()方法打断
                 */
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                System.out.println("没有获得锁,返回");
                return;
            }
            try {
                System.out.println("获取到锁");
            } finally {
                lock.unlock();
            }
        }, "testThread");

        lock.lock();

        t1.start();
        System.out.println("打断t1");
        t1.interrupt();

    }
}

标签:count,执行,Java,lock,编程,线程,thread1,static,多线程
From: https://www.cnblogs.com/yaocy/p/17274933.html

相关文章

  • java integer == integer返回 true 还是 false?
    理论:IntegerCache缓存JAVA的Integer有IntegerCache会缓存-128~127之间的对象。如:Integerx=100,会调用Integer的valueOf()方法,这个方法就是返回一个Integer对象,但是在返回前,作了一个判断,判断要赋给对象的值是否在[-128,127]区间中,且IntegerCache(是Integer类的内部类,里面有一......
  • (转)Golang 编程思维和工程实战
    原文:https://zhuanlan.zhihu.com/p/426368274一Golang编程思维首先,我们先来看下最基本的,就是Golang的学习技巧,比如:通读Golang的一些好的文章如 FrequentlyAskedQuestions(FAQ)或者看看 FAQ的中文翻译 ,主要是了解Golang的全貌。Go精华文章列表Go相关博客......
  • 【Java】删除String数组中的所有空值
    1、封装一个方法/****去除String数组中的空值*/privateString[]deleteArrayNull(Stringstring[]){StringstrArr[]=string;//step1:定义一个list列表,并循环赋值ArrayList<String>strList=newArrayList<String>();......
  • React 编程思想 #2
    React编程思想#2接上文,已经实现了一个静态的页面,现在就要给页面加上交互了。寻找State状态是应用需要记录的最小变化,构建状态的最重要的原则是DRY(Don’tRepeatYourself,不要重复自己)。对于一个应用,构建出它的状态的绝对最小表示,并通过这些状态计算其他需要的内容。例如,如......
  • JavaFx 行间距 margin
    自己边尝试边摸索,间距都调不了,气的呀。如有不足,请指正!spacing相当于margin-topmargin-bottomenvBox.setSpacing(20);insets能设置上下左右间距btn1.setOpaqueInsets(newInsets(0,10,0,0));vgap="10"上下间距hgap="10"左右间距 ......
  • 系统化学习前端之JavaScript(ES6)
    前言ES6同样是ECMAScript的拓展,发布于2015年,目前多数浏览器对于部分ES6更新内容不支持,通常需要借助bable编译器编译成ES5或者ECMAScript早期版本语法去执行。ES6的学习推荐阮一峰老师的ES6教程。ES6ES6是ECMAScript最近一次较大版本的更新,更新内容主要是一......
  • 通过MapEdit源程序的学习MAP文件 3, 改编程序,没有编辑功能,只显示MAP
    unitshowmap;interfaceusesWindows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,ExtCtrls,StdCtrls,WIL,ComCtrls;constUNITX=48;UNITY=32;typeTMapInfo=packedrecord//这个好像不需要PACKED......
  • 重学Java设计模式-结构型模式-享元模式
    重学Java设计模式-结构型模式-享元模式内容摘自:https://bugstack.cn/md/develop/design-pattern/2020-06-14-重学Java设计模式《实战享元模式》.html#重学-java-设计模式-实战享元模式「基于redis秒杀-提供活动与库存信息查询场景」享元模式介绍图片来自:https://refactorin......
  • 一文搞懂Java异常处理
    一、什么是异常处理在Java编程中,异常处理是一种机制,用于处理程序运行时可能出现的异常情况。当程序出现异常时,程序会抛出一个异常对象,如果不加以处理,程序就会终止运行。因此,我们需要使用异常处理机制来捕获并处理这些异常,以使程序能够在出现异常时继续运行。在Java中,异常处理主要......
  • Java计算百分比
    代码如下publicstaticvoidmain(String[]args){floatnum=3.14f;inttotal=10;//创建一个数值格式化对象java.text.NumberFormatnumberformat=java.text.NumberFormat.getInstance();//设置精确到小数点后2位......