【思考】Java中多个线程操作同一个实例中的同一个方法会有什么影响?

本人是刚入门的小白,最近在重新学习spring;在理解spring中默认单例bean时引起了我对于标题的思考。

于是上网搜索,众多的回答给了我一点启发,但是都没有醍醐灌顶的感觉,当我尝试从硬件角度思考时似乎答案变得清晰了起来...

在spring中,无论是配置xml还是添加注解  对象的创建默认都是单例的,我们暂且不谈单例的好处是什么,单单来分析一下 如果多个线程同时调用这个实例的同一个方法会发生什么:

 这个有两种情况:

 1、方法只进行简单的数据处理,涉及的变量都是局部变量;

 2、方法会操作全局的变量或者是数据库中的数据;

先来分析一下第一种情况:



// 该方法只对输入的数据进行简单的处理(方法为spring托管的单例对象的方法)

public String sout(String str){

    return str+"字符串进行了变更";

}


/**
 * 假设现在有两个线程同时调用 单例对象(或者同一个实例)的sout方法
 *
 */

// 线程1
singleton.sout("张三");

// 线程2
singleton.sout("李四");

现在思考的地方来了,这个单例对象被创建在堆内存中,现在两个线程同时调用他的sout方法会不会导致一个对象需要处理两个线程的请求造成结果混乱呢?这也是我最初的疑惑,最初类比的是一个机器如果同时给两个人的两个苹果削皮  完成后会不会分不清哪个苹果属于哪个人?

其实这个类比就是错误的,计算机中处理的只是数据,堆内存中的对象其实就是等待被cpu处理的数据块;单个sout方法就是缺少输入的数据块,当方法被调用时cpu读取sout和输入数据整合为完整的数据块进入cpu执行;如果两个线程是被两个cpu处理 那么两个cpu就会分别整合出两个完整的数据块进入cpu执行;

到此问题就清晰了,存于堆内存中的对象只是一种数据形式(并不是之前类比的处理数据的机器)对于cpu来说只是一条可处理的数据而已,无论是单个对象还是多个对象cpu只将其读取进来和输入数据进行整合(cpu读取对象不会影响到对象,这个对象任然可以被其他cpu读取)然后处理;对象和输入数据都只是数据,cpu才是机器;创建的对象只是一种常驻内存的能够处理数据的数据逻辑(也还是数据)

========================================================

所以说对于第一种情况并不会有什么影响,但是对于第二张情况影响就比较大了

// 假设下面的方法是对内存中 A 的大小进行修改(全局变量可被所有线程访问)

public void updateNum(){

       A -= 10;

}



/**
 * 现在有两个线程同时都调用了这个方法,会有什么情况发生
 */


// 线程1
thread1.updateNum();

// 线程2
thread2.updateNum();

现在两个线程同时调用这个方法;下面简单分析一下:

线程1调用方法,cpu1读取到A的值为100  此时进行处理  A的值修改为90

于此同时线程2也调用方法,cpu2同时读取A的值也是100  cpu2进行处理  A的值修改为90

现在两个线程都处理完之后 A的值为90;可见这种多线程同时修改全局变量会出现问题。

多线程操作共享数据涉及到同步和锁的问题在此不过多赘述,本文旨在讨论多线程操作单例bean时有什么影响,分析完可见操作线程自身的局部变量时并不会产生什么影响;对象只是存在于内存中的赋予处理数据逻辑的数据,cpu才是真正处理数据的机器;相同的对象无论一个还是两个都是提供给cpu读取的不存在两个线程竞争对象的问题。

ps:以上内容仅仅是一个刚入门小白的思考和自身理解,如有不对的地方 感谢指正!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java线程,如果一个线程出现异常,需要让所有线程都回滚的话,可以使用以下的实例代码: ```java import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class MultiThreadRollbackDemo { private static final String DB_URL = "jdbc:mysql://localhost:3306/testdb?useSSL=false"; private static final String USER = "root"; private static final String PASSWORD = "password"; private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>(); public static void main(String[] args) throws InterruptedException { // 创建 3 个线程 Thread t1 = new Thread(new Worker()); Thread t2 = new Thread(new Worker()); Thread t3 = new Thread(new Worker()); // 启动线程 t1.start(); t2.start(); t3.start(); // 等待所有线程完成 t1.join(); t2.join(); t3.join(); System.out.println("All workers finished successfully."); } static class Worker implements Runnable { @Override public void run() { Connection conn = getConnection(); connectionHolder.set(conn); try { // 开始事务 conn.setAutoCommit(false); // 执行一些操作 // ... // 模拟异常 if (Thread.currentThread().getName().equals("Thread-1")) { throw new RuntimeException("Something went wrong."); } // 提交事务 conn.commit(); } catch (Exception e) { // 出现异常,回滚事务并抛出异常 try { conn.rollback(); } catch (SQLException ex) { ex.printStackTrace(); } throw new RuntimeException(e); } finally { // 关闭连接 try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } // 移除线程变量 connectionHolder.remove(); } } private Connection getConnection() { try { Connection conn = connectionHolder.get(); if (conn == null) { conn = DriverManager.getConnection(DB_URL, USER, PASSWORD); connectionHolder.set(conn); } return conn; } catch (SQLException e) { throw new RuntimeException(e); } } } } ``` 在这个示例,我们创建了三个线程,并且在每个线程都执行了一些操作。如果某个线程抛出异常,其他线程回滚事务。我们使用了 ThreadLocal 来存储每个线程的连接对象,保证了线程安全。在 run() 方法,我们使用 try-catch-finally 块来处理事务回滚和连接关闭的逻辑。如果出现异常,我们回滚事务并抛出运行时异常,以便在主线程检查到异常。最后,我们在主线程等待所有线程完成后打印一条成功的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值