JUC并发编程(多线程进阶整理)

2023-11-06

JUC并发编程

要想学习JUC就必须了解 java.util concurrent 包的工具类,其中包含:

  • java.util concurrent (并发包)
  • java.util concurrent.atomic (并发原子包)
  • java.util concurrent.locks (并发lock包)

1. volatile关键字——内存可见性

添加volatile关键字后,当多个线程同时操作共享线程时,可以保证内存中的数据可见。相较于synchronized是一种较为轻量级的同步策略。

【注意点:①volatile 不具备 “互斥性” ,即程序中如果有一个线程访问共享数据拿到锁时,另一个线程依然可以访问该共享数据。

②volatile 不能保证变量的 “原子性” 】

解决 volatile 中的不能保证原子性问题,可以用jdk5之后的 java.util concurrent.atomic 包下提供的常用原子变量

1,volatile 保证内存可见性

2,CAS(Compare-And-Swap)算法保证数据的原子性  (乐观锁的实现方式之一)

CAS 算法是硬件对于并发操作共享数据的支持,包含了三个操作数:内存值V、预估值A、更新值B;当且仅当 V == A 时,才会执行 V = B ,否则将不做任何操作,即当多个线程并发的对共享数据进行修改时,有且只有一个会成功,就保证了变量的原子性。

(CAS算法的效率要高于synchronized锁,因为CAS算法在本次线程执行操作不成功后,依然可以立即对数据进行修改操作,不会阻塞,一般情况下是一个自旋操作,即不断的重试

public class TestIcon {
    public static void main(String[] args){
        AtomicDemo atomicDemo = new AtomicDemo();
        for (int x = 0;x < 10; x++){
            new Thread(atomicDemo).start();
        }
    }
}

class AtomicDemo implements Runnable{
    //创建原子变量对象
    private AtomicInteger serialNum = new AtomicInteger();
    public int getSerialNumber(){
        return serialNum.getAndIncrement();//调用原子变量的方法:原子上增加一个当前值
    }
    
    @Override
    public void run() {
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(getI());
    }
}

 2.Lock锁实现线程精准通知唤醒机制(Condition接口)

在多线程基础篇已经详细讲解Lock锁的作用和用法,这里补充详细讲解一下Condition接口。

一个Lock对象中可以创建多个Condition实例(即对象监视器),一个Condition实例本质上绑定到一个锁。通过 newCondition()获取特定Condition实例。方法 await() 使当前线程等或interrupted;方法 signal() 唤醒一个等待的线程,signalAll() 唤醒所有等待的线程。

//Condition实现线程精准通知和唤醒,让线程A,B,C依次轮流执行
public class Demo {
    public static void main(String[] args) {
        Date date = new Date();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) date.mythreadA();
        },"线程A").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) date.mythreadB();
        },"线程B").start();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) date.mythreadC();
        },"线程C").start();
    }
}
class Date{
    private int number = 0;//标记正在执行的线程
    private final Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Condition condition1 = lock.newCondition();
    Condition condition2 = lock.newCondition();

