首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >爽玩多线程来开发,太哇塞了!

爽玩多线程来开发,太哇塞了!

作者头像
码猿技术专栏
发布于 2023-05-01 07:27:36
发布于 2023-05-01 07:27:36
60700
举报
运行总次数:0

多线程大家肯定都不陌生,理论滚瓜烂熟,八股天花乱坠,但是大家有多少在代码中实践过呢?很多人在实际开发中可能就用用@Async,new Thread()。线程池也很少有人会自己去建,默认的随便用用。在工作中大家对于多线程开发,大多是用在异步,比如发消息,但是对于提效这块最重要的优势却很少有人涉及。因此本篇文章会结合我自己的工作场景带大家去发掘项目中的多线程场景,让你的代码快如闪电。

多线程普及

多线程解决了什么问题?带来了什么问题?

Cpu为了均衡与内存的速度差异,增加了缓存--导致了可见性问题

操作系统增加了进程和线程,分时复用CPU,进而均衡CPU与IO设备的速度差异--导致了原子性问题

编译程序优化指令排序(JVM指令重排序)--导致了有序性问题

可见性问题--线程A修改共享变量,修改后CPU缓存中的数据没有及时同步到内存,线程B读取了内存中老数据

原子性问题--多个线程增加数据,有几个线程挂了,这数据就少了

有序性问题--经典的对象创建三步,堆中分配内存-->初始化-->变量指向内存地址,如果排序重排会出现132,导致没有初始化的对象被创建

JVM提供了什么工具去解决线程不安全问题?Java代码有哪些实现思路?

JVM提供了三个关键词,synchronized、volatile、final和JMM(线程操作内存规范,例如8个happen before原则)

Java代码实践可从三方面入手

  1. 同步:synchronized和ReentrantLock
  2. 非同步:CAS(CPU原语,依赖硬件)
  3. 线程安全:局部变量(虚拟机栈或者本地方法栈,线程私有)和ThreadLocal(本地线程变量副本,空间换安全,每个线程一份)

如何开启线程?

基础的Thread、runable、callable,进阶的ThreadExecutor和Future,以及JDK8的终极武器CompletableFuture

线程间如何协作?

基础

1、volatile和synchronized关键字

  • volatile关键字用来修饰共享变量,保证了共享变量的可见性,任何线程需要读取时都要到内存中读取(确保获得最新值)。
  • synchronized关键字确保只能同时有一个线程访问方法或者变量,保证了线程访问的可见性和排他性。

2、等待/通知机制 --指一个线程A调用了对象O的wait()方法进入等待状态,而另一个线程B 调用了对象O的notify()或者notifyAll()方法,线程A收到通知后从对象O的wait()方法返回,进而 执行后续操作

3、管道输入/输出流

  • 管道输入/输出流和普通的文件输入/输出流或者网络输入/输出流不同之处在于,它主要 用于线程之间的数据传输,而传输的媒介为内存。
  • 管道输入/输出流主要包括了如下4种具体实现:PipedOutputStream、PipedInputStream、 PipedReader和PipedWriter,前两种面向字节,而后两种面向字符。

4、join()方法--如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才 从thread.join()返回。线程Thread除了提供join()方法之外,还提供了join(long millis)和join(long millis,int nanos)两个具备超时特性的方法。关注工众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!这两个超时方法表示,如果线程thread在给定的超时 时间里没有终止,那么将会从该超时方法中返回。

5、ThreadLocal--即线程本地变量(每个线程都有自己唯一的一个哦),是一个以ThreadLocal对象为键、任意对象为值的存储结构。底层是一个ThreadLocalMap来存储信息,key是弱引用,value是强引用,所以使用完毕后要及时清理(尤其使用线程池时)。

进阶的有JDK5开始提供的Semaphore(信号量)、CyclicBarrier、CountDownLatch以及JDK8的CompletableFuture

场景实战

多线程处理场景

并行聚合处理数据

以下案例主要运用CompletableFuture.allOf()方法,将原本串行的操作改为并行。本案例相对比较常规,算是CompletableFuture的基本操作,其他特性就不一一介绍了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
AtomicReference<List<SellOrderList>> orderLists = new AtomicReference<>();
AtomicReference<List<InternationalSalesList>> salesLists = new AtomicReference<>();
AtomicReference<Map<String, BigDecimal>> productMap = new AtomicReference<>();
.........



//逻辑A
CompletableFuture<Void> orderListCom =
        CompletableFuture.runAsync(() -> {
            orderLists.set(sellOrderListService.lambdaQuery()
                    .ge(SellOrderList::getOrderCreateDate, startDate)
                    .le(SellOrderList::getOrderCreateDate, endDate)
                    .eq(SellOrderList::getIsDelete, 0).list());
        });
