手写Java线程池_超详细解说_绝对能运行_代码超详细注释

线程池

问题背景

只是单纯使用 new Thread(runnable).start(); 的方式创建线程, 将会导致严重的程序性能问题: 1.线程创建, 销毁需要消耗很大的系统资源; 2.虚拟机创建线程的数量是有限的; 2.线程调度切换也将使程序性能下降; 针对这些问题, 对线程数量进行管理, 有效地重复利用线程, 将会很好地提高程序性能.

线程池原理

使用队列创建一定数量的线程, 当有任务的时候, 使用队列中线程执行任务(如果任务过多, 就将其放入任务队列, 进入等待执行状态), 任务执行完就自动回收线程队列中的线程(任务过少或者任务数量小于线程数量, 超出的线程将会销毁, 做到线程队列具有伸缩性);

根据上面描述, 我们自己的线程池将具有一下特点:
1.内部使用队列来管理线程, 管理提交的任务.
2.控制线程数量, 做到线程队列具有良好的伸缩性.
3.当任务数过多, 或者任务队列已经饱和, 将使用任务拒绝策略, 告诉对应的任务提交者.
4.使用线程工厂定制线程队列中, 每个线程的名字, 状态, 是否为守护线程等等.

线程池类图结构

  1. 任务队列, 队列使用limit限制提交任务的大小, 实现RunnableQueue接口(RunnableQueue接口负责: 1.接收用户提交的任务; 2.获取任务队列中的任务; 3.查看任务队列大小), LinkedRunnableQueue实现RunnableQueue中的方法, 并且针对用户提交不同的任务以及线程池种类(ThreadPool)的不同, 决定是否执行拒绝策略(拒绝策略具有多个, 拒绝方式取决于用户自定义, 在线程池内部具有默认的拒绝策略实现);
    在这里插入图片描述

  2. 实现Runnable接口, 在run方法中获取RunnableQueue中的任务, 然后执行RunnQueue中的任务, InternalTask中的run方法是一个while循环循环结束条件取决于是否关闭该线程(关闭线程据需要设置flag变量, 当flage为false, 线程run方法结束, 自动结束生命), 而不是当前用户提交的任务是否执行完!!!; InternalTask主要是对RunnableQueue的一种封装; stop方法主要是设置线程flag(flag主要判断当前线程是否关闭)
    在这里插入图片描述

  3. 线程池原型:
    1.实现Runnable接口(此处注明, BasicThreadPool是继承Thread, 但是Thread内容太多了不能很好地在UML图中显示, 所以我就把他删除了, 只留下了实现Runnable接口), 因为线程池自身执行也需要一个线程, 所以继承Thread, 这样可以在BasicThreadPool的构造方法中执行start(), run方法中执行创建线程的操作(线程池内部执行任务的线程); 创建线程取决于线程池设置的最大线程数, 核心线程数, 初始化线程数, 用户提交的任务数;

    2.实现ThreadPool接口(该接口主要用于定义线程池的基本操作, 比如执行任务, 获取线程池的一些基本属性) ;

    3.内部具有2个内部类(ThreadTask负责对InternalTask进行封装, DefaultThreadFactory主要定义默认的线程创建方式), 不同的线程池中拥有不同的默认创建方式, 因此将线程创建方式设置为内部类;

    4.在BasicThreadPool中使用newThread方法创建线程(这些线程用于执行ThreadTask中的任务);

    5.线程池原型中具有2个队列, 第一个是刚才上面提的RunnQueue(负责执行的任务), 第二个是ThreadQueue(负责存储创建的每一个线程, 使用ArrayQueue实现, 这样很好地维护管理了线程, 做到资源重用)

    6.removeThread方法: 删除多余的线程, 当用户提交的任务数量小于线程池中创建的线程数量, 那么就删除一定数量的线程, 这样才不会浪费线程资源.

    7.在构造方法中设置基本属性, 以及当前线程池的拒绝策略.
    在这里插入图片描述

每个接口, 类的详细定义

ThreadPool(interface 定义线程池基本操作)

package com.concurrent.customthreadpool;/*** 线程池接口* @author regotto*/
public interface ThreadPool {/*** 执行提交的Runnable任务* @param runnable*/void execute(Runnable runnable);/*** 关闭线程池*/void shutdown();/*** 获得线程池初始化大小* @return initSize*/int getInitSize();/*** 获得线程池最大线程数* @return maxSize*/int getMaxSize();/*** 获取线程池核心线程数* @return coreSize*/int getCoreSize();/*** 获取线程池中用于缓存任务队列的大小* @return queueSize*/int getQueueSize();/*** 获取线程池中国活跃的线程数量* @return activeCount*/int getActiveCount();/*** 查看线程池是否shutdown* @return boolan*/boolean isShutdown();}