    public void mythreadA(){
        lock.lock();
        try {
            while (number != 0) {//判断执行的线程
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"---A线程正在执行");
            //唤醒指定的线程
            number = 1;
            condition1.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void mythreadB(){
        lock.lock();
        try {
            while (number != 1){
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName()+"---B线程正在执行");
            number = 2;
            condition2.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void mythreadC(){
        lock.lock();
        try {
            while (number != 2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+"---C线程正在执行");
            number = 0;
            condition.signal();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}

注意:在线程并发执行问题中有一个典型的线程虚假唤醒的情况,解决虚假唤醒问题的关键就在于判断线程是否等待或唤醒时应使用while循环判断,而不是if判断。原因是if只判断一次就继续向下执行,while会循环判断条件。】 

3.同步容器类/并发容器类(JUC线程安全的集合)

1)ConcurrentHashMap

我们都知道HashMap是线程不安全的,简单说一下原因;而HashTable是线程安全的,因为其内部用到了synchronized锁,而且是锁整个hash表,所以当执行多线程操作时,效率非常低。jdk5后提供了ConcurrentHashMap同步容器类增加的一个线程安全的哈希表,对于多线程的操作介于hashMap和hashTable之间

jdk7 中采用的是锁分段机制,将整个数据结构分段(默认为16段)进行存储,然后给每一段数据(segment)配一把锁(继承ReentrantLock),当一个线程占用锁访问其中一个段的数据的时候,其他段的数据仍然能被其他线程访问,能够实现真正的并发访问。结构如下图所示:

在jdk8中concurrentHashMap已经优化,其数据结构已经接近对应版本的HashMap。取消了Segment分段锁的数据结构,取而代之的是Node数组+链表+红黑树的结构,从而实现了对每一行数据进行加锁,进一步减少并发冲突的概率【Node类成员变量Node的元素val和指针next都标注volatile,目的是在多线程环境下线程A修改结点的val或者新增节点的时候是对线程B可见的】;JDK8采用CAS(读)+Synchronized(写)保证线程安全;对每个数组元素(Node)加锁;在链表节点数量大于8且当前数组长度大于64时,会将链表转化为红黑树进行存储;JDK8推荐使用mappingCount方法而不是size方法获取当前map表的大小,因为这个方法的返回值是long类型,size方法是返回值类型是int。下图是jdk8存储结构:

//线程安全的map集合
Map<String,String> map = new ConcurrentHashMap<>();
for (int i = 0; i < 10; i++) {
    new Thread(() -> {
         map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
                System.out.println(map);
      },String.valueOf(i)).start();
}

2)CopyOnWriteArrayList/CopyOnWriteArraySet:写入并复制

当期望许多线程访问一个给定的collection时,ConcurrentHashMap 通常优于同步的hashMap,ConcurrentSkinListMap 通常优于同步的treeMap。当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的ArrayList。

【注意:CopyOnWriteArrayList 在添加修改操作多时,效率低,因为每次添加修改时都会进行复制,开销很大;当并发迭代操作多时选择更好。】

/*
 * 并发情况下,ArrayList线程不安全,报错java.util.ConcurrentModificationException并发修改异常
 * 解决方法①:List<String> list = new Vector<>();
 *       ②:List<String> list = Collections.synchronizedList(new ArrayList<>());
 *       ③:List<String> list = new CopyOnWriteArrayList<>();
 */
// List<String> list = new ArrayList<>();
   List<String> list = new CopyOnWriteArrayList<>();
   for (int i = 0; i < 10; i++) {
      new Thread(() -> {
          list.add(UUID.randomUUID().toString().substring(0,5));
          System.out.println(list);
   },String.valueOf(i)).start();
}

3)CountDownLatch:闭锁

允许一个或多个线程,等待其他N个线程(一组线程)完成某个事情之后才能继续执行的同步辅助类,通俗的讲就是在完成某些运算时,只有其他所有线程的运算全部完成,当前运算才继续执行,类似于倒计时器(锁存器)。

一个CountDownLatch为一个计数的CountDownLatch用作一个简单的开/关锁存器,或者门:所有线程调用await()在门口等待,直到被调用countDown()的线程打开。

//构建一个给定的计数,总数是10,必须要执行任务的时候,再使用
CountDownLatch cdl = new CountDownLatch(10);

System.out.println("open door");
    for (int i = 0; i < 10; i++) {
       new Thread(() -> {
           System.out.println(Thread.currentThread().getName()+"Go out");
           //减少锁存器(计数器)的计数,如果计数达到零,释放所有等待的线程
           cdl.countDown();//每执行完一个就递减一个
            },String.valueOf(i)).start();
        }
//等待锁存器(计数器)归零,然后再向下执行
cdl.await();
System.out.println("close door");

4)CyclicBarrier

N个线程相互等待,任何一个线程完成之前,所有的线程都必须等待。

两种构建方法:CyclicBarrier(int parties);CyclicBarrier(int parties,Runnable barrierAction

public class CyclicBarrieDemo {
    public static void main(String[] args) {
        CyclicBarrier cbr = new CyclicBarrier(3);

        for (int i = 1; i <= cbr.getParties(); i++) {
            int tmp = i;
            new Thread(() -> {
                try {
                    System.out.println("学员1号:翻越第"+tmp+"号障碍");
                    //等待 让cbr中的所有线程都完成后才会继续下一步行动
                    cbr.await();//即在所有人翻越完第一个障碍之前都不能继续翻越其他障碍
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(() -> {
                try {
                    System.out.println("学员2号:翻越第"+tmp+"号障碍");
                    cbr.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
            new Thread(() -> {
                try {
                    System.out.println("学员3号:翻越第"+tmp+"号障碍");
                    cbr.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
        System.out.println("main主线程finish");
    }
}

CountDownLatch和CyclicBarrier的区别: 

①CountDownLatch计算为0时释放所有等待的线程;CyclicBarrier计数达到指定值时释放所有等待线程。

②CountDownLatch计数为0时,无法重置;CyclicBarrier计数达到指定值时,计数置为0重新开始。

③CountDownLatch不可重复利用;CyclicBarrier可重复利用。

总的来说:CountDownLatch 是计数器, 线程完成一个就记一个, 就像 报数一样, 只不过是递减的。

          CyclicBarrier更像一个水闸, 线程执行就想水流, 在水闸处都会堵住, 等到水满(线程到齐)了, 才开始泄流。

5)Semaphore:计数信号量(信号灯)

Semaphore 是 synchronized 的加强版,作用是控制线程的并发数量。每个acquire()都会阻塞,直到许可证可用,然后才能使用它。 每个release()添加许可证,潜在地释放阻塞获取方。 但是,没有使用实际的许可证对象; Semaphore只保留可用数量的计数,并相应地执行。

构造器构建方式:Semaphore(int permits)---创建一个 Semaphore与给定数量的许可证和非公平设置

                 Semaphore(int permits,boolean fair)----创建一个 Semaphore与给定数量的许可证和给定的公平设置

【是否公平,获得锁的顺序与线程启动顺序有关,就是公平,先启动的线程,先获得锁。fair 不能100% 保证公平,只能是大概率公平。fair 为 true,则表示公平,先启动的线程先获得锁.】

public class SemaphoreDemo {
    public static void main(String[] args) {
        //模拟资源类,限流,假设有三个空车位
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    //获得一个资源,会将当前信号量-1,假设如果已经满了,就等待到被释放为止
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢占到了车位");
                    Thread.sleep(2000);//抢占车位后停2秒
                    System.out.println(Thread.currentThread().getName()+"离开了车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    //释放此资源,会将当前信号量+1,然后唤醒等待的线程
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }
}

【小扩展:若将构建的Semaphore限流个数设置为1,等价于synchronized的用法,即资源类被synchronized锁住,哪个线程抢到就可以得到资源数据。那么可以变相实现多线程并发抢占资源类(锁),抢到的线程可以任意设置其抢占资源的时间。】 

6)ReadWriteLock:读写锁

ReentrantLock(排他锁)具有完全互斥排他的效果,即同一时刻只允许一个线程访问,这样做虽然虽然保证了实例变量的线程安全性,但效率非常低下。ReentrantReadWriteLock实现了ReadWriteLock接口。ReentrantReadWriteLock里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和writeLock()用来获取读锁和写锁。读写锁维护了两个锁,一个是读操作相关的锁称为共享锁,一个是写操作相关的锁称为独占锁(排他锁)。通过分离读锁和写锁,其并发性比一般排他锁有了很大提升。

多个读锁之间不互斥,读锁与写锁互斥,写锁与写锁互斥只要出现写操作的过程就是互斥的)】

public class ReadWriteLockDemo {
    public static void main(String[] args) {
        ReadWriteLockThread rwl = new ReadWriteLockThread();

        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                rwl.write();
            }
        },"write").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                rwl.read();
            }
        },"read1").start();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                rwl.read();
            }
        },"read2").start();
    }
}
class ReadWriteLockThread{
    private int number = 0;
    ReadWriteLock lock = new ReentrantReadWriteLock();

    //读
    public void read(){
        lock.readLock().lock();//上锁
        try {
            System.out.println(Thread.currentThread().getName()+":"+number);
        } finally {
            lock.readLock().unlock();//释放锁
        }
    }
    //写
    public void write(){
        lock.writeLock().lock();//上锁
        try {
            number = (int)( Math.random()*100+1);
            System.out.println(Thread.currentThread().getName()+":"+number);
        } finally {
            lock.writeLock().unlock();//释放锁
        }
    }
}

 7)BlockingQueue :阻塞队列

核心用法:

方法类型 抛出异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e,time,unit)
移除 remove() poll() take() poll(time,unit)
检查 element() peek() 不可用 不可用

