java 多线程 工作笔记

                                                                            java 多线程  工作笔记

 
1. springBoot 和 springCloud  

 

Dubbo 采用单一长连接和 NIO 异步通讯(保持连接/轮询处理),
使用自定义报文的 TCP 协议,并且序列化使用定制 Hessian2 框架,
适合于小数据量大并发的服务调用,
以及服务消费者机器数远大于服务提供者机器数的情况,
但不适用于传输大数据的服务调用。
而 Spring Cloud 直接使用 HTTP 协议
(但也不是强绑定,也可以使用 RPC 库,
或者采用 HTTP 2.0 + 长链接方式(Fegin 可以灵活设置))。

 


      Dubbo  适合小数据量大并发的服务

 

      SpringCloud 适合传输大数据的服务

 


Dubbo 支持 RPC 调用,服务之间的调用性能会很好。
支持多种序列化协议,如 Hessian、HTTP、WebService。
Dobbo Admin后台管理功能强大,提供了路由规则、动态配置、访问控制、权重调节、均衡负载等功能。
在国内影响力比较大,中文社区文档较为全面。
阿里最近重启维护。

 


2.Dubbo A, RPC 调用,服务调用性能  B, 支持多种序列化协议 C,提供路由规则/动态配置/访问控制/权重调节/负载均衡

 

 

 

 

 


Registry 严重依赖第三方组件(zookeeper 或者 redis),当这些组件出现问题时,服务调用很快就会中断。
Dubbo 只支持 RPC 调用。使得服务提供方(抽象接口)与调用方在代码上产生了强依赖,服务提供者需要不断将包含抽象接口的 jar 包打包出来供消费者使用。一旦打包出现问题,就会导致服务调用出错,并且以后发布部署会成很大问题(太强的依赖关系)。
另外,以后要兼容 .NET Core 服务,Dubbo RPC 本身不支持跨语言(可以用跨语言 RPC 框架解决,比如 Thrift、gRPC(重复封装了),或者自己再包一层 REST 服务,提供跨平台的服务调用实现,但相对麻烦很多)
Dubbo 只是实现了服务治理,其他微服务框架并未包含,如果需要使用,需要结合第三方框架实现(比如分布式配置用淘宝的 Diamond、服务跟踪用京东的 Hydra,但使用相对麻烦些),开发成本较高,且风险较大。
社区更新不及时(虽然最近在疯狂更新),但也难免阿里以后又不更新了,就尴尬了。
主要是国内公司使用,但阿里内部使用 HSF,相对于 Spring Cloud,企业应用会差一些。

 

 

 

 

 

有强大的 Spring 社区、Netflix 等公司支持,并且开源社区贡献非常活跃。
标准化的将微服务的成熟产品和框架结合一起,Spring Cloud 提供整套的微服务解决方案,开发成本较低,且风险较小。
基于 Spring Boot,具有简单配置、快速开发、轻松部署、方便测试的特点。
支持 REST 服务调用,相比于 RPC,更加轻量化和灵活(服务之间只依赖一纸契约,不存在代码级别的强依赖),有利于跨语言服务的实现,以及服务的发布部署。另外,结合 Swagger,也使得服务的文档一体化。
提供了 Docker 及 Kubernetes 微服务编排支持。
国内外企业应用非常多,经受了大公司的应用考验(比如 Netfilx 公司),以及强大的开源社区支持。

 


3.  springClod  

 

     基于SpringBoot ,简单配置、快速开发、轻松部署、方便测试  
     支持REST 服务调用有利于跨语言服务实现  
     结合Swagger,服务文档一体化
     提供了 Docker 及 Kubernetes

 

 

 

 

 

支持 REST 服务调用,可能因为接口定义过轻,导致定义文档与实际实现不一致导致服务集成时的问题(可以使用统一文档和版本管理解决,比如 Swagger)。
另外,REST 服务调用性能会比 RPC 低一些(但也不是强绑定)
Spring Cloud 整合了大量组件,相关文档比较复杂,需要针对性的进行阅读。

 


4.SpringClod  缺点

 

            REST 服务调用性能比RPC性能低 ,处理大数据之间的服务
            SpringCloud 整合大量组件, 文档复杂

 

 

 


5.Dubbo 专注 RPC 和服务治理,Spring Cloud 则是一个微服务架构生态。

 

 

 