CompletableFuture<Void> productCom = CompletableFuture.runAsync(() -> {
//逻辑B});
CompletableFuture<Void> euLineCom = CompletableFuture.runAsync(() -> {

//逻辑C});

//汇总线程操作
CompletableFuture.allOf(orderListCom, productCom, euCloudCom).handle((res, e) -> {

    if (e != null) {
        log.error("客户订单定时任务聚合数据异常", e);
    } else {
        try {
        //获取全部数据后处理数据
            aggregateData(customerList, saleMonth, orderLists, salesLists, productMap, euLineList, asLineList,
                    euCloudList, asCloudList, itemMap, deliveryMap, parities);
        } catch (Exception ex) {
            log.error("客户订单处理数据异常", ex);
        }
    }
    return null;
});

修改for循环为并行操作

这里借鉴了parallelStream流的思路,将串行的for循环分割成多个集合后,对分割后的集合进行循环。这应该是最普遍的多线程应用场景了,需要注意的是线程池需要自定义大小、不安全的集合例如ArrayList并行add时需要加锁,加好日志就完事了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//自建线程池,ForkJoinPool默认的太小,一般是逻辑CPU数量-1
int logicCpus = Runtime.getRuntime().availableProcessors();
ForkJoinPool forkJoinPool = new ForkJoinPool(logicCpus * 80);
//指定集合大小,避免频繁扩容
List<RedundantErpSl> slAddList = new ArrayList<>(50000);
//谷歌提供工具类切分集合--import com.google.common.collect.Lists; 
List<List<SlErpDTO>> partition = Lists.partition(slErpList, 1000);
int finalLastStatus = lastStatus;

CompletableFuture<Void> handle = CompletableFuture.allOf(partition.stream().map(addPartitionList ->
                CompletableFuture.runAsync(() -> {
                    for (SlErpDTO slErp : addPartitionList) {
                        //TODO 逻辑处理
                        synchronized (slAddList) {
                            //ArrayList线程不安全,多线程会出现数据覆盖,体现为数据丢失
                            slAddList.add(sl);
                        }
                    }
                }, forkJoinPool)).toArray(CompletableFuture[]::new))
        .whenComplete((res, e) -> {
            if (e != null) {
                log.error("多线程组装数据失败", e);
            } else {
                try {
                    //进一步处理循环后的结果
                    slService.batchSchedule(versionNum, slAddList);
                } catch (Exception ex) {
                    log.error("批量插入失败", ex);
                }
            }
        });
handle.join();

List转Map解除嵌套循环

一个常见的场景,当我们需要判断当前数据的唯一值是否在数据库存在从而判断是新增还是修改时,常规做法是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for(X x:待新增或者修改的数据){
    String xKey=x.getKey();//key为当前数据的唯一值
    for(Y y:数据库中的数据){
        if(Objects.equals(xKey,yKey)){
            update;//相等就修改        
        }else{
            add;//不等就新增        
        }
    } 
} 

循环套循环,时间复杂度O(n²),性能肯定是比较差的,那怎么改造呢?如果能确定key是唯一值,就拿优化神器Map来操作,算法里面常见的优化手段。我们把唯一值key作为键,输出对象作为值那么上面的代码就可以改造为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Map<String, SafeRule> list2Map() {
    List<SafeRule> ruleList = lambdaQuery().eq(SafeRule::getIsDelete, 0).list();
    return ruleList.stream().collect(Collectors.toMap(e -> e.getBigClass() + e.getSmallClass(),
Function.identity()));
}
for(X x:待新增或者修改的数据){
    String xKey=x.getKey();//key为当前数据的唯一值
    Map<String, SafeRule> map=list2Map();
        if(map.get(xKey)!=null){
            update;//相等就修改        
        }else{
            add;//不等就新增        
        }
    } 
} 

引入Map之后,第二个嵌套的循环就干掉了,换成了单次循环来组装Map,时间复杂度降为O(n)。此方法仅适用于每行数据拥有唯一值,不然stream在组装时会提示重复key。

修改Map遍历为并行操作

既然for循环能转换,那么map遍历必然也能通过多线程改造。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 将map切成段--工具类
 *
 * @param splitMap 被切段的map
 * @param splitNum 每段的大小
 */
public static <k, v> List<Map<k, v>> mapSplit(Map<k, v> splitMap, int splitNum) {
    if (splitMap == null || splitNum <= 0) {
        List<Map<k, v>> list = new ArrayList<>();
        list.add(splitMap);
        return list;
    }
    Set<k> keySet = splitMap.keySet();
    Iterator<k> iterator = keySet.iterator();
    int i = 1;
    List<Map<k, v>> total = new ArrayList<>();
    Map<k, v> tem = new HashMap<>();
    while (iterator.hasNext()) {
        k next = iterator.next();
        tem.put(next, splitMap.get(next));
        if (i == splitNum) {
            total.add(tem);
            tem = new HashMap<>();
            i = 0;
        }
        i++;
    }
    if (!CollectionUtils.isEmpty(tem)) {
        total.add(tem);
    }
    return total;
}
//代码示例
Map<String, List<BudgetErpDTO>> materialMap = materialList.parallelStream()
                .collect(Collectors.groupingBy(e -> e.getInvOrgId() + "-" + e.getItemId() + "-" + e.getVendorId()));
        List<Map<String, List<BudgetErpDTO>>> mapList = MapUtil.mapSplit(materialMap, 50);
        CompletableFuture<Void> handle =
                CompletableFuture.allOf(mapList.stream().map(splitMap -> CompletableFuture.runAsync(() -> {
                            splitMap.forEach((identity, list) -> {
                                
//业务操作                                
                            });
                        }, ioDense)).toArray(CompletableFuture[]::new))
                        .exceptionally(e -> {
                            log.error("多线程组装数据失败", e);
                            return null;
                        });

上面提供了一个切分的工具类,以及Map改造的代码,总体还是非常简单,思路和for循环的改造是差不多的

多线程新增

我个人在开发中会使用一些小工具来提高开发效率,接下来公开一个我常用的批量插入的小工具,这个小工具最开始是同事给我的,然后我做了优化和扩充,主要是扩充了多线程以及service块的代码。

总览

该工具类用于生成复制可用的代码,这里需要提前指定一些固定变量。除了entity和serviceName需要根据实际情况变化之外,方法名和参数名可以不变。生成了四个方法,分别是mapper类中的方法、mapper.xml中的foreach批量插入代码、普通无事务的多线程批量插入代码、多线程事务代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//批量方法名,对应mapper和xml中id
String methodName = "batchSchedule";
//mapper参数名称
String paramName = "addList";
//实际代码里面的service命名
String serviceName = "baseInfoService";
Class<?> entity = BudgetBase.class;
//批量插入
printMapper(entity.getSimpleName(), methodName, paramName);
printXml(entity, methodName, paramName);
//普通多线程批量插入,无事务
printSave(entity.getSimpleName(), serviceName, paramName, 1000);
//多线程事务,慎用
printAddTransaction(entity.getSimpleName(), paramName, 1000);

mapper方法
xml批量插入语句
多线程批量插入

这个多线程插入其实就是我上面多线程处理场景中for循环改造的变种,将集合拆分进行并行批量插入

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
if (CollectionUtils.isNotEmpty(addList)) {
     List<List<BudgetBase>> partition = Lists.partition(addList, 1000);
            CompletableFuture.allOf(partition.stream().map(addPartitionList ->
                                    CompletableFuture.runAsync(() -> baseInfoService.getBaseMapper().batchSchedule(addPartitionList)))
                            .toArray(CompletableFuture[]::new))
                    .exceptionally(e -> {
                        log.error("多线程处理异常", e);
                        return null;
                    });
        }

花里胡哨-多线程事务提交

这个才是本文的重点,接下来我会详细介绍我在开发中遇到的坑和知识点,敲黑板了啊,重点来了!

我写的这个多线程事务本质就是根据2PC理论手写了一个分布式事务,涉及到多线程、Spring事务、ThreadLocal、LockSupport这些知识点,在线上一定要慎重使用,最好不用,可作炫技用,秀就完了。

深刻理解Spring事务、ThreadLocal

从头说起,既然是多线程事务,那自然不能使用注解@Transactional去开启事务,Spring事务采用ThreadLocal来做线程隔离,ThreadLocalMap内部key为当前线程的ThreadLocal对象,也可以当作以当前线程为key,value也是个map,看源码可以知道,map里面key为数据源,value为数据库连接。

当然上来看源码,肯定认识不够深刻,接下来是一段错误代码示范,充分展示了理解上面那段话的重要性。我的第一次失败就是如下一段代码,首先肯定是能运行的,不能运行的例子我就不给了,先来看看这段代码。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//存储事务集合
List<TransactionStatus> traStatusList = new ArrayList<>();
//最外部更新或者删除时手动创建一个新事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
TransactionStatus statusStart = transactionManager.getTransaction(def);
traStatusList.add(statusStart);
//外部DML操作
lambdaUpdate().set(RedundantErpSl::getIsDelete, 1).set(RedundantErpSl::getUpdateTime, new Date())
        .eq(RedundantErpSl::getVersionNum, versionNumber).eq(RedundantErpSl::getIsDelete, 0).update();
List<List<RedundantErpSl>> partition = Lists.partition(RedundantErpSlList, 1000);
try {
    CompletableFuture<Void> future = CompletableFuture.allOf(partition.stream().map(addPartitionList ->
                    CompletableFuture.runAsync(() -> {
                        //Spring事务内部由ThreadLocal存储事务绑定信息,因此需要每个线程新开一个事务
                        DefaultTransactionDefinition defGo = new DefaultTransactionDefinition();
                        defGo.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                        defGo.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
                        TransactionStatus statusGo = transactionManager.getTransaction(defGo);
                        //ArrayList线程不安全,多线程会出现数据覆盖,体现为数据丢失
                        synchronized (traStatusList) {
                            traStatusList.add(statusGo);
                        }
                        getBaseMapper().batchSchedule(addPartitionList);
                    })).toArray(CompletableFuture[]::new))
            .exceptionally(e -> {
                log.error("批量导入出现异常", e);
                //向外抛出异常,确保最外面catch回滚
                throw new PtmException(e.getMessage());
            });
    future.join();
    for (TransactionStatus status : traStatusList) {
        transactionManager.commit(status);
    }
} catch (Exception e) {
    log.error("批量导入出现异常回滚开始", e);
    for (TransactionStatus status : traStatusList) {
        transactionManager.rollback(status);
    }
}

先说说这个错误例子我当时开发的思路,手动开启事务后,在每个线程操作开始的时候都创建一个事务,Spring事务传播级别用的TransactionDefinition.PROPAGATION_REQUIRES_NEW,即默认创建新事务。隔离级别一开始没改,然后我就尝试着操作了一下,好家伙批量新增的时候直接锁了。

查看正在锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS;

查看等待锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS;

异常如下图,锁超时异常

第一次看见下图这个错的时候,我是疑惑的,没有当回事,以为是多数据源的问题。我项目里有直连Oracle和MySQL两种关系型数据库,当时怀疑是多数据源事务没有正确解绑导致的问题。

PS:事实上这个坑给足了我提示,根本原因就是多线程事务解绑失败,但是我理解出现了偏差,为后文埋下了伏笔。

我当时一看有锁,这我能惯着,马上修改事务隔离级别为TransactionDefinition.ISOLATION_READ_COMMITTED读已提交(MySQL默认事务隔离级别为可重复读,这里下调了一级,总共四级)。

顺利插入但还是报上面这个错,错误位置是下面这个循环提交时报的,第二次循环的时候一定会报错。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
for (TransactionStatus status : traStatusList) {
    transactionManager.commit(status);
}

当时一度以为是多数据源的问题,但是Debug后发现resource里面只有一个数据源key,解绑一次后就没了,第二个循环解绑的时候就报上面这个错,因为找不到可以解绑的key了。关注工众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!我就很疑惑,为啥就一个数据源key,我不是在别的线程开了事务嘛,按理说开了多少个线程就有多少个事务,这个问题困扰了我大概一天左右的时间。然后我想到了Spring事务的实现原理ThreadLocal,然后联想到我的多线程开启事务,再看到我在主线程里面进行傻叉循环解绑,我瞬间为梦想窒息。

所以破案了,我在主线程是操作不了子线程事务,这也是代码报key找不到的原因,因为用主线程做key在ThreadLocal里肯定是拿不到子线程信息的,只能拿到主线程自己的。

多线程事务提交方案

因此解决方案就很简单,子线程的事务自己操作,那么多线程事务处理哪家强,JDK里找CompletableFuture!当然这里使用CountDownLatch也是可行的,网上也有案例。多线程事务在处理逻辑上其实和分布式事务很像,因此我这里采用2PC的思想,一阶段所有子线程全部开启事务并执行SQL,然后阻塞等待,二阶段判断是否全部成功,是就唤醒所有线程提交事务,否就全部回滚。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-----------需要注入Bean,一个是Spring Boot事务管理,一个是线程池-----------
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
@Qualifier("ioDenseExecutor")
private ThreadPoolTaskExecutor ioDense;
-----------多线程事务新增操作-----------
private void batchSchedule(List<BudgetBase> addList) {
        if (!CollectionUtils.isEmpty(addList)) {
            //定义局部变量,是否成功、顺序标识、等待线程队列
            AtomicBoolean isSuccess = new AtomicBoolean(true);
            AtomicInteger cur = new AtomicInteger(1);
            List<Thread> unfinishedList = new ArrayList<>();
            //切分新增集合
            List<List<BudgetBase>> partition = Lists.partition(addList, 1000);
            int totalSize = partition.size();
            //多线程处理开始
            CompletableFuture<Void> future =
                    CompletableFuture.allOf(partition.stream().map(addPartitionList -> CompletableFuture.runAsync(() -> {
                        //Spring事务内部由ThreadLocal存储事务绑定信息,因此需要每个线程新开一个事务
                        DefaultTransactionDefinition defGo = new DefaultTransactionDefinition();
                        defGo.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                        TransactionStatus statusGo = transactionManager.getTransaction(defGo);
                        int curInt = cur.getAndIncrement();
                        try {
                            log.info("当前是第{}个线程开始启动,线程名={}", curInt, Thread.currentThread().getName());
                            baseInfoService.getBaseMapper().batchSchedule(addPartitionList);
                            log.info("当前是第{}个线程完成批量插入,开始加入等待队列,线程名={}", curInt, Thread.currentThread().getName());
                            //ArrayList线程不安全,多线程会出现数据覆盖,体现为数据丢失
                            synchronized (unfinishedList) {
                                unfinishedList.add(Thread.currentThread());
                            }
                            log.info("当前是第{}个线程已加入队列,开始休眠,线程名={}", curInt, Thread.currentThread().getName());
                            notifyAllThread(unfinishedList, totalSize, false);
                            LockSupport.park();
                            if (isSuccess.get()) {
                                log.info("当前是第{}个线程提交,线程名={}", curInt, Thread.currentThread().getName());
                                transactionManager.commit(statusGo);
                            } else {
                                log.info("当前是第{}个线程回滚,线程名={}", curInt, Thread.currentThread().getName());
                                transactionManager.rollback(statusGo);
                            }
                        } catch (Exception e) {
                            log.error("当前是第{}个线程出现异常,线程名={}", curInt, Thread.currentThread().getName(), e);
                            transactionManager.rollback(statusGo);
                            isSuccess.set(false);
                            notifyAllThread(unfinishedList, totalSize, true);
                        }
                    }, ioDense)).toArray(CompletableFuture[]::new));
            future.join();
        }
    }
private void notifyAllThread(List<Thread> unfinishedList, int totalSize, boolean isForce) {
        if (isForce || unfinishedList.size() >= totalSize) {
            log.info("唤醒当前所有休眠线程,线程数={},总线程数={},是否强制={}", unfinishedList.size(), totalSize, isForce);
            for (Thread thread : unfinishedList) {
                log.info("当前线程={}被唤醒", thread.getName());
                LockSupport.unpark(thread);
            }
        }
    }

方案详解

为什么用LockSupport的park()和unpark()而不用Thread.sleep()、Object.wait()、Condition.await()?

  1. 更简单,不需要获取锁,能直接阻塞线程。
  2. 更直观,以thread为操作对象更符合阻塞线程的直观定义;
  3. 更精确,可以准确地唤醒某一个线程(notify随机唤醒一个线程,notifyAll唤醒所有等待的线程);
  4. 更灵活 ,unpark方法可以在park方法前调用。

第4点很重要,如果不能提前使用unpark()的话,按照代码逻辑最后一个线程会被永久阻塞。

为什么要自建线程池?

CompletableFuture默认的线程池ForkJoinPool.commonPool()偏向于计算密集型任务处理,核心线程数和逻辑CPU数少1,对于多线程事务这种IO密集型任务来说核心线程数偏少。并且上述方法在操作中都是阻塞线程,无法一次性开启全部线程的话,会导致notifyAllThread方法无法执行,老线程阻塞新线程无法开启,就尬住了。

ForkJoinPool基于工作窃取算法,所以最适合的是计算密集型任务,这里我们开启一个参数调整为IO密集型(多核心少队列)的ThreadPoolTaskExecutor线程池即可。

注意MySQL/Druid等数据库的最大连接数

使用多线程的时候也别忘了调整其他组件的最大连接数。Druid线程池这个代码配置可以调,MySQL5.7默认151得用配置文件调整。MySQL最大连接数调整的方法之前从零开始的SQL修炼手册-实战篇有讲解过,欢迎读者们翻翻我之前写的干货。

真实的批量提交

https://www.cnblogs.com/wxw7blog/p/8706797.html

话根据数据量判断用文章中的代码案例即可。

多线程事务提交方案--使用须知

如果此时数据库连接池配置较小,比如spring.datasource.druid.max-active=10(Druid配置最大连接数为10)。但是我在使用多线程提交时,分批次数为20,那么开了10个之后达到上线就会一直卡住,原因是老的线程挂起不会释放,新的线程因为线程池满了无法创建。因此在使用该方案时一定要估算数据量,分好合适的大小,连接池和数据库的最大连接数也要注意是否匹配。

来源:juejin.cn/post/7139700932018700319

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-04-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码猿技术专栏 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
专访北航机器人教授、博导王田苗(下):机器人未来三大重点应用领域
GAIR 今年夏天,雷锋网将在深圳举办一场盛况空前的“全球人工智能与机器人创新大会”(简称GAIR)。大会现场,雷锋网将发布“人工智能&机器人Top25创新企业榜”榜单。目前,我们正在四处拜访人工智能、机器人领域的相关公司,从而筛选最终入选榜单的公司名单。如果你的公司也想加入我们的榜单之中,请联系:2020@leiphone.com 按:王田苗,北京航空航天大学机器人研究所名誉所长,IEEE机器人与自动化中国北京大区主席,北京航空航天大学教授、博导,“十二五”服务机器人重点专项专家组组长、863计划先进制
AI科技评论
2018/03/07
8130
专访北航机器人教授、博导王田苗(下):机器人未来三大重点应用领域
北航机器人研究所名誉所长王田苗:人工智能与机器人的发展趋势 | 北大AI公开课笔记
主讲人:王田苗 | 北航机器人研究所名誉所长 整理:俞晶翔 张康 量子位 出品 | 公众号 QbitAI 5月30日周三晚,北京大学“人工智能前沿与产业趋势”第十三讲,本期北京航空航天大学博士生导师、机器人研究所名誉所长王田苗,分享了近期由机器人行业发生的变化引起的思考,以及整个机器人行业的发展和应用的相关内容。 本期内容案例与理论结合,深入浅出,量子位作为合作媒体,为大家带来详细课程笔记一份。 主讲嘉宾:王田苗,北京航空航天大学博士生导师,机器人研究所名誉所长、智能技术与机器人iTR实验室主任、国家特聘
量子位
2018/07/20
8860
北航博导王田苗剖析:达芬奇机器人为什么会成功?
近日,一篇《人工智能投资热图:拿到投资最多的是医疗健康领域的AI初创公司》的文章让不少人意识到医疗健康领域的机器人已呈现黑马之姿,让不少投资人和做智能机器人的从业者在机器人的短暂迷茫过后重新找到了激情,并希望这个领域的权威人士能带他们做个系统入门,而专注医疗机器人多年的北航教授,显然是个很好的导航师。 王田苗,北航教授、博导,北航机器人研究所名誉所长,国家长江学者,国家杰出基金获得者,IEEE机器人与自动化中国北京大区主席,峰会主旨报告《医疗机器人的现状》。以下为演讲的精华部分 目前的人工智能,主要是通过视
AI科技评论
2018/03/07
1.2K0
北航博导王田苗剖析:达芬奇机器人为什么会成功?
北京航空航天大学王田苗教授:当前智能机器人发展若干挑战性问题 | CCF-GAIR 2018
AI 科技评论按:2018 全球人工智能与机器人峰会(CCF-GAIR)在深圳召开,峰会由中国计算机学会(CCF)主办,雷锋网、香港中文大学(深圳)承办,得到了宝安区政府的大力指导,是国内人工智能和机器人学术界、工业界及投资界三大领域的顶级交流盛会,旨在打造国内人工智能领域最具实力的跨界交流合作平台。
AI科技评论
2018/08/06
5020
北京航空航天大学王田苗教授:当前智能机器人发展若干挑战性问题 | CCF-GAIR 2018
引爆AI不仅靠技术,执行有力才明势:明势资本科技高峰论坛实录
【新智元导读】 明势资本年会4月7日在北京举行。明势资本创始合伙人黄明明介绍了2016年度基金情况和2017年基金发展展望,并解析了明势资本的投资价值观和方法论。年会同时举办《科技高峰论坛》,新智元创始人杨静主持,黄明明、雷鸣、王田苗、张泉灵、张益肇等嘉宾参与讨论了人工智能的引爆点、如何落地问题。 2017年4月7日,明势资本在北京举行年会,发布了2016年度基金情况和2017年基金发展展望。明势资本创始合伙人黄明明解析了明势资本的投资价值观和方法论。年会同期举办的《科技高峰论坛》上,新智元创始人杨静主持、
新智元
2018/03/27
8350
引爆AI不仅靠技术,执行有力才明势:明势资本科技高峰论坛实录
AI Pioneers|星海图高继扬:人形机器人不是具身智能的唯一答案
人类正在迎来人工智能领域的爆炸式更新,技术向未知拓展的每一步,几乎都引起惊人的关注度。
机器之心
2024/04/26
6380
AI Pioneers|星海图高继扬:人形机器人不是具身智能的唯一答案
李宇浩:工程师、七家上市企业高管和努力「犯错」的机器人「西西弗斯」
“机器人市场还未短兵相接,刘邦在村里,项羽在乡里,我离你还有2000里,先打到中原再说。”
AI掘金志
2022/11/08
8040
李宇浩:工程师、七家上市企业高管和努力「犯错」的机器人「西西弗斯」
戴盟机器人王煜:「具身技能」才能发挥人形机器人的作用 | 具身智能十人谈
本月,在爆火的世界人工智能大会(WAIC 2024)中,人形机器人「十八金刚」成为了会场中当之无愧的焦点。
AI科技评论
2024/07/17
2770
戴盟机器人王煜:「具身技能」才能发挥人形机器人的作用 | 具身智能十人谈
优必选CEO:人形机器人是AI最终载体,智能音箱只是过渡产品
唐旭 发自 深圳 量子位 报道 | 公众号 QbitAI “人最容易犯的一个错误,就是用现在的眼光去看未来的人。” 在谈及未来人形机器人的陪伴是否会让人更孤独的问题时,优必选创始人、CEO周剑如是回答
量子位
2018/03/29
8770
首个国产脑外科手术机器人获批准产,王田苗教授指导打造
虽然乍一看与其他医疗器械并无明显不同,但无论是国内医疗领域,还是AI机器人业内,这都是标志性的大事件。
量子位
2018/07/24
4760
首个国产脑外科手术机器人获批准产,王田苗教授指导打造
15位学界、业界大咖深度解读仿生机器人及机器人应用丨CCF-GAIR 2018
AI研习社按:2018 全球人工智能与机器人峰会(CCF-GAIR)在深圳召开,峰会由中国计算机学会(CCF)主办,雷锋网、香港中文大学(深圳)承办,得到了深圳市宝安区政府的大力指导,是国内人工智能和机器人学术界、工业界及投资界三大领域的顶级交流盛会,旨在打造国内人工智能领域最具实力的跨界交流合作平台。
AI研习社
2018/07/26
5770
15位学界、业界大咖深度解读仿生机器人及机器人应用丨CCF-GAIR 2018
清华赵明国:智能人形机器人≠智能+人形 | 智者访谈
2024 年,人形机器人领域迎来爆发式增长。特斯拉 Optimus 的持续迭代、OpenAI 对 1X 的战略投资,众多初创公司异军突起,以及包括 Mobile ALOHA 在内学术界的不断创新,共同描绘出一幅激动人心的未来图景。
机器之心
2025/02/14
1580
清华赵明国:智能人形机器人≠智能+人形 | 智者访谈
史上首次!人形机器人敲响上市锣,优必选苦熬11年闯关成功
另一边,全新一代工业版人形机器人Walker S首次亮相,手持另一个锣锤走向舞台中央,与周剑一起敲响开市锣。
新智元
2024/01/04
2790
史上首次!人形机器人敲响上市锣,优必选苦熬11年闯关成功
商用清洁机器人江湖:16 位创业者「同台竞技」
王刚、程昊天、黄晓庆、韩龙、李宇浩、崔彧玮、郭震...他们一齐涌入商用清洁机器人赛道,却有着不同的思考,正所谓“八仙过海,各显神通”。
AI掘金志
2022/11/08
1.1K0
商用清洁机器人江湖:16 位创业者「同台竞技」
成本低至 16.8 万的人形机器人,正在成为现实
9月底,一则关于特斯拉机器人的视频流出,外界看到了Optimus擎天柱机器人的最新进展。
AI科技评论
2023/10/16
3730
成本低至 16.8 万的人形机器人,正在成为现实
优必选冲刺「人形机器人第一股」 背后:苦熬11年,布局全栈技术
机器之心原创 作者:吴昕 很难不佩服这家公司。  它最初由周剑在 2012 年创立,完全不起眼。当时,讨论商业化人形机器人和马斯克谈论火星移民一样如同天方夜谭。  人形机器人是一件艰难而伟大的事,也是一个对硬科技要求最高的超长赛道,需要细水长流。仅靠情怀远远不够,目的地只属于幸存于每一段艰险航程的人。 从来,时势造英雄,适合的思维秉性,套上合适的商业模式,遇上适合的产业演进阶段,坚守 11 年,这家公司的航程来到里程碑时刻 —— 2023 年 1 月 31 日,优必选科技向港交所提交招股说明书。  优必选
机器之心
2023/02/23
5150
优必选冲刺「人形机器人第一股」 背后:苦熬11年,布局全栈技术
人形机器人技术专利:中国第一
人民网研究院官方出品,中国机器人产业联盟提供学术支持。不仅覆盖全球范围内、涵盖整个技术体系、时间维度横跨超20年,整个人形机器人产业发展历程悉数在报告中展现。
量子位
2023/12/05
3940
人形机器人技术专利:中国第一
LeCun爆粗口、马斯克哭笑不得,只因9个人形机器人开了场新闻发布会
虽然LeCun也礼貌性地写了一句“原谅我的粗鲁(Pardon my french)”,但紧接着又说:
量子位
2023/08/05
2330
LeCun爆粗口、马斯克哭笑不得,只因9个人形机器人开了场新闻发布会
CCF-GAIR 2018来袭:100位嘉宾,11大专场,打造全球最大「跨界」人工智能和机器人盛会
本着让读者「读懂智能&未来」的使命,「人工智能」和「机器人」一直以来都是雷锋网近年来重点报道的对象。从泛语境来讲,人工智能为机器人提供了一种能力,机器人为人工智能提供了一个载体,虽然两者相辅相成,但总体来说,人工智能偏软、偏学术,机器人偏硬、偏应用,所以在早些年科技界举办行业盛会的时候,界限都比较明确,学术会议和产业会议完全是两个不同的景象。 近年来,人工智能行业飞速发展,得到多个商业巨头的重视,大量资本涌入,企业开始打造自己的各种「实验室」,学术科研机构也相应地成立了面向产业化的部门,产学融合的大趋
昱良
2018/06/25
8260
机器人崛起:具身智能的技术、商业与社会落地路线图
【AI&Society百人百问】是由腾讯研究院联合多方发起,通过不同学科背景、不同领域从业者的提问与解答,促进围绕人工智能的多元、前瞻与系统化思考。百人百问旨在聚焦生成式人工智能带来的技术、商业与社会议题,通过多方对话、研究与协同,探索面向智能化未来的新路径、新范式,更好发挥人工智能的潜力与优势,致力于提升社会福祉。
小腾资讯君
2024/07/02
3780
推荐阅读
专访北航机器人教授、博导王田苗(下):机器人未来三大重点应用领域
8130
北航机器人研究所名誉所长王田苗:人工智能与机器人的发展趋势 | 北大AI公开课笔记
8860
北航博导王田苗剖析:达芬奇机器人为什么会成功?
1.2K0
北京航空航天大学王田苗教授:当前智能机器人发展若干挑战性问题 | CCF-GAIR 2018
5020
引爆AI不仅靠技术,执行有力才明势:明势资本科技高峰论坛实录
8350
AI Pioneers|星海图高继扬:人形机器人不是具身智能的唯一答案
6380
李宇浩:工程师、七家上市企业高管和努力「犯错」的机器人「西西弗斯」
8040
戴盟机器人王煜:「具身技能」才能发挥人形机器人的作用 | 具身智能十人谈
2770
优必选CEO:人形机器人是AI最终载体,智能音箱只是过渡产品
8770
首个国产脑外科手术机器人获批准产,王田苗教授指导打造
4760
15位学界、业界大咖深度解读仿生机器人及机器人应用丨CCF-GAIR 2018
5770
清华赵明国:智能人形机器人≠智能+人形 | 智者访谈
1580
史上首次!人形机器人敲响上市锣,优必选苦熬11年闯关成功
2790
商用清洁机器人江湖:16 位创业者「同台竞技」
1.1K0
成本低至 16.8 万的人形机器人,正在成为现实
3730
优必选冲刺「人形机器人第一股」 背后:苦熬11年,布局全栈技术
5150
人形机器人技术专利:中国第一
3940
LeCun爆粗口、马斯克哭笑不得,只因9个人形机器人开了场新闻发布会
2330
CCF-GAIR 2018来袭:100位嘉宾,11大专场,打造全球最大「跨界」人工智能和机器人盛会
8260
机器人崛起:具身智能的技术、商业与社会落地路线图
3780
相关推荐
专访北航机器人教授、博导王田苗(下):机器人未来三大重点应用领域
更多 >
交个朋友
加入腾讯云官网粉丝站
双11活动抢先看 更有社群专属礼券掉落
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档