四组不同操作解释:

抛出异常

当阻塞队列满时,再往队列中add插入元素会抛异常:Queue full

当阻塞队列空时,再往队列中remove移除元素会抛NOSuchElementException

特殊值

插入方法,成功true,失败false

移除方法,成功返回出队列的元素,队列里没有就返回null

一直阻塞

当阻塞队列满时,生产者线程继续往队列里put元素,队列会一直阻塞生产者线程直到put数据or响应中断退出

当阻塞队列空时,消费者线程试图从队列中take元素,队列会一直阻塞消费者线程直到队列可用

超时退出 当阻塞对内满时,队列会阻塞生产者线程所设定的时间,超过显示后生产者线程会退出

BlockingQueue接口还有多种常用实现类:ArrayBlockingQueue,LinkBlockingQueue,SychronousQueue等,本篇文章没有介绍,可以参考https://www.cnblogs.com/KingIceMou/p/8075343.html

4.各种锁及其含义

公平锁,非公平锁,可重入锁(递归锁),自旋锁,独占锁(互斥锁,排他锁),共享锁,以及乐观锁和悲观锁

1)公平锁:是指多个线程按照申请锁的顺序来获取锁。在并发情况下,每个线程在获取锁时会查看此锁维护的等待队列,如果为空,或者当前线程是等待队列的第一个,就占有锁,否则就会加入到等待队列中,以后会按照FIFO的规则从队列中取到自己。