RunnableQueue(interface 任务队列)

package com.concurrent.customthreadpool;/*** 存放提交的Runnable, 使用BlockedQueue, 设置limit* @author regotto*/
public interface RunnableQueue {/*** 缓存提交到线程池中的任务* @param runnable*/void offer(Runnable runnable);/*** 从缓存中获取Runnable任务* 如果没有任务, 调用者线程挂起, 在某些特定的时候抛出中断异常* @throws InterruptedException* @return runnable*/Runnable take() throws InterruptedException;/*** 缓冲区大小* @return size*/int size();}

LinkedRunnableQueue(class 对RunnableQueue的封装, 用户提交任务, 线程执行任务, 此过程使用生产者-消费者模式实现)

package com.concurrent.customthreadpool;import java.util.LinkedList;/*** 线程池的内部线程队列, 缓冲区* @author regotto*/
public class LinkedRunnableQueue implements RunnableQueue{/*** limit: 限制当前runnableList中还能存放多少内容* denyPolicy: 拒绝策略* runnableList: 存放runnable的缓冲区* threadPool: 线程池*/private final int limit;private  final RunnableDenyPolicy denyPolicy;private final LinkedList<Runnable> runnableList = new LinkedList<>();private final ThreadPool threadPool;public LinkedRunnableQueue(int limit, RunnableDenyPolicy denyPolicy, ThreadPool threadPool){this.limit = limit;this.denyPolicy = denyPolicy;this.threadPool = threadPool;}@Overridepublic void offer(Runnable runnable) {synchronized (runnableList) {if (runnableList.size() >= limit) {//用户提交的任务大于限制条件, 执行对应的拒绝策略System.out.println(runnableList.size() + " >= " + limit + " execute deny policy");denyPolicy.reject(runnable, threadPool);} else {//添加任务到任务队列尾部, 有任务存在, 唤醒刚才wait的线程runnableList.addLast(runnable);runnableList.notifyAll();}}}@Overridepublic Runnable take() throws InterruptedException{synchronized (runnableList) {while (runnableList.isEmpty()) {try {//从RunnableQueue中取出任务, 如果任务为空, 使当前线程waitrunnableList.wait();} catch (InterruptedException e) {throw e;}}//移除任务缓冲区的第一个return runnableList.removeFirst();}}@Overridepublic int size() {synchronized (runnableList) {return runnableList.size();}}
}

InternalTask(class 对RunnableQueue中任务的执行)

package com.concurrent.customthreadpool;/*** 用于线程池内部, 获取runnableQueue中的runnable* @author regotto*/
public class InternalTask implements Runnable {private final RunnableQueue runnableQueue;private volatile boolean running = true;public InternalTask(RunnableQueue runnableQueue){this.runnableQueue = runnableQueue;}@Overridepublic void run() {//如果线程没有关闭, 就让该线程死循环, 处理每一个提交的任务while (running && !Thread.currentThread().isInterrupted()){try {//处于中断时候的线程不做处理//获取RunnableQueue中任务, 然后执行Runnable take = runnableQueue.take();System.out.println("runnableQueue.take(): " + take.toString());take.run();} catch (InterruptedException e) {running = false;break;}}}/*** 停止当前任务, 设置其running为false, 在shutdown中处理*/public void stop(){this.running = false;}}

RunnableDenyPolicy(interface 任务拒绝策略)

package com.concurrent.customthreadpool;/*** 当任务数提交超过缓冲区limit, 执行对应的任务拒绝策略* @author regotto*/
@FunctionalInterface
public interface RunnableDenyPolicy {/*** 对提交到threadPool的runnable是否执行reject* @param runnable* @param threadPool*/void reject(Runnable runnable, ThreadPool threadPool);/*** 该策略使用空方法直接丢弃任务*/class DiscardDenyPolicy implements RunnableDenyPolicy {@Overridepublic void reject(Runnable runnable, ThreadPool threadPool) {System.out.println(runnable + "不做处理");}}/*** 该策略抛出一个RunnableDenyException*/class AbortDenyPolicy implements RunnableDenyPolicy {@Overridepublic void reject(Runnable runnable, ThreadPool threadPool) {throw new RunnableDenyException("The" + runnable + "will be abort");}}/***该策略Runnable给提交者所在的线程中运行, 不加入到线程中*/class RunnerDenyPolicy implements RunnableDenyPolicy{@Overridepublic void reject(Runnable runnable, ThreadPool threadPool) {if (threadPool.isShutdown()) {runnable.run();}}}}

RunnableDenyException(class 处理RunnableDenyPolicy抛出的运行时异常)

package com.concurrent.customthreadpool;public class RunnableDenyException extends RuntimeException {public RunnableDenyException(String message) {super(message);}
}

ThreadFactory(interface 定义创建线程的接口)

package com.concurrent.customthreadpool;/*** 创建线程接口, 定制线程属于哪一个group, 是否为守护线程, 优先级, 名字等* @author regotto*/
public interface ThreadFactory {/*** 创建定制化线程* @param runnable* @return thread*/Thread createThread(Runnable runnable);}

BasicThreadPool(class 线程池的实现)

package com.concurrent.customthreadpool;import java.util.ArrayDeque;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;/*** 默认的自定义的线程池, 内部使用Queue进行维护* @author regotto*/
public class BasicThreadPool extends Thread implements ThreadPool{/**initSize: 初始化线程数* maxSize: 线程池最大线程数* coreSize: 线程核心数* activeCount: 当前活跃线程数* threadFactory: 线程工厂, 配置线程创建需要的参数* runnableQueue: 任务队列* isShutdown: 是否关闭线程池* threadQueue: 工作线程队列* DEFAULT_THREAD_FACTORY: 默认的线程工厂* keepAliveTime: 线程存活时间*/private final int initSize;private final int maxSize;private final int coreSize;private int activeCount;private final ThreadFactory threadFactory;private final RunnableQueue runnableQueue;private volatile boolean isShutdown = false;private Queue<ThreadTask> threadQueue = new ArrayDeque<>();private final static RunnableDenyPolicy DEFAULT_DENY_POLICY = new RunnableDenyPolicy.DiscardDenyPolicy();private final static ThreadFactory DEFAULT_THREAD_FACTORY = new DefaultThreadFactory();private final static long DEFAULT_KEEP_ALIVE_TIME = 10;private final long keepAliveTime;private final TimeUnit timeUnit;public BasicThreadPool(int initSize, int maxSize, int coreSize, int queueSize) {this(initSize, maxSize, coreSize, DEFAULT_THREAD_FACTORY, queueSize, DEFAULT_DENY_POLICY,DEFAULT_KEEP_ALIVE_TIME, TimeUnit.SECONDS);}public BasicThreadPool(int initSize, int maxSize, int coreSize, ThreadFactory threadFactory, int queueSize,RunnableDenyPolicy denyPolicy, long keepAliveTime, TimeUnit timeUnit) {this.initSize = initSize;this.maxSize = maxSize;this.coreSize = coreSize;this.threadFactory = threadFactory;this.runnableQueue = new LinkedRunnableQueue(queueSize, denyPolicy, this);this.keepAliveTime = keepAliveTime;this.timeUnit = timeUnit;this.init();}/*** 初始化线程池, 创建initThread*/private void init() {start();for (int i = 0; i < initSize; ++i) {newThread();}}/*** 创建线程添加到线程队列, 然后用该线程执行ThreadTask任务(层层封装, 封装用户提交的任务)*/private void newThread() {InternalTask internalTask = new InternalTask(runnableQueue);//使用自定义的线程工厂创建线程Thread thread = this.threadFactory.createThread(internalTask);ThreadTask threadTask = new ThreadTask(thread, internalTask);System.out.println(threadTask.thread.getName() + "被添加");//添加到线程队列threadQueue.offer(threadTask);this.activeCount++;//被添加后的线程执行startthread.start();}@Overridepublic void execute(Runnable runnable) {if (this.isShutdown) {throw new IllegalStateException("The thread pool id destroy");}//将用户提交的任务放到runnableQueue中, 等待线程队列中线程执行this.runnableQueue.offer(runnable);}private void removeThread() {//ArrayDeque的remove就是removeFirstThreadTask threadTask = threadQueue.remove();//设置当前线程flag, 在InternalTask中跳出循环自动结束线程生命threadTask.internalTask.stop();this.activeCount--;}@Overridepublic void run() {while (!isShutdown && !isInterrupted()) {try {timeUnit.sleep(keepAliveTime);} catch (InterruptedException e) {isShutdown = true;break;}synchronized (this) {if (isShutdown) {break;}//当前队列中有任务还没有处理, 且activeCount < coreSizeif (runnableQueue.size() > 0 && activeCount < coreSize) {//此处i曾写做i=0,导致多创建了一个线程,在没有任务的时候该线程一直保持wait//因为关闭pool,该线程没有add到threadQueue,导致Interrupt失败,最终导致线程一直运行中for (int i = initSize; i < coreSize; ++i) {newThread();}//防止后面的if判断创建线程数超过coreSize, 在coreSize还没有满的时候, 只执行当前的ifcontinue;}//当上面if中创建的线程数不足的时候, 就扩大线程池线程数, 直到maxSizeif (runnableQueue.size() > 0 && activeCount < maxSize) {for (int i = coreSize; i < maxSize; ++i) {newThread();}}//当没有任务, 但是activeCount线程数超出coreSize大小, 回收超出coreSize的线程if (runnableQueue.size() == 0 && activeCount > coreSize) {for (int i = coreSize; i < activeCount; ++i) {removeThread();}}}}}@Overridepublic void shutdown() {synchronized (this) {if (!isShutdown) {isShutdown = true;System.out.println("threadQueue size:" + threadQueue.size());threadQueue.forEach(threadTask -> {//调用internalTask中stop, 设置当前线程运行标志为falsethreadTask.internalTask.stop();//设置线程中断状态threadTask.thread.interrupt();System.out.println(threadTask.thread.getName());});System.out.println("threadQueue中线程已经关闭");//当前线程池自己也要关闭this.interrupt();}}}@Overridepublic int getInitSize() {if (isShutdown) {throw new IllegalStateException("The thread pool is destroy");}return this.initSize;}@Overridepublic int getMaxSize() {if (isShutdown) {throw new IllegalStateException("The thread pool is destroy");}return this.maxSize;}@Overridepublic int getCoreSize() {if (isShutdown) {throw new IllegalStateException("The thread pool is destroy");}return this.coreSize;}@Overridepublic int getQueueSize() {if (isShutdown) {throw new IllegalStateException("The thread pool is destroy");}return this.runnableQueue.size();}@Overridepublic int getActiveCount() {if (isShutdown) {throw new IllegalStateException("The thread pool is destroy");}return this.activeCount;}@Overridepublic boolean isShutdown() {return this.isShutdown;}/*** 内部类, 定义自己默认的线程工厂*/private static class DefaultThreadFactory implements ThreadFactory {private static final AtomicInteger GROUP_COUNTER = new AtomicInteger(1);//设置线程组private static final ThreadGroup GROUP = new ThreadGroup("MyThreadPool-" + GROUP_COUNTER.getAndDecrement());private static final AtomicInteger COUNTER = new AtomicInteger(0);@Overridepublic Thread createThread(Runnable runnable) {//创建定制化的线程return new Thread(GROUP, runnable, " thread-pool-" + COUNTER.getAndDecrement());}}/*** 封装InternalTask, 与每次创建的线程绑定在一起*/private class ThreadTask {Thread thread;InternalTask internalTask;ThreadTask(Thread thread, InternalTask internalTask){this.thread = thread;this.internalTask = internalTask;}}
}

ThreadPoolTest(class 测试类)

package com.concurrent.customthreadpool;import java.util.concurrent.TimeUnit;/*** 用于测试自定的线程池* @author regotto*/
public class ThreadPoolTest {public static void main(String[] args) {//初始化线程数:2, 最大线程数:6, 核心线程数:4, 任务队列大小:1000final BasicThreadPool threadPool = new BasicThreadPool(2, 6, 4, 1000);//创建20个任务提交进行执行for (int i = 0; i < 20; ++i) {threadPool.execute(() -> {try {TimeUnit.SECONDS.sleep(3);System.out.println(Thread.currentThread().getName() + "is running and done.");} catch (InterruptedException e) {e.printStackTrace();}});}//此处用于测试线程池运行时基本信息状态
//        for (int j = 0; j < 1000; ++j) {
//            System.out.println("getActiveCount: " + threadPool.getActiveCount());
//            System.out.println("getQueueSize: " + threadPool.getQueueSize());
//            System.out.println("getCoreSize: " + threadPool.getCoreSize());
//            System.out.println("getMaxSize: " + threadPool.getMaxSize());
//            try {
//                TimeUnit.SECONDS.sleep(3);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }try {TimeUnit.SECONDS.sleep(25);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("shutdown");//测试线程池shutdown功能threadPool.shutdown();}
}

总结