6.   需要学习的框架和深入的技术框架
集成ORM  hibernate ,jdo,toplink,apache ojb ,ibatis,jpa
集成视图  jsp ,jstl ,tiles,velocity, freemarker,xslt ,pdf/excel,jaseperreports
集成web框架  jsf struts tapestry webwork
整合其它J2EE  webserives,ejb jms jmx jca 邮件抽象层,定时调度和线程池,java注解
数据库 mysql ,sqlsever,oralce,sqlite,db2
Ajax框架  ext,jquery ,prototype,dojo,yui,
容器 Jboss,tomcat,weblogic

 

 

 

7.

 

目前很多中间件或者应用程序都会是通过命令窗口启动的,对于生产环境一定不要将CMD命令窗口模式设为编辑模式,因为这样会因为误操作而导致应用程序暂停。

 


对于较长SQl的语句,首先分析出此SQL是从哪里发出的,是程序还是存储过程,可以进行逻辑等价改写语句。针对长SQl性能调优时固化视图方案,针对只读效率是很高的,固化视图有写的操

 


作效率则跟不上的。

 


南XX系统DBA对数据进行awr报告分析时发现有一个SQL语句执行时间超过10000秒,对于这种耗时长的语句,需要首先分析物理读耗时,逻辑读耗时(逻辑读是CPU针对执行),CPU读耗时,是否

 


有递归SQL语句。如果前面三个读耗时都低则可能是SQL语句解析时间长。查看VPD策略,根据实际情况该语句是在非空间时间执行的一个执行计划job。通过10046跟踪执行计划日志。数据库的

 

 

 

 

 

8.

 

针对几十万条的数据快访问,放到内存中还是效率不足时,我们就要看看是索引块热,还是表块热。如果放在内存缓存中还是解决不了问题,就需要把这些块打散。减少同时访问一个块的概

 

率。

 

为了提高表的效率可以关闭表的checksum,dblock。反转索引和范围索引要谨慎使用。

 

.在登录数据库前,请记得查询v$database或v$instance;同样,登录服务器时,请记得执行hostname确认,以免将生产环境当成测试环境;

 

9.若数据库存在阻塞需要kill session时,请确认阻塞session,涉及后台进程时DBA不能擅自kill该session。
10.关闭数据库前,一定要停止JOB,同时检查v$transaction,确保没有事务运行,然后手工执行一次检查点(Altersystem checkpoint);停止数据库时,严禁使用shutdown abort。
11.在服务器上执行rm –rf前,请记得执行pwd确认,要知道,这条命令可能会导致巨大的灾难。

 


12.

 

   Loadrunner负载测试Webservice接口

 


13.   可以尝试考取Oracle的DBA认证,分为OCA、OCP和OCM三级。

 


14. 消息队列的应用场景?

 

跨系统的调用,异步性质的调用最佳。
高并发问题,利用队列串行特点。
订阅模式,数据被未知数量的消费者订阅,比如某种数据的变更会影响多个系统的数据,订单数据就是比较好理解的。

 

 

 

/** 用户策略组类型:1.自动屏蔽词 2审核屏蔽词 3系统黑名单 4屏蔽地区 5系统白名单 6系统白签名 **/
 public static final int USER_STRATEGY_GROUP_SYS_BLACK_WORDS_AUTO = 1;
 public static final int USER_STRATEGY_GROUP_SYS_BLACK_WORDS_CHECK = 2;
 public static final int USER_STRATEGY_GROUP_SYS_BLACK_MOBILE = 3;
 // public static final int USER_STRATEGY_GROUP_SYS_BLACK_LOCATION = 4;
 public static final int USER_STRATEGY_GROUP_SYS_WHITE_MOBILE = 5;
 public static final int USER_STRATEGY_GROUP_SYS_WHITE_SIGN = 6;
 public static final int USER_STRATEGY_GROUP_SYS_BLACK_SIGN = 7;

 

 

 


15. 锁 (1.Java synchronized 、lock writeLock、readLock 2.数据库 悲观锁和乐观锁 )

 

 

 

CopyOnWriteArrayList、CopyOnWriteArraySet

 

rrayList、LinkedList、HashMap等都是线程非安全的类

 


:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着"某条线程"指的是当前线程

 

18、synchronized和ReentrantLock的区别

 

synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上:

 