2)非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,开始就直接尝试占有锁,如果尝试失败,就再采取类似公平锁那种方式,有可能后申请的线程先申请的线程优先获取锁。在高并发的情况下,有可能会造成优先级反转或者饥饿现象。

ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁,默认是非公平锁。

    非公平锁创建:ReentrantLock lock = new ReentrantLock(); 或  ReentrantLock lock = new ReentrantLock(false);

    公平锁创建:ReentrantLock lock = new ReentrantLock(true);  非公平锁的优点在于吞吐量比公平锁大

3)可重入锁(递归锁):指的是同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码,在同一线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。也就是说,线程可以进入任何一个它已经拥有的锁中所有同步着的代码块或方法。可重入锁最大的作用是避免死锁

4)自旋锁:是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下切换的消耗,缺点是循环会消耗CPU。底层是由CAS算法实现

5)独占锁(写锁):指该锁一次只能被一个线程所持有。写操作:原子 + 独占

6)共享锁(读锁):指该锁可被多个线程所持有。共享锁可保证并发读是非常高效的

7)乐观锁:总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。

【version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。】

8)悲观锁:总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁。

【synchronized是非公平、可重入、独占的悲观锁;ReentrantLock是默认非公平、可重入,独占的悲观锁】


扩展:

/*    八锁问题:
 * 1.两个普通同步方法,同一对象,分别用两个线程去调用---> one    two
 * 2.在getOne方法中加入Threa.sleep(),同一对象,再分别用两个线程去调用---> one    two
 * 3.新增普通静态方法getThree(),同一对象,三个线程分别调用--->three  one  two
 * 4.两个普通同步方法,两个number对象,两个线程--->two  one
 * 5.修改getOne()方法为静态同步方法,同一对象,两线程调--->two   one
 * 6.修改两个方法均为静态同步方法,同一对象,两线程调--->one   two
 * 7.getOne()为静态同步,getTwo()为非静态同步,两个对象,两线程--->two  one
 * 8.两个均为静态同步方法,两个对象,两线程--->one   two
 *
 * 线程8锁问题:①非静态方法的锁默认为this,静态方法的锁为Class实例
 *          ②某一时刻,只能有一个线程有锁,无论几个方法。
 */