  1. 不要让BasicThreadPool继承Thread, 这样会导致直接调用BasicThreadPool中start方法, 导致线程池崩溃, 应该将Thread设置成组合, 隐藏start();
  2. 修改拒绝策略, 做到可以让用户自我实现拒绝方式, 此处拒绝策略可以使用策略模式;
  3. 使用工厂模式定义ThreadPool;
  4. 对每个参数进行合法验证;
  5. 在自己写的过程中, 出现参数传递出错的问题, 导致线程一直wait, 如果遇到这种问题, 使用jconsole工具来查看线程状态, 定位出错的位置, 这样能很快解决遇到的问题.

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468630.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

分享一个非常 nice 的工具

最近有个问题&#xff0c;我需要经常使用远程连接工具&#xff0c;原因很简单&#xff0c;我需要控制另外一台电脑&#xff0c;我刚开始使用的是 teamviewer 这个软件&#xff0c;刚开始用的时间是非常爽的&#xff0c;不过有一天他给我来了个提示&#xff0c;说我的软件被商用…

推荐周立功先生的一本书

1. 这篇文章主要是推荐周工的一本书&#xff0c;大家在学习嵌入式的时候&#xff0c;很多人不明白嵌入式系统和单片机的区别&#xff0c;又感觉自己对嵌入式有所了解&#xff0c;知道什么是嵌入式&#xff0c;文章里的很多见解我觉得对很多人都非常有帮助&#xff0c;今晚上周工…

图管够!灌篮高手、女儿国…阿里日_这帮程序员太会玩了!

5月10日是阿里一年一度的阿里日&#xff0c;这对阿里人来说&#xff0c;是个非常特别的日子。 那什么是阿里日呢&#xff1f;看看官方介绍&#xff1a; 它起源于2005年4月20日&#xff0c;是为了纪念2003年5月的“非典”时期阿里人的激情和信念。因此阿里巴巴决定&#xff0c;今…

复习Collection_迭代器使用细节_泛型_综合案例

Collection_迭代器使用细节_泛型_综合案例 主要内容 Collection集合迭代器增强for泛型 第一章 Collection集合 1.1 集合概述 集合&#xff1a;集合是java中提供的一种容器&#xff0c;可以用来存储多个数据。 集合和数组既然都是容器&#xff0c;他们之间的区别: 数组的…

Kubernetes dashboard集成heapster

图形化展示度量指标的实现需要集成k8s的另外一个Addons组件&#xff1a; Heapster 。 Heapster原生支持K8s(v1.0.6及以后版本)和 CoreOS &#xff0c;并且支持多种存储后端&#xff0c;比如&#xff1a; InfluxDB 、 ElasticSearch 、 Kafka 。 下载源包和images文件 下载地址h…

ESP32 分区介绍

软件设计分区表 如上图是正常运行需要的分区&#xff0c;一个系统的正常运行是需要这么多分区的。 分区表&#xff0c;我们软件默认配置的分区表如下 # Espressif ESP32 Partition Table # Name, Type, SubType, Offset, Size, Flags nvs,data,nvs,0x9000,16K, otadata,data,…

python坦克大战_Life is short,you need Python——Python实现坦克大战(一)

先展示一下效果搓搓小手手&#xff0c;坦克大战即将开始……https://www.zhihu.com/video/1140743290784817152一、游戏引擎的安装安装方式有两种&#xff1a;1.pip安装 windows R --> cmd --> pip install pygame2.pycharm安装 File -->setting -->project -->…

复习Java字节流_字符流使用及案例

字节流_字符流 主要内容 IO流字节流字符流异常处理Properties 第一章 IO概述 1.1 什么是IO 生活中&#xff0c;你肯定经历过这样的场景。当你编辑一个文本文件&#xff0c;忘记了ctrls &#xff0c;可能文件就白白编辑了。当你电脑上插入一个U盘&#xff0c;可以把一个视频…

ESP32 coredump 分析

1. 上次写了一个 ESP32 入门&#xff0c;我想有必要再写这篇文章&#xff0c;这次主要是分析 coredump 的&#xff0c;这就像 Android 和 Linux 系统的死机分析&#xff0c;有意思&#xff0c;也有难度。我们写代码的时候&#xff0c;不可避免的会遇到一些 coredump 的问题&am…

我那个在华为过得很好的朋友

最近华为的事件又上了热搜&#xff0c;不管是微信还是知乎&#xff0c;都在啃这个热点&#xff0c;一种是看热闹不闲事大的&#xff0c;一种是看热闹闲事大的&#xff0c;我呢&#xff1f;昨晚跟我的一个华为朋友玩了几把王者荣耀&#xff0c;虽然不能带他全盘浪&#xff0c;但…

Redis概述_使用命令对redis的数据进行增删改查_Jedis连接redis进行数据操作_redis进行数据缓存案例

学习目标 redis 概念下载安装命令操作 1. 数据结构持久化操作使用Java客户端操作redis Redis 前言(从百度上抄的, 看看了解一下, 懒得排版了) 1. 概念&#xff1a; redis是一款高性能的NOSQL系列的非关系型数据库1.1.什么是NOSQLNoSQL(NoSQL Not Only SQL)&#xff0c;意即…

要有敬畏之心

最近开始周六也没有休息&#xff0c;心理上还是很难接受&#xff0c;也不是说周六上班有多累&#xff0c;就是没有找到对的感觉&#xff0c;就像和一个不喜欢的姑娘相亲&#xff0c;就算姑娘家财万贯&#xff0c;貌美如花&#xff0c;自己还是提不起兴趣&#xff0c;可能这就是…

复习Java_List_Set_HashSet原理_Collections使用_Comparator使用

复习Java_List_Set_HashSet原理_Collections使用_Comparator使用 主要内容 数据结构List集合Set集合Collections 第一章 数据结构 2.1 数据结构有什么用&#xff1f; 加快应用开发, 提高程序健壮性, 提高程序可重用性 2.2 常见的数据结构 数据存储的常用结构有&#xff…

最近,我也要买书了

1.当当网给我做的一个活动&#xff0c;购书优惠&#xff0c;确实是福利了&#xff0c;而且筛选的书籍对我们做嵌入式都是比较有帮助的&#xff0c;你应该知道&#xff0c;做活动的时候&#xff0c;价格都是比较不错&#xff0c;不要错过了时间再想要当时的价格&#xff0c;有需…

复习Java异常处理_异常分类_自定义异常_线程初步了解

复习Java异常处理_异常分类_自定义异常_线程 主要内容 异常、线程 教学目标 第一章 异常 1.1 异常概念 异常&#xff0c;就是不正常的意思。在生活中:医生说,你的身体某个部位有异常,该部位和正常相比有点不同,该部位的功能将受影响.在程序中的意思就是&#xff1a; 异常…

跟我师兄聊天引发的思考

我一个师兄&#xff0c;上大学的时候关系很好&#xff0c;我师兄也很喜欢打篮球&#xff0c;也许这就是我们有很多共同话题的原因&#xff0c;人是很奇怪&#xff0c;特别是做技术的&#xff0c;本来就比较闷&#xff0c;很难跟陌生人马上来电&#xff0c;但是如果有个共同爱好…

别忘了当初你为何出发

最近&#xff0c;特别喜欢听一个广播&#xff0c;宝安905&#xff0c;估计是因为我最近营养不太够&#xff0c;需要多喝一些鸡汤&#xff0c;刚好这个电台广播的鸡汤非常够味&#xff0c;非常符合我现在的胃口。比如这一句「你想收获世界上最好的东西&#xff0c;先让世界看到最…

一位跟我一样疑惑的同学~

今天遇到一个同学给我提的问题&#xff0c;非常像我之前的经历&#xff0c;想分享给大家&#xff0c;最近手机码字&#xff0c;觉得有用&#xff0c;给个在看或者转发&#xff0c;小弟感激不尽&#xff0c;在所不辞。~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~我2012年毕业&#xff…

在深圳转户口这件小事

我对于政策这种事情是比较感冒的&#xff0c;比如老师说不能去外面上网&#xff0c;我是绝不会偷偷跑出去的&#xff0c;来深圳的第一年&#xff0c;我就想着办理户口了&#xff0c;第一次机会是在 TCL 的时候&#xff0c;人事部门有专门给我们办理转深户的&#xff0c;不过那时…

CPU 和 GPU

端午回来&#xff0c;发的第一篇文章&#xff0c;还在谋划其他事情&#xff0c;大家共勉。现在电脑这么普及&#xff0c;应该每个人都知道 CPU &#xff0c;即使不懂电脑的人都能憋出大招告诉你电脑里面有一个 CPU&#xff0c;CPU 是中央处理器&#xff0c;是很多很多年积累下来…