(1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁

 

(2)ReentrantLock可以获取各种锁的信息

 

(3)ReentrantLock可以灵活地实现多路通知

 

另外,二者的锁机制其实也是不一样的。ReentrantL

 

另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定

 

Hashtable

 


20、ReadWriteLock是什么

 

首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。

 

因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。

 

 

 

22、Linux环境下如何查找哪个线程使用CPU最长

 

这是一个比较偏实践的问题,这种问题我觉得挺有意义的。可以这么做:

 

(1)获取项目的pid,jps或者ps -ef | grep java,这个前面有讲过

 

(2)top -H -p pid,顺序不能改变

 

这样就可以打印出当前的项目,每条线程占用CPU时间的百分比。注意这里打出的是LWP,也就是操作系统原生线程的线程号,我笔记本山没有部署Linux环境下的Java工程,因此没有办法截图演示,网友朋友们如果公司是使用Linux环境部署项目的话,可以尝试一下。

 

使用"top -H -p pid"+"jps pid"可以很容易地找到某条占用CPU高的线程的线程堆栈,从而定位占用CPU高的原因,一般是因为不当的代码操作导致了死循环。

 

最后提一点,"top -H -p pid"打出来的LWP是十进制的,"jps pid"打出来的本地线程号是十六进制的,转换一下,就能定位到占用CPU高的线程的当前线程堆栈了。

 

 

 


死锁:一、数据库发生死锁:在数据库系统的设计中考虑了监测死锁以及从死锁中恢复,
       数据库如果监测到了一组事务发生了死锁时,
       将选择一个牺牲者并放弃这个事务

 

      二、代码层面发生死锁:当一组Java线程发生死锁时,这两个线程就永远不能再使用了,
         并且由于两个线程分别持有了两个锁,
         那么这两段同步代码/代码块也无法再运行了----
        
        
        
        
        
        
        
40、高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?

 

这是我在并发编程网上看到的一个问题,把这个问题放在最后一个,希望每个人都能看到并且思考一下,因为这个问题非常好、非常实际、非常专业。关于这个问题,个人看法是:

 

(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

 

(2)并发不高、任务执行时间长的业务要区分开看:

 

  a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务

 

  b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

 

(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。

 


高并发、业务处理时间长短特点  解决办法

 


 一、  (一)高并发、业务执行时间短 : 业务=====》【处理逻辑】   线程池使用:  线程数设置为 CPU核数 +1 、减少线程上下文切换
 
 
       (二)并发不高、业务执行时间长: 业务====》【处理逻辑】  线程池使用:  

 


                      业务时间集中点: (1) IO   =》IO属于计算机系统管理, 与cpu无关系, 加大线程数量,高效利用CPU
                                                                            
                        (2) CPU 操作/计算密集型任务   cpu核数 + 1
           
  (三) 并发高、业务执行时间长
      
                      业务数据分析、业务处理分析:A: 制作缓存数据 ————》启动缓存服务器【redis 集群】、【rabittMQ业务数据】  (业务拆分应对高并发)
                                     
                 B:  服务器扩容  —————》 nginx 集群 、oracle集群、 websphere/weblogic 集群 

 

                  (docker镜像) -->【kubernetes】《====一键式扩容机制====》  
                 
                  监控内存与数据量  》》策略性的路由转发  (项目临时版本发布) 《====(1)
                 
                  检测各个mq ,redis ,mongoDB缓存容量/数据量 目的进行路由转发策略   简称通道   代码+加壳  =中级版 《===== (2)   
                 
                 C: oracle 表分区,分库   【数据库读写分离】      
   
   
   
   
   
多线程作用:A: 发挥多核CPU优势 (多核cpu上的多线程,它能让你的多段逻辑同时工作) B: 防止阻塞 (单核cpu应用多线程防止阻塞/线程上下文切换) C: 便于建模 (大任务A--> 解耦成:B、C、D建立程序模型)
               
       
       
线程创建: (1)继承Thread 类            start() 才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行/如果调用run()方法,代码是同步执行,必须等待一个线程的run()方法代码全部执行完毕,另外一个线程才可以执行run()方法

 


           (2)实现Runnable接口         run()方法返回值是 void,纯碎的执行run()方法

 

           (3)实现Callable接口   call()方法返回值是 一个泛型   Callable +Future/FutureTask 获取异步执行的结果【1.线程是否执行?2.线程执行时长?3.取消该任务】
    
    
 CyclicBarrier /CountDownLatch  代码运行到某个点上:

 

             CyclicBarrier 某个线程运行到某个点上,该线程停止运行,直到所有的线程都到达这个点,所有的线程才重新运行 仅能唤起一个任务
    
    CountDownLatch 某线程运行到某个点上之后,只是给某个数值-1 ,该线程继续运行 计数=0,CountDownLatch不可用 可以唤起多个任务  
       
       
 线程安全 (1) 不可变 :String 、Integer、Long 是final类型 
 
          (2) 绝对线程安全:java CopyOnWriteArrayList、 CopyOnWriteArraySet

 

              (3)  相对线程安全:Vector 有个线程在遍历某个Vector 、有个线程同时在add这个Verctor 出现ConcurrentModificationException    
       
     (4)  线程非安全: ArrayList、LinkedList、 HashMap 是线程非安全的类   
       
       
java 中获取线程dump文件:

 

               死循环、死锁、阻塞、页面打开慢,打线程dump是最好的解决问题的途径
               线程dump 是线程堆栈

 

               A:get 到线程的pid ,jps命令(windowns)/ ps -ef | grep java (linux)

 

               B: 打印线程堆栈,jstack(windowns)  /   kill -3 pid (linux)          
       
线程遇到运行时异常: 若果异常没有捕获,该线程停止执行 如果这个线程持有某个对象的监视器,这个对象监视器被立即释放  

 

线程共享数据: 线程之间共享对象,通过wait/notify/notifyAll   await/signal/signalAll   唤起和等待 
    阻塞队列BlockingQueue :线程之间共享数据而设计的

 


sleep 与wait :
       
   A相同点:都可以放弃CPU一定的时间
  
   B不同点: 如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法放弃这个对象的监视器
    
       
  生产者消费者模型作用:
  
                    A:通过平衡生产者的生产能力和消费者的能力来提升整个系统的运行效率
       
        B:解耦
       
       
ThreadLocal作用:  空间换时间, Thread 中 ThreadLocal.ThreadLocalMap ,数据进行隔离,数据不共享  

 

jdk 强制wait()方法和notify()/notifyAll() 在调用前必须先获得对象的锁

 

区别  wait()方法和notify/notifyAll() 方法放弃对象监视器: wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕放弃对象监视器
     
线程池:避免频繁的创建和销毁线程达到线程对象的重用,控制并发的数目  

 

检测线程持有对象监视器:Thread 类提供一个 holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,这是个static方法意味着某条线程指的是当前线程

 


synchronized 和ReentrantLock 区别:

 

               本质区别, synchronized 是关键字, ReentrantLock 是类,提供了更灵活的特性,可以被继承、可以有方法
     
      A: ReentrantLock 可以获取锁的等待时间进行设置,避免了死锁
     
      B:  ReentrantLock  可以获取各种锁的信息
     
      C:  ReentrantLock  可以实现多路通知、ReentrantLock锁的机制不同
     
ConcurrentHashMap 并发度:ConcurrentHashMap 并发度是segment的大小,默认是16,最多同时有16条线程操作ConcurrentHashMap
                         
                                 
ReadWriteLock:    局限性,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据 
                  
                  读数据是不会改变数据的,没有必要加锁,但是还是加锁了降低了程序的性能 

 

                   基于ReadWriteLock 局限性诞生了ReadWriteLock 是一个读写锁接口 , ReentrantReadWriteLock 是ReadWriteLock 接口的一个具体实现,
      
       实现了读写分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写,写和读、写和写之间才会互斥,提升读写的性能   
     
             
FutureTask :   表示一个异步运算的任务, FutureTask 传入一个Callable 的具体实现类,可以对这个异步运算的任务的结果进行等待获取,
              
               判断是否已经完成、取消任务等操作  ,由于FutureTask 也是Runnable接口的实现类,FutureTask 可以放入线程池中   
     

 

查找线程对CPU使用时长: 

 


                   A:get 项目的pid ,jps(windowns) ps -ef | grep java  (linux)

 

                   B:  top  -H -p pid  (顺序不能变) 
                   
       使用 top -H -p  pid  找到某条占用CPU高的线程的线程堆栈(百分比),从而定位占用CPU高的原因 ,一般是因为不当代码操作导致四循环
      
       top  -H -p pid  打出来的LWP 是十进制的,   jps pid 打印出来的 本地线程号是十六进制的需要转换

 

 唤醒阻塞线程: A:如果线程是因为调用wait()、 sleep()或者join()方法而导致的阻塞,可以中断线程并且通过抛出 InterruptedException来唤醒

 

                B: 如果线程遇到了IO阻塞,无能为力(因为IO是操作系统实现的,java代码并没有办法直接接触操作系统)
    
    
不可变对象对多线程作用: 不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率

 

线程上下文切换: CPU 控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU 执行的线程的过程

 


提交任务时,线程池队列已满,这时会发生:

 

                                    A:如果使用LinkedBlockingQueue(无界队列),继续添加任务到阻塞队列中等待执行   -------LinkedBlockingQueue 可以近乎认为是一个无穷大的队列,可以无限存放任务

 

                                    B:如果使用的是有界队列(ArrayBlockingQueue),任务首先会被添加到ArrayBlockingQueue中,
            ArrayBlockingQueue 满,则使用拒绝策略(RejectedExecutionHandler)处理满的任务,默认AbortPolicy

 

 

 


java 中用到的线程调度算法: 抢占式, 一个线程用完CPU之后,操作系统根据线程优先级、线程饥饿情况等 算出一个总的优先级分配下一个时间片给某个线程执行

 

Thread.sleep(0) 作用: 由于java采用抢占式的线程调度算法,因此可能会出极限某条线程常常获取到CPU 控制权的情况,
                     
        为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0) 手动触发一次操作系统分配时间片的操作,也是平衡CPU控制权的一种操作
                     
                     
       自旋: (自旋是一种策略) 很多synchronized 里面的代码只是一些简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得操作,
                            
                               因为线程阻塞涉及到用户态和内核态切换的问题,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界做忙循环,这就是自旋
                             
                               如果做了多次忙循环发现还没有获得所,在阻塞       
乐观锁和悲观锁:
 
               (1) 乐观锁,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,它不要持有锁,
                         将比较---替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则发生冲突,

 

               (2) 悲观锁, 对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,每次对某资源进行操作时,都会持有一个独占所,      

 

 

 

CAS : (Compare and Swap) 即:比较--替换 ,三个操作数 内存制V 、旧的预期值A 、  要修改的值B , 当且仅当预期值A 和内存值V相同时,才会将内存值修改为B 并返回true,否则什么也不做并返回false(cas he volatile 配合)
   
AQS : (AbstractQueuedSychronizer) 抽象队列同步器 

 


semaphore 作用: 是一个信号量,作用是限制某段代码的并发数。Semaphore 有一个构造函数,传入一个int整数n, 表示某段代码做多只n 个线程可以访问
                 ,如果超出了n,请等待,等到某个线程执行完毕这段代码块,下个线程再进入
    
    
Hashtable size() 方法中明明只有一条语句 return count 还要继续同步:

 

                       A: 同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程同时访问
       
        B: CPU 执行代码,执行的不是java代码,

 

 

 

线程类的构造方法、静态块是被那个线程调用的:

 

                             线程类的构造方法、静态块是被new 这个线程类所在的线程所调用的,而run方法的代码才是被线程自身所调用
  
  
  
同步方法和同步快:  同步块之外的代码是异步执行的, 比同步整个方法更能提升代码的效率  (同步范围越小越好)----锁粗化【StringBuffer】

 

 

 

 


   

本文档是本人5年JAVA经验整理的文档(持续性的),由于CSDN只能单个附件只能上传15MB,所以只好分包压缩了(4个包),希望各位同行能够理解。 本文档包括JAVA/FLEX/数据建模/Database/移动办公/项目管理/单点登录/软件架构/测试等相关性技术。 本文档还在持续维护中,各位可以通过《笔记变更说明》查看升级日志。 在这里声明下,本文档一部分是个人从学习、工作中整理出来的(代码都是经过调试的),还有一部分是从网上整理的。 本站相关文档都是经过后期整理的,或是在实际工作中整理的实例代码总结而成的。 由于本人目前允许上传的资源太小,所以无法上传整个的技术学习笔记JAVA 五年的工作经验和学习笔记),待以后有机会会分享给大家。 技术体系包括: J2SE/J2ME/J2EE/JAVA代码优化/Flex(BlazeDS、PureMVC等技术)/LDAP/C++/Portal/即时通讯/数据建模/UML/UML设计工具(Rose、EA、PD等)/移动办公(Android、Symbian、Wap等技术)/项目管理(敏捷开发等)/软件架构(NoSQL、SaaS、设计模式等)/数据库(MySQL、Oracle、EDB、SQLServer等)/测试(单元测试、压力测试)/linux服务器等 其中含有大量实例源代码。 这里需要说明的时,该文档是本人5年工作经验的积累,文档中大部分知识点来源于实际工作中的总结,(除了JAR等资源文件外)其代码都是可运行的,还有一部分知识来源于网络或者其他书籍,这里做一些收集,使该体系更加完善。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值