public class EightLocksDemo {
    public static void main(String[] args) {
        GetNumberThread number = new GetNumberThread();
        GetNumberThread number2 = new GetNumberThread();

        new Thread(()-> number.getOne()).start();
        new Thread(()->{
//            number.getTwo();
            number2.getTwo();
        }).start();
//        new Thread(()-> number.getThree()).start();
    }
}
class GetNumberThread{
    //同步方法1
//    public synchronized void getOne(){
//        try {
//            Thread.sleep(200);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }
//        System.out.println("one");
//    }
    public static synchronized void getOne(){
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("one");
    }
    //同步方法2
//    public synchronized void getTwo(){
//        System.out.println("two");
//    }
    public static synchronized void getTwo(){
        System.out.println("two");
    }
    //普通静态方法
    public static void getThree(){
        System.out.println("three");
    }
}

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUC并发编程(多线程进阶整理) 的相关文章

  • Qt信号发送过快,槽函数处理不过来解决方法

    问题 跨线程使用信号与槽连接 信号的发送时间间隔小于槽函数处理的时间间隔 造成的问题 子线程下的槽函数 用sleep来模拟槽函数的耗时操作 void MyThread myTimeout qDebug lt lt test QThread
  • Thread.Sleep(0)的妙用

    https www cnblogs com JianGuoWan p 9139698 html Thread Sleep 0 表示挂起0毫秒 你可能觉得没作用 你要写Thread Sleep 1000 就有感觉了 似乎毫无意义 MSDN的说
  • 无线传感网必知必会

    一 填空题 传感器网络三大基本要素 传感器 感知对象 用户 观测者 传感器节点的基本功能模块包括 数据采集模块 数据处理和控制模块 通信模块 供电模块 四个 其中 通信模块 能量消耗最大 传感器节点通信模块的工作模式有 发送 接收 空闲 睡
  • 静态代理模式

    package com kuang Demo04 静态代理总结 真实对象和代理对象都要实现同一个接口 代理对象要代理真实对象 好处 代理对象可以做很多真实对象做不了的事情 真实对象可以专注做自己的事情 public class Static
  • ⛳ 面试题-单例模式会存在线程安全问题吗?

    目录 面试题 单例模式会存在线程安全问题吗 一 单例模式 简介 二 饿汉式 三 懒汉式 3 1 懒汉式 在调用 getInstance 的时候才创建对象 线程不安全 3 2 改造1 对懒汉式进行加锁改造 线程安全 3 3 改造2 对懒汉式继
  • Cpp关键字破解(三)【volatile】篇

    关键字总结 volatile 文章目录 关键字总结 volatile 0 前言 1 概念 2 作用 3 使用场景 4 volatile成员函数 5 代码体验 0 前言 参考几位前辈博客 汇总整理了一下 C 中volatile关键字的使用详解
  • 线程安全的单例模式

    线程安全的单例模式 单例模式 属于创建类型的一种常用的软件设计模式 通过单例模式创建的类在当前进程中只有一个实例 一份资源只能被申请加载一次 如何实现 饿汉模式 资源在程序初始化的时候就去加载 后边使用的时候直接使用 使用会非常流畅 但是有
  • fcgi程序两种编写风格

    fcgi进程可以写成单线程的 也可以写成多线程的 单线程就是main函数中有一个死循环 一直等待接受请求 有请求过来时 就处理请求 并返回结果 没有并发性 多线程也分两种模式 一种是main函数起多个线程 每个线程都独立接受请求 另一种是m
  • java多线程使用教程

    文章目录 如何使用多线程 继承Thread类 实现Runnable接口 线程的生命周期 线程同步 线程间通信 shutdown 方法的重要性 如何使用多线程 在Java中 创建多线程的方式有两种 一种是继承Thread类 另一种是实现Run
  • Java 线程关闭

    Java线程关闭的方式 1 使用状态位 public class CloseThread extends Thread boolean flag true int index 0 Override public void run while
  • java数据迁移程序

    环境 mysql 目标 亿级数据迁移 最终耗时 1 2小时 服务器更佳 建议在晚上或者没人访问的情况下操作 思路 1 不能一下将所有数据 导入到目标数据表 耗时太久 且占用资源 所有就用程序批量执行 每次执行一个范围段 比如第一个线程 1
  • 关于Semaphore信号量的源码解读

    Semaphore的简单使用 利用Semaphore可以实现对线程数量的控制 比如如下的代码 class SemaphoreTest public static void main String args Semaphore semapho
  • [Java实现 Scoket实时接收Tcp消息 优化层层叠加]

    目录 前言 基础实现代码 描述 优化代码多线程处理客户端连接和消息接收 描述 再次优化异步实现 以下是使用 CompletableFuture 实现异步处理客户端请求的示例代码 描述 进一步优化的代码 Netty来实现Socket服务器 描
  • Java 多线程模式 —— Guarded Suspension 模式

    Part1Guarded Suspension 模式的介绍 我们只从字面上看 Guarded Suspension 是受保护暂停的意思 1Guarded Suspension 模式 在实际的并发编程中 Guarded Suspension
  • 【Java】网络编程——多线程下载文件

    前言 多线程下载文件 比单线程要快 当然 线程不是越多越好 这和获取的源文件还有和网速有关 原理 在请求服务器的某个文件时 我们能得到这个文件的大小长度信息 我们就可以下载此长度的某一个片段 来达到多线程下载的目的 每条线程分别下载他们自己
  • [QT编程系列-25]:多线程机制 - QThread和MoveToThread简介

    目录 第1章 简介 1 1 多线程的目的 1 2 QThread多线程使用方法 1 3 QT支持多线的步骤 第2章 QThread 2 1 概述 2 2 moveToThread 第1章 简介 1 1 多线程的目的 QThread类提供了一
  • C++多线程(并发、进程、线程的基本概念和综述)

    并发 进程 线程的基本概念和综述 并发 并发表示两个或者更多任务 独立的活动 同时发生 进行 例如 一面唱歌一面弹琴 一面走路一面说话 画画的时候听小说等 回归到计算机领域 所谓并发 就是一个程序同时执行多个独立的任务 以往计算机只有单核C
  • 02Linux下C语言锁的学习之Linux下的读写锁

    02Linux下C语言锁的学习之Linux下的读写锁 概述 下面的锁的意思均是代表读写锁 读写锁的特性 1 若一把锁被一个线程以读方式锁住 当其它线程以读方式上锁的话 那么可以上锁成功 2 若一把锁被一个线程以写方式锁住 当其它线程以读或者
  • wxwidgets编写多线程程序--wxThread

    细节描述 线程基本上来说是应用程序中一条单独执行的路径 线程有时被称为轻量级进程 但线程与进程的根本不同之处在于不同进程存储空间是相互独立的 而同一进程里的所有线程共享同一地址空间 尽管这使得它更容易共享几个线程间的普通数据 但这也使得它有
  • VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题...

    VC9编译的程序在没有装过VC9 确切的说是 Net Framework3 5 的机器上运行时 如果提示 由于应用程序配置不正确 应用程序未能启动 重新安装应用程序可能会纠正这个问题 这个错误 那么就说明该程序动态链接了VC9的运行时库 如

