返回顶部
首页 > 资讯 > 精选 >什么是Java并发编程
  • 199
分享到

什么是Java并发编程

2023-06-15 13:06:10 199人浏览 独家记忆
摘要

本篇内容主要讲解“什么是Java并发编程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是java并发编程”吧!Java多线程概述在Java中使用多线程是提高程序并发响应能力的重要手段,但同时

本篇内容主要讲解“什么是Java并发编程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是java并发编程”吧!

Java多线程概述

在Java中使用多线程是提高程序并发响应能力的重要手段,但同时它也是一把双刃剑;如果使用不当也很容易导致程序出错,并且还很难直观地找到问题。这是因为:1)、线程运行本身是由操作系统调度,具有一定的随机性;2)、Java共享内存模型在多线程环境下很容易产生线程安全问题;3)、不合理的封装依赖,极容易导致发布对象的不经意逸出。

所以,要用好多线程这把剑,就需要对Java内存模型、线程安全问题有较深的认识。但由于Java丰富的生态,在实际研发工作中,需要我们自己进行并发处理的场景大都被各类框架或组件给屏蔽了。这也是造成很多Java开发人员对并发编程意识淡薄的主要原因。

首先从Java内存模型的角度理解下使用多线程编程最核心的问题,具体如下图所示:

什么是Java并发编程

如上图所示,在Java内存模型中,对于用户程序来说用得最频繁的就是堆内存和栈内存,其中堆内存主要存放对象及数组,例如由new()产生的实例。而栈内存则主要是存储运行方法时所需的局部变量、操作数及方法出口等信息。

其中堆内存是线程共享的,一个类被实例化后生成的对象、及对象中定义的成员变量可以被多个线程共享访问,这种共享主要体现在多个线程同时执行、同一个对象实例的某个方法时,会将该方法中操作的对象成员变量分别以多个副本的方式拷贝到方法栈中进行操作,而不是直接修改堆内存中对象的成员变量值;线程操作完成后,会再次将修改后的变量值同步至堆内存中的主内存地址,并实现对其他线程的可见。

这个过程虽然看似行云流水,但在JVM中却至少需要6个原子步骤才能完成,具体如下图所示:

什么是Java并发编程

如上图所示,在不考虑对共享变量进行加的情况下,堆内存中一个对象的成员变量被线程修改大概需要以下6个步骤:

read(读取):从堆内存中的读取要操作的变量;

load(载入):将读取的变量拷贝到线程栈内存;

use(使用):将栈内存中的变量值传递给执行引擎;

assign(赋值):将从执行引擎得到的结果赋值给栈内存中变量;

store(存储):将变更后的栈内存中的变量值传递到主内存;

write(写入):变更主内存中的变量值,此时新值对所有线程可见;

由此可见,每个线程都可以按这几个步骤并行操作同一个共享变量。可想而知,如果没有任何同步措施,那么在多线程环境下,该共享变量的值将变得飘忽不定,很难得到最终正确的结果。而这就是所谓的线程安全问题,也是我们在使用多线程编程时,最需要关注的问题!

线程池的使用

在实际场景中,多线程的使用并不是单打独斗,线程作为宝贵的系统资源,其创建和销毁都需要耗费一定的系统资源;而无限制的创建线程资源,也会导致系统资源的耗尽。所以,为了重复使用线程资源、限制线程的创建行为,一般都会通过线程池来实现。以Java  WEB服务中使用最广的Tomcat服务器举例,为了并行处理网络请求就使用了线程池,源码示例如下:

public boolean processSocket(SocketWrapperBase<S> socketWrapper,         SocketEvent event, boolean dispatch) {     try {         if (socketWrapper == null) {             return false;         }         SocketProcessorBase<S> sc = null;         if (processorCache != null) {             sc = processorCache.pop();         }         if (sc == null) {             sc = createSocketProcessor(socketWrapper, event);         } else {             sc.reset(socketWrapper, event);         }         //这里通过线程池对线程执行进行管理         Executor executor = getExecutor();         if (dispatch && executor != null) {             executor.execute(sc);         } else {             sc.run();         }     } catch (RejectedExecutionException ree) {         getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);         return false;     } catch (Throwable t) {         ExceptionUtils.handleThrowable(t);         // This means we Got an OOM or similar creating a thread, or that         // the pool and its queue are full         getLog().error(sm.getString("endpoint.process.fail"), t);         return false;     }     return true; }

上述代码为Tomcat源码使用线程池并发处理网络请求的示例,这里以Tomcat为例,主要是因为基于Spring Bootspring  mvc开发的Web服务大都运行在Tomcat容器,而对于线程、线程池使用的复杂度都被屏蔽在中间件和框架中了,所以很多同学虽然写了不少Java代码,但在业务研发中额外使用线程的场景可能并不多,举这个例子的目的就是为了提升下并发编程的意识!

在Java中使用线程池的主要方式是Executor框架,该框架作为JUC并发包的一部分,为Java程序提供了一个灵活的线程池实现。其逻辑层次如下图所示:

什么是Java并发编程

如图所示,使用Executor框架,既可以通过直接自定义配置、扩展ThreadPoolExecutor来创建一个线程池,也可以通过Executors类直接调用“newSingleThreadExecutor()、newFixedThreadPool()、newCachedThreadPool()”这三个方法来创建具有一定功能特征的线程池。

除此之外,也可以通过自定义配置、扩展ScheduledThreadPoolExecutor来创建一个具有周期性、定时功能的线程池,例如线程10s后运行、线程每分钟运行一次等。同样,与ThreadPoolExecutor一样,如果不想自定义配置,也可以通过Executors类直接调用“newScheduledThreadPool()、newSingleThreadScheduledExecutor()”这两个方法来分别创建具备自动线程规模扩展能力和线程池中只允许有单个线程的特定线程池。

而ForkJoinPool是jdk1.8以后新增的一种线程池实现类型,类似于Fork-Join框架所支持的功能。这是一种可以将一个大任务拆分成多个任务队列,并具体分配给不同线程处理的机制,而关键的特性在于,通过窃取算法,某个线程在执行完本队列任务后,可以窃取其他队列的任务进行执行,从而最大限度提高线程的利用效率。

在实际应用中,虽然可以通过Executors方便的创建单个线程、固定线程或具备自动收缩能力的线程池,但一般还是建议直接通过ThreadPoolExecutor或ScheduledThreadPoolExecutor自定义配置,这主要是因为Executors默认创建的线程池,很多采用的是无界队列,例如LinkedBlockingQueue,这样线程就可以被无限制的添加都线程池的任务执行队列,如果请求量过大容易造成OOM。

接下来以一个实际的例子来演示通过ThreadPoolExecutor如何自定义配置一个业务线程池,具体如下:

1)、配置一个线程池类

public final class SingleBlockPoolExecutor {           private final ThreadPoolExecutor pool = new ThreadPoolExecutor(30, 100, 5, TimeUnit.MINUTES,             new ArrayBlockingQueue<Runnable>(100), new BlockThreadFactory(), new BlockRejectedExecutionHandler());      public ThreadPoolExecutor getPool() {         return pool;     }      private SingleBlockPoolExecutor() {     }           public static class BlockThreadFactory implements ThreadFactory {          private AtomicInteger count = new AtomicInteger(0);          @Override         public Thread newThread(Runnable r) {             Thread t = new Thread(r);             String threadName = SingleBlockPoolExecutor.class.getSimpleName() + "-" + count.addAndGet(1);             t.setName(threadName);             return t;         }     }           public static class BlockRejectedExecutionHandler implements RejectedExecutionHandler {          @Override         public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {             try {                 //被拒线程再次返回阻塞队列进行等待处理                 executor.getQueue().put(r);             } catch (InterruptedException e) {                 Thread.currentThread().interrupt();             }         }     }           private static class Holder {          private static SingleBlockPoolExecutor instance = new SingleBlockPoolExecutor();     }           public static SingleBlockPoolExecutor getInstance() {         return Holder.instance;     }           public void destroy() {         if (pool != null) {             //线程池销毁             pool.shutdownNow();         }     } }

如上述代码所示,通过单例模式配置了一个线程池。在对ThreadPoolExecutor的配置中,需要设置“核心线程数、最大线程数、存活时间设置、采用的队列类型、线程工厂类、线程池拒绝处理类”,这几个核心参数。

2)、定义系统全局线程池管理类

public class AsyncManager {           public static final ExecutorService service = SingleBlockPoolExecutor.getInstance().getPool();  }

在应用中,除了框架定义的线程池外,如果自定义线程池,为了方便统一管理和使用,可以建立一个全局管理类,如上所示,该类通过静态变量的方式初始化了前面我们所定义的线程池。

3)、业务中使用

@Service @Slf4j public class OrderServiceImpl implements OrderService {      @Override     public CreateOrderBO createOrder(CreateOrderDTO createOrderDTO) {         //1、同步处理核心业务逻辑         log.info("同步处理业务逻辑");         //2、通过线程池提交,异步处理非核心逻辑,例如日志埋点         AsyncManager.service.execute(() -> {             System.out.println("线程->" + Thread.currentThread().getName() + ",正在执行异步日志处理任务");         });         return CreateOrderBO.builder().result(true).build();     } }

如上代码所示,业务中需要通过线程池异步处理时,可以通过线程池管理类获取对应的线程池,并向其提交执行线程任务。

FutureTask实现异步结果返回

在使用Thread或Runnable实现的线程处理中,一般是不能返回线程处理结果的。但如果希望在调用线程异步处理完成后,能够获得线程异步处理的结果,那么就可以通过FutureTask框架实现。示例代码如下:

@Service @Slf4j public class OrderServiceImpl implements OrderService {      @Override     public CreateOrderBO createOrder(CreateOrderDTO createOrderDTO) {         //Future异步处理返回执行结果         //定义接收线程执行结果的FutureTask对象         List<Future<Integer>> results = Collections.synchronizedList(new ArrayList<>());         //实现Callable接口定义线程执行逻辑         results.add(AsyncManager.service.submit(new Callable<Integer>() {             @Override             public Integer call() throws Exception {                 int a = 1, b = 2;                 System.out.println("Callable接口执行中");                 return a + b;             }         }));         //输出线程返回结果         for (Future<Integer> future : results) {             try {                 //这里获取结果,等待时间设置200毫秒                 System.out.println("a+b=" + future.get(200, TimeUnit.MILLISECONDS));             } catch (InterruptedException e) {                 e.printStackTrace();             } catch (ExecutionException e) {                 e.printStackTrace();             } catch (TimeoutException e) {                 e.printStackTrace();             }         }         //判断线程是否执行完毕,完毕则获取执行结果         return CreateOrderBO.builder().result(true).build();     } }

如上述代码,如果希望线程返回执行结果,那么可以通过实现Callable接口定义线程类,并通过FutureTask接收线程处理结果。不过在实际使用时,需要注意线程暂时未执行完成情况下的业务处理逻辑。

CountDownLatch实现线程并行同步

在并发编程中,一个复杂的业务逻辑可以通过多个线程并发执行来提高速度;但如果需要同步等待这些线程执行完后才能进行后续的逻辑,那么就可以通过CountDownLatch来实现对多个线程执行的同步汇聚。其逻辑示意图如下:

什么是Java并发编程

从原理上看CountDownLatch实际上是在其内部创建并维护了一个volatile类型的整数计数器,当调用countDown()方法时,会尝试将整数计数器-1,当调用wait()方法时,当前线程就会判断整数计数器是否为0,如果为0,则继续往下执行,如果不为0,则使当前线程进入阻塞状态,直到某个线程将计数器设置为0,才会唤醒在await()方法中等待的线程继续执行。

常见的代码使用示例如下:

1)、创建执行具体业务逻辑的线程处理类

public class DataDealTask implements Runnable {      private List<Integer> list;     private CountDownLatch latch;      public DataDealTask(List<Integer> list, CountDownLatch latch) {         this.list = list;         this.latch = latch;     }      @Override     public void run() {         try {             System.out.println("线程->" + Thread.currentThread().getName() + ",处理" + list.size());         } finally {             //处理完计数器递减             latch.countDown();         }     } }

该线程处理类,在实例化时接收除了待处理数据参数外,还会接收CountDownLatch对象,在执行完线程逻辑,注意,无论成功或失败,都需要调用countDown()方法。

2)、具体的使用方法

@Service @Slf4j public class OrderServiceImpl implements OrderService {      @Override     public CreateOrderBO createOrder(CreateOrderDTO createOrderDTO) {         //CountDownLatch的使用示例         //模拟待处理数据生成         Integer[] array = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 101, 102};         List<Integer> list = new ArrayList<>();         Arrays.asList(array).stream().map(o -> list.add(o)).collect(Collectors.toList());         //对数据进行分组处理(5条记录为1组)         Map<String, List<?>> entityMap = this.groupListByAvg(list, 6);         //根据数据分组数量,确定同步计数器的值         CountDownLatch latch = new CountDownLatch(entityMap.size());         Iterator<Entry<String, List<?>>> it = entityMap.entrySet().iterator();         try {             //将分组数据分批提交给不同线程处理             while (it.hasNext()) {                 DataDealTask dataDealTask = new DataDealTask((List<Integer>) it.next().getValue(), latch);                 AsyncManager.service.submit(dataDealTask);             }             //等待分批处理线程处理完成             latch.await();         } catch (InterruptedException e) {             e.printStackTrace();         }         return CreateOrderBO.builder().result(true).build();     } }

如上所示代码,在业务逻辑中如果处理数据量多,则可以通过分组的方式并行处理,而等待所有线程处理完成后,再同步返回调用方。这种场景就可以通过CountDownLatch来实现同步!

CycliBarrier栅栏实现线程阶段性同步

CountDownLatch的功能主要是实现线程的一次性同步。而在实际的业务场景中也可能存在这样的情况,执行一个阶段性的任务,例如”阶段1->阶段2->阶段3->阶段4->阶段5"。那么在并发处理这个阶段性任务时,就要在每个阶段设置栅栏,只有当所有线程执行到某个阶段点之后,才能继续推进下一个阶段任务的执行,其逻辑如图所示:

什么是Java并发编程

针对上述场景,就可以通过CycliBarrier来实现。而从实现上看,CyclicBarrier使用了基于ReentrantLock的互斥锁实现;在CyclicBarrier的内部有一个计数器  count,当count不为0时,每个线程在到达同步点会先调用await方法将自己阻塞,并将计数器会减1,直到计数器减为0的时候,所有因调用await方法而被阻塞的线程就会被唤醒继续执行。并进入下一轮阻塞,此时在new  CyclicBarrier(parties) 时设置的parties值,会被赋值给 count 从而实现复用。

例如,计算某个部门的员工工资,要求在所有员工工资都计算完之后才能进行下一步整合操作。其代码示例如下:

@Slf4j @Service public class SalaryStatisticServiceImpl implements SalaryStatisticService {           public static Map<String, List<EmployeeSalaryInfo>> employeeMap = Collections.synchronizedMap(new HashMap<>());      static {         EmployeeSalaryInfo employeeA = new EmployeeSalaryInfo();         employeeA.setEmployeeNo("100");         employeeA.setBaseSalaryAmount(10000);         employeeA.setSubsidyAmount(3000);         EmployeeSalaryInfo employeeB = new EmployeeSalaryInfo();         employeeB.setEmployeeNo("101");         employeeB.setBaseSalaryAmount(30000);         employeeB.setSubsidyAmount(3000);         List<EmployeeSalaryInfo> list = new ArrayList<>();         list.add(employeeA);         list.add(employeeB);         employeeMap.put("10", list);     }      @Override      public StatisticReportBO statisticReport(StatisticReportDTO statisticReportDTO) {         //查询部门下所有员工信息(模拟)         List<EmployeeSalaryInfo> employeeSalaryInfos = employeeMap.get(statisticReportDTO.getDepartmentNo());         if (employeeSalaryInfos == null) {             log.info("部门员工信息不存在");             return StatisticReportBO.builder().build();         }         //定义统计总工资的安全变量         AtomicInteger totalSalary = new AtomicInteger();         //开启栅栏(在各线程触发之后触发)         CyclicBarrier cyclicBarrier = new CyclicBarrier(employeeSalaryInfos.size(), new Runnable() {             //执行顺序-B1(随机)             //该线程不会阻塞主线程             @Override             public void run() {                 log.info("汇总已分别计算出的两个员工的工资->" + totalSalary.get() + ",执行顺序->B");             }         });         //执行顺序-A         for (EmployeeSalaryInfo e : employeeSalaryInfos) {             AsyncManager.service.submit(new Callable<Integer>() {                 @Override                 public Integer call() {                     int totalAmount = e.getSubsidyAmount() + e.getBaseSalaryAmount();                     log.info("计算出员工{}", e.getEmployeeNo() + "的工资->" + totalAmount + ",执行顺序->A");                     //汇总总工资                     totalSalary.addAndGet(totalAmount);                     try {                         //等待其他线程同步                         cyclicBarrier.await();                     } catch (InterruptedException e) {                         e.printStackTrace();                     } catch (BrokenBarrierException e) {                         e.printStackTrace();                     }                     return totalAmount;                 }             });          }         //执行顺序-A/B(之前或之后随机,totalSalary值不能保证一定会得到,所以CyclicBarrier更适合无返回的可重复并行计算)         //封装响应参数         StatisticReportBO statisticReportBO = StatisticReportBO.builder().employeeCount(employeeSalaryInfos.size())                 .departmentNo(statisticReportDTO.getDepartmentNo())                 .salaryTotalAmount(totalSalary.get()).build();         log.info("封装接口响应参数,执行顺序->A/B");         return statisticReportBO;     }      @Data     public static class EmployeeSalaryInfo {                   private String employeeNo;                  private Integer baseSalaryAmount;                  private Integer subsidyAmount;     } }

上述代码的执行结果如下:

[kPoolExecutor-1] c.w.c.s.impl.SalaryStatisticServiceImpl  : 计算出员工100的工资->13000,执行顺序- [kPoolExecutor-2] c.w.c.s.impl.SalaryStatisticServiceImpl  : 计算出员工101的工资->33000,执行顺序- [kPoolExecutor-2] c.w.c.s.impl.SalaryStatisticServiceImpl  : 汇总已分别计算出的两个员工的工资->46000, [NIO-8080-exec-2] c.w.c.s.impl.SalaryStatisticServiceImpl  : 封装接口响应参数,执行顺序->A/B

从上述结果可以看出,受CycliBarrier控制的线程会等待其他线程执行完成后同步向后执行,并且CycliBarrier并不会阻塞主线程,所以最后响应参数封装代码可能在CycliBarrier汇总线程之前执行,也可能在其之后执行,使用时需要注意!

Semaphore(信号量)限制访问资源的线程数

Semaphore可以实现对某个共享资源访问线程数的限制,实现限流功能。以停车场线程为例,代码如下:

@Service @Slf4j public class ParkServiceImpl implements ParkService {           private static Semaphore semaphore = new Semaphore(2);      @Override     public AccessparkBO accessPark(AccessParkDTO accessParkDTO) {         AsyncManager.service.execute(() -> {             if (semaphore.availablePermits() == 0) {                 log.info(Thread.currentThread().getName() + ",车牌号->" + accessParkDTO.getCarNo() + ",车位不足请耐心等待");             } else {                 try {                     //获取令牌尝试进入停车场                     semaphore.acquire();                     log.info(Thread.currentThread().getName() + ",车牌号->" + accessParkDTO.getCarNo() + ",成功进入停车场");                     //模拟车辆在停车场停留的时间(30秒)                     Thread.sleep(30000);                     //释放令牌,腾出停车场车位                     semaphore.release();                     log.info(Thread.currentThread().getName() + ",车牌号->" + accessParkDTO.getCarNo() + ",驶出停车场");                 } catch (InterruptedException e) {                     e.printStackTrace();                 }             }          });         //封装返回信息         return AccessParkBO.builder().carNo(accessParkDTO.getCarNo())                 .currentPositionCount(semaphore.availablePermits())                 .isPermitAccess(semaphore.availablePermits() > 0 ? true : false).build();     } }

上述代码模拟停车场有2车位,并且每辆车进入车场后会停留30秒,然后并行模拟3次停车请求,具体执行效果如下:

[kPoolExecutor-1] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-1,车牌号->10,成功进入停车场  顺序->A [kPoolExecutor-2] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-2,车牌号->20,成功进入停车场  顺序->A [kPoolExecutor-3] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-3,车牌号->30,车位不足请耐心等待00,执行顺序->B [kPoolExecutor-1] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-1,车牌号->10,驶出停车场     [kPoolExecutor-2] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-2,车牌号->20,驶出停车场     [kPoolExecutor-4] c.w.c.service.impl.ParkServiceImpl       : SingleBlockPoolExecutor-4,车牌号->30,成功进入停车场

可以看到由于通过Semaphore限制了可允许进入的线程数是2个,所以第三次请求会被拒绝,直到前两次请求通过.release()方法释放证书后第4次请求才会被允许进入!

后记

本文从应用层面总结了,JVM基本的内存模型以及线程对共享内存操作的原子方式,并着重介绍了线程池、FutrueTask、CountDownLatch、CycliBarrier以及Semaphore这几种在Java并发编程中经常使用的JUC工具类。

到此,相信大家对“什么是Java并发编程”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: 什么是Java并发编程

本文链接: https://lsjlt.com/news/280298.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 什么是Java并发编程
    本篇内容主要讲解“什么是Java并发编程”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“什么是Java并发编程”吧!Java多线程概述在Java中使用多线程是提高程序并发响应能力的重要手段,但同时...
    99+
    2023-06-15
  • Java并发编程的三要素是什么
    本篇内容介绍了“Java并发编程的三要素是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1 原子性 1.1 原子性的定义原子性指的是一个...
    99+
    2023-06-30
  • Java并发编程学习方法是什么
    这篇文章主要讲解了“Java并发编程学习方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java并发编程学习方法是什么”吧!一:并发基础和多线程首先需要学习的就是并发的基础知识,什么...
    99+
    2023-06-02
  • Java并发编程中的内存模型是什么
    这篇文章主要介绍“Java并发编程中的内存模型是什么”,在日常操作中,相信很多人在Java并发编程中的内存模型是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java并发编程中的内存模型是什么”的疑惑有所...
    99+
    2023-06-25
  • java中为什么需要并发编程
    这篇文章主要介绍了java中为什么需要并发编程,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。为什么需要并发编程?  并发编程帮助我们把做什么和什么时候做分开,可以明显的改进应...
    99+
    2023-06-27
  • Java并发编程中并发机制的底层实现原理是什么
    今天就跟大家聊聊有关Java并发编程中并发机制的底层实现原理是什么,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Java中的并发机制依赖于JVM的实现和CPU指令,接下来我们深入底层...
    99+
    2023-06-19
  • java什么是并发?
    并发与并行并发:是指在某个时间段内,多任务交替的执行任务。当有多个线程在操作时,把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行。 在一个时间段的线程代码运行时,其它线程处于挂起状。并行:是指同一时刻同时处理多任务的能力。当...
    99+
    2020-11-24
    java教程 java 并发
  • java并发是什么?
    并发是什么?并发:是指在某个时间段内,多任务交替的执行任务。当有多个线程在操作时,把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行。 在一个时间段的线程代码运行时,其它线程处于挂起状。在并发环境下,程序的封闭性被打破,出现以...
    99+
    2015-07-19
    java 并发
  • 并发编程LongAdder的原理是什么
    本篇内容介绍了“并发编程LongAdder的原理是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!目录一、前言二、LongAdder类的使...
    99+
    2023-06-20
  • python基础之什么是并发编程
    本篇内容介绍了“python基础之什么是并发编程”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、协程定义和作用协程(coroutine),...
    99+
    2023-06-25
  • java中什么是并发?
    java中什么是并发?并发:是指在某个时间段内,多任务交替的执行任务。当有多个线程在操作时,把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行。 在一个时间段的线程代码运行时,其它线程处于挂起状。简单来说,就是cpu在同一时刻...
    99+
    2016-05-09
    java教程 java 并发
  • GO并发编程使用方法是什么
    这篇文章主要介绍了GO并发编程使用方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇GO并发编程使用方法是什么文章都会有所收获,下面我们一起来看看吧。啥是并发编程呢指在一台处理器上同时处理多个任务此处说的...
    99+
    2023-07-05
  • java并发编程详解
    一.synchronized的缺陷synchronized是java中的一个关键字,也就是说是Java语言内置的特性。那么为什么会出现Lock呢?如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其...
    99+
    2016-07-17
    java
  • Java并发编程之threadLocal
    目录1、ThreadLocal介绍2、ThreadLocal使用实例3、ThreadLocal实现原理1、ThreadLocal介绍 多个线程访问同一个共享变量时特别容易出现并发问题...
    99+
    2024-04-02
  • Python 并发编程的关键挑战是什么?
    Python 是一种高级编程语言,它在数据处理、科学计算、人工智能等领域广泛应用。Python 语言的一个优点就是它天生就支持并发编程。Python 中有多种方式来实现并发编程,包括多线程、多进程和协程等。但是,Python 并发编程也存...
    99+
    2023-09-26
    并发 spring 自然语言处理
  • Java 线程池:并发编程中的并发魔杖
    线程池是一个用于管理线程的机制,它允许应用程序在需要时创建和销毁线程,而不是为每个任务创建单独的线程。这可以显著提高应用程序的性能和可伸缩性。 线程池的好处 使用线程池的主要好处包括: 提高性能:线程池避免了创建和销毁线程的开销,提高了...
    99+
    2024-03-13
    线程池
  • C#并发编程和线程同步机制是什么
    C#并发编程是指在C#程序中同时执行多个任务的能力。线程同步机制是确保多个线程能够安全地访问共享资源的方法。在C#中,线程同步可以通...
    99+
    2024-03-07
    C#
  • Java并发编程之线程怎么创建
    本篇内容主要讲解“Java并发编程之线程怎么创建”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java并发编程之线程怎么创建”吧!1.线程与进程进程是代码在数据集合上的一次运行活动,是系统进行资...
    99+
    2023-06-30
  • Java并发编程中volatile关键字有什么作用
    本篇内容主要讲解“Java并发编程中volatile关键字有什么作用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java并发编程中volatile关键字有什么作用”吧!1、volatile是什...
    99+
    2023-06-25
  • Git和Django的并发编程:Java是关键吗?
    Git和Django是当今最常用的工具之一,它们各自拥有独特的功能和用途。Git是一个分布式版本控制系统,而Django是一个基于Python的Web框架。在日常开发中,我们经常需要使用Git和Django来开发和管理项目。但是,当我们需要...
    99+
    2023-10-16
    git django 并发
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作