随机推荐

  • 三种方法构建Java树形结构,Stream真的厉害

    小熊学Java网站 https javaxiaobear gitee io 每周持续更新干货 建议收藏 平时大概率我们会构建一些树形结果返回给前端 比如菜单结构 部门列表 文件结构等 我们一般想到的就是利用递归来循环构建 现在 就我个人解决
  • 二值网络——开启小而快神经网络时代

    摘要 这种使用浮点计算的神经网络要求的大存储空间和大计算量 严重阻碍了其在手机 手表和移动机器人等设备上的应用 二值神经网络设法让计算主要在正1或负1间进行 几十倍地降低了网络大小和计算量 但一直以来难以达到高预测准确率 最新的进展大幅提高
  • E1接口介绍

    E1 通道本来设计用来传输电话的 每个 E1 带宽 2 048M 可以传 30 路电话 后来扩大的应用范围 可以用作传网络 串口等不同的业务 E1 是一个基本的传输单元 其最终还是通过光纤来传输的 如 PDH 光端机 就是用来传 E1 的
  • WSL2:开发环境安装

    写在前面 主要是记录一下如何安装和搭建基于WSL2的开发环境 参考博文 搭建优雅的Windows终端 Windows terminal scoop starship 一 安装WSL2 以管理员身份运行CMD 执行以下命令即可WSL和Linu
  • 26段简短代码带你零基础入门Python

    01 运行方式 本文示例代码使用的Python版本为Python 3 6 运行Python代码有两种方式 一种方式是启动Python 然后在命令窗口下直接输入相应的命令 另一种方式就是将完整的代码写成 py脚本 如hello py 然后在对
  • 最新版 dubbo-admin 0.3.0 版本安装配置教程,如何才能正确打开

    首先是资源的下载 进入该链接 https github com apache dubbo 然后往下翻 找到这里 点进去 然后这里我直接下载压缩包 解压以后 包里的内容 这里需要关注的有这两三个包 选中的 server是后端包 ui是前端包
  • three.js设置Geometry顶点位置、顶点颜色数据

  • 鸡和兔子共36脚100Matlab,matlab习题集精选.pdf

    matlab习题集精选 Matlab 习题 2014 12 Matlab 习题 1 MATLAB 操作基础 1 1用一元二次方程求根公示解方程x 2 2 x 3 0 的根 1 2三角形边长分别为3 4 5 求其面积 1 2 0 1 0 1
  • git 常见命令——将本地仓库推送到远程仓库的相关流程

    目录 1 初始化项目 2 建立本地仓库和远程仓库的连接 3 已有项目只需克隆项目到本地 无需进行前两步 4 创建并切换分支 4 1 查看当前分支 4 2 切换分支 4 3 常见分支类型有 4 4 在切换分支的时候 将当前分支修改的内容 同步
  • Outlook 错误号 0x800CCC0B,怎么解决?

    正常设置了OUTLOOK EXPRESS等邮件客户端软件后 若发信不正常 OUTLOOK EXPRESS会给您一个错误信息 这个错误信息里面会包含一个错误码根据错误码与下表进行比较 一般都可以找出大多数问题的解决办法 错误码 意义 0x80
  • 如何优雅的写单词_lduoj_kmp

    如何优雅的写单词 Description 单词背多少了 心里还没有点数了 还有多长时间考试你知道吗 你说 单词背到第几章了 呜呜呜 别骂了别骂了 再骂人傻了 在深知单词的重要性之后 PushyTao下定决心要好好背单词 为了防止在考试的时候
  • AngularJS之组件化篇

    AngularJS组件化 将页面中可以重复使用的标签封装成一个组件 方便这一部分UI重复使用 类似于JS中的函数 封装了一部分处理代码 通过函数调用就可以重复使用这些代码 组件可以用 component 注册 在 angular modul
  • java--基础--16.5--IO流--BufferedWriter,BufferedReader

    java 基础 16 5 IO流 BufferedWriter BufferedReader 1 字符流 字符流 字符流 字节流 编码表 字符输入流 Reader 抽象类 int read 一次读取一个字符 int read char ch
  • NXP i.MX RT1052介绍

    1 NXP i MX RT1052 连载之 MCU 简介 1 KiFF的博客 CSDN博客 2 NXP i MX RT1052 连载之 Boot 简介 2 KiFF的博客 CSDN博客 重要 3 i MXRT单片机 Cortex M7 i
  • 1-1、Lua总结开篇

    一 序言 最近在开发物联网相关的探针业务 用于对机顶盒中的网络数据进行嗅探并处理以获取用户行为数据 然后提供给大数据平台 由此 我们可以看到物联网很大一部分功能是为大数据服务的 采集 物 中的数据提供给大数据平台 而进一步讲 大数据的数据提
  • 华为OD机试真题 Java 实现【最短木板长度】【2022Q4 100分】,附详细解题思路

    一 题目描述 小明有 n 块木板 第 i 1 i n 块木板长度为 ai 小明买了一块长度为 m 的木料 这块木料可以切割成任意块 拼接到已有的木板上 用来加长木板 小明想让最短的木板尽量长 请问小明加长木板后 最短木板的长度可以为多少 二
  • FDFS如何卸载

    之前在安装FDFS的时候 有些 sample文件没有生成 我也不知道是不是安装的问题 所以只有是卸载重装 重装后 问题解决 1 停止trackerd服务 sudo service fdfs trackerd stop 2 停止storage
  • Vue.js2+Cesium1.103.0 三、模型加载与切割

    Vue js2 Cesium1 103 0 三 模型加载与切割 Demo 模型加载 const tileset new Cesium Cesium3DTileset url https lab earthsdk com model 3610
  • VMware推免费服务器版虚拟软件

    VMware宣布将免费推出服务器版虚拟软件VMware Server 而其beta版本已经可以下载 作为商业版VMware GSX Server的继任者 VMware Server for Linux Windows允许用户同时运行多个操作
  • JUC并发编程(多线程进阶整理)

    JUC并发编程 要想学习JUC就必须了解 java util concurrent 包的工具类 其中包含 java util concurrent 并发包 java util concurrent atomic 并发原子包 java uti