首页 文章 精选 留言 我的

精选列表

搜索[面试],共4916篇文章
优秀的个人博客,低调大师

想要金九银十面试通关,不懂 Java多线程肯定是不行的!

作者 | 纳达丶无忌 如果对什么是线程、什么是进程仍存有疑惑,请先 Google 之,因为这两个概念不在本文的范围之内。用多线程只有一个目的,那就是更好的利用 CPU 的资源,因为所有的多线程代码都可以用单线程来实现。说这个话其实只有一半对,因为反应“多角色”的程序代码,最起码每个角色要给他一个线程吧,否则连实际场景都无法模拟,当然也没法说能用单线程来实现:比如最常见的“生产者,消费者模型”。很多人都对其中的一些概念不够明确,如同步、并发等等,让我们先建立一个数据字典,以免产生误会。多线程:指的是这个程序(一个进程)运行时产生了不止一个线程并行与并发:并行:多个 CPU 实例或者多台机器同时执行一段处理逻辑,是真正的同时。并发:通过 CPU 调度算法,让用户看上去同时执行,实际上从 CPU 操作层面不是真正的同时。并发往往在场景中有公用的资源,那么针对这个公用的资源往往产生瓶颈,我们会用 TPS 或者 QPS 来反应这个系统的处理能力。 线程安全:经常用来描绘一段代码。指在并发的情况之下,该代码经过多线程使用,线程的调度顺序不影响任何结果。这个时候使用多线程,我们只需要关注系统的内存,CPU 是不是够用即可。反过来,线程不安全就意味着线程的调度顺序会影响最终结果,如不加事务的转账代码: void transferMoney(User from, User to, float amount){ to.setMoney(to.getBalance() + amount); from.setMoney(from.getBalance() - amount); } 同步:Java 中的同步指的是通过人为的控制和调度,保证共享资源的多线程访问成为线程安全,来保证结果的准确。如上面的代码简单加入 @synchronized 关键字。在保证结果准确的同时,提高性能,才是优秀的程序。线程安全的优先级高于性能。 好了,让我们开始吧。我准备分成几部分来总结涉及到多线程的内容: 扎好马步:线程的状态 内功心法:每个对象都有的方法(机制) 太祖长拳:基本线程类 九阴真经:高级多线程控制类扎好马步:线程的状态 先来两张图: 各种状态一目了然,值得一提的是 "Blocked" 和 "Waiting" 这两个状态的区别:线程在 Running 的过程中可能会遇到阻塞 (Blocked) 情况 对 Running 状态的线程加同步锁 (Synchronized) 使其进入 (lock blocked pool),同步锁被释放进入可运行状 (Runnable)。从 jdk 源码注释来看,blocked 指的是对 monitor 的等待(可以参考下文的图)即该线程位于等待区。 线程在 Running 的过程中可能会遇到等待(Waiting)情况线程可以主动调用 object.wait 或者 sleep,或者 join(join内部调用的是 sleep ,所以可看成 sleep 的一种)进入。从 jdk 源码注释来看,Waiting 是等待另一个线程完成某一个操作,如 join 等待另一个完成执行,object.wait() 等待object.notify() 方法执行。 Waiting 状态和 Blocked 状态有点费解,我个人的理解是:Blocked 其实也是一种 wait ,等待的是 monitor ,但是和Waiting 状态不一样,举个例子,有三个线程进入了同步块,其中两个调用了 object.wait(),进入了 Waiting 状态,这时第三个调用了 object.notifyAll() ,这时候前两个线程就一个转移到了 Runnable,一个转移到了 Blocked。从下文的 monitor 结构图来区别:每个 Monitor 在某个时刻,只能被一个线程拥有,该线程就是 “Active Thread”,而其它线程都是 “Waiting Thread”,分别在两个队列 “ Entry Set” 和 “Wait Set” 里面等候。在 “Entry Set” 中等待的线程状态 Blocked,从 jstack 的dump 中来看是 “Waiting for monitor entry”,而在 “Wait Set” 中等待的线程状态是 Waiting,表现在 jstack 的 dump 中是 “in Object.wait()”。此外,在 runnable 状态的线程是处于被调度的线程,此时的调度顺序是不一定的。Thread 类中的 yield 方法可以让一个 running 状态的线程转入 runnable。内功心法:每个对象都有的方法(机制)synchronized, wait, notify 是任何对象都具有的同步工具。让我们先来了解他们 他们是应用于同步问题的人工线程调度工具。讲其本质,首先就要明确 monitor 的概念,Java 中的每个对象都有一个监视器,来监测并发代码的重入。在非多线程编码时该监视器不发挥作用,反之如果在 synchronized 范围内,监视器发挥作用。wait/notify 必须存在于 synchronized 块中。并且,这三个关键字针对的是同一个监视器(某对象的监视器)。这意味着 wait之后,其他线程可以进入同步块执行。当某代码并不持有监视器的使用权时(如图中5的状态,即脱离同步块)去 wait 或 notify,会抛出java.lang.IllegalMonitorStateException。也包括在 synchronized 块中去调用另一个对象的 wait/notify,因为不同对象的监视器不同,同样会抛出此异常。再讲用法:synchronized 单独使用:代码块:如下,在多线程环境下,synchronized 块中的方法获取了 lock 实例的 monitor,如果实例相同,那么只有一个线程能执行该块内容 public class Thread1 implements Runnable { Object lock; public void run() { synchronized(lock){ ..do something } } } 直接用于方法:相当于上面代码中用 lock 来锁定的效果,实际获取的是 Thread1 类的 monitor。更进一步,如果修饰的是 static 方法,则锁定该类所有实例 public class Thread1 implements Runnable { public synchronized void run() { ..do something } } synchronized, wait, notify 结合:典型场景生产者消费者问题 /** * 生产者生产出来的产品交给店员 */ public synchronized void produce() { if(this.product >= MAX_PRODUCT) { try { wait(); System.out.println("产品已满,请稍候再生产"); } catch(InterruptedException e) { e.printStackTrace () ; } return; } this.product++; System.out.println("生产者生产第" + this.product + "个产品."); notifyAll(); //通知等待区的消费者可以取出产品了 } /** * 消费者从店员取产品 */ public synchronized void consume() { if(this.product <= MIN_PRODUCT) { try { wait(); System.out.println("缺货,稍候再取"); } catch (InterruptedException e) { e.printStackTrace(); } return; } System.out.println("消费者取走了第" + this.product + "个产品."); this.product--; notifyAll(); //通知等待去的生产者可以生产产品了 } volatile多线程的内存模型:main memory(主存)、working memory(线程栈),在处理数据时,线程会把值从主存 load 到本地栈,完成操作后再 save 回去 (volatile 关键词的作用:每次针对该变量的操作都激发一次 load and save) 。 针对多线程使用的变量如果不是 volatile 或者 final 修饰的,很有可能产生不可预知的结果(另一个线程修改了这个值,但是之后在某线程看到的是修改之前的值)。其实道理上讲同一实例的同一属性本身只有一个副本。但是多线程是会缓存值的,本质上,volatile 就是不去缓存,直接取值。在线程安全的情况下加 volatile 会牺牲性能。太祖长拳:基本线程类基本线程类指的是 Thread 类,Runnable 接口,Callable 接口Thread 类实现了 Runnable 接口,启动一个线程的方法: MyThread my = new MyThread(); my.start(); Thread类相关方法 //当前线程可转让 cpu 控制权,让别的就绪状态线程运行(切换) public static Thread.yield() //暂停一段时间 public static Thread.sleep() //在一个线程中调用 other.join(),将等待other执行完后才继续本线程。 public join() //后两个函数皆可以被打断 public interrupte() 关于中断:它并不像 stop 方法那样会中断一个正在运行的线程。线程会不时地检测中断标识位,以判断线程是否应该被中断(中断标识值是否为 true )。终端只会影响到 wait 状态、sleep 状态和 join 状态。被打断的线程会抛出 InterruptedException。Thread.interrupted() 检查当前线程是否发生中断,返回booleansynchronized 在获锁的过程中是不能被中断的。中断是一个状态!interrupt()方法只是将这个状态置为 true 而已。所以说正常运行的程序不去检测状态,就不会终止,而 wait 等阻塞方法会去检查并抛出异常。如果在正常运行的程序中添加while(!Thread.interrupted()) ,则同样可以在中断后离开代码体Thread类最佳实践:写的时候最好要设置线程名称 Thread.name,并设置线程组 ThreadGroup,目的是方便管理。在出现问题的时候,打印线程栈 (jstack -pid) 一眼就可以看出是哪个线程出的问题,这个线程是干什么的。如何获取线程中的异常 Runnable与 Thread 类似Callablefuture 模式:并发模式的一种,可以有两种形式,即无阻塞和阻塞,分别是 isDone 和 get。其中 Future 对象用来存放该线程的返回值以及状态 ExecutorService e = Executors.newFixedThreadPool(3); //submit 方法有多重参数版本,及支持 callable 也能够支持runnable 接口类型. Future future = e.submit(new myCallable()); future.isDone() //return true,false 无阻塞 future.get() // return 返回值,阻塞直到该线程运行结束 九阴真经:高级多线程控制类以上都属于内功心法,接下来是实际项目中常用到的工具了,Java1.5 提供了一个非常高效实用的多线程包: java.util.concurrent, 提供了大量高级工具,可以帮助开发者编写高效、易维护、结构清晰的 Java 多线程程序。1.ThreadLocal类用处:保存线程的独立变量。对一个线程类(继承自 Thread )当使用 ThreadLocal 维护变量时,ThreadLocal 为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。常用于用户登录控制,如记录 session 信息。实现:每个Thread 都持有一个 TreadLocalMap 类型的变量(该类是一个轻量级的 Map,功能与 map 一样,区别是桶里放的是 entry 而不是 entry 的链表。功能还是一个 map 。)以本身为 key,以目标为 value。主要方法是 get() 和 set(T a),set 之后在 map 里维护一个threadLocal -> a,get 时将 a 返回。ThreadLocal 是一个特殊的容器。2.原子类(AtomicInteger、AtomicBoolean……)如果使用 atomic wrapper class 如 atomicInteger,或者使用自己保证原子的操作,则等同于 synchronized //返回值为 boolean AtomicInteger.compareAndSet(int expect,int update) 该方法可用于实现乐观锁,考虑文中最初提到的如下场景:a 给 b 付款10元,a 扣了 10 元,b 要加 10 元。此时 c 给 b 2 元,但是 b的加十元代码约为: if(b.value.compareAndSet(old, value)){ return ; }else{ //try again // if that fails, rollback and log } AtomicReference对于 AtomicReference 来讲,也许对象会出现,属性丢失的情况,即 oldObject == current,但是 oldObject.getPropertyA != current.getPropertyA。这时候,AtomicStampedReference 就派上用场了。这也是一个很常用的思路,即加上版本号3.Lock类lock: 在 java.util.concurrent 包内。共有三个实现:ReentrantLockReentrantReadWriteLock.ReadLockReentrantReadWriteLock.WriteLock 主要目的是和 synchronized 一样, 两者都是为了解决同步问题,处理资源争端而产生的技术。功能类似但有一些区别。区别如下:1.lock 更灵活,可以自由定义多把锁的枷锁解锁顺(synchronized 要按照先加的后解顺序)2.提供多种加锁方案,lock 阻塞式, trylock 无阻塞式, lockInterruptily 可打断式, 还有 trylock 的带超时时间版本3.本质上和监视器锁(即 synchronized 是一样的)4.能力越大,责任越大,必须控制好加锁和解锁,否则会导致灾难。5.和 Condition 类的结合。6.性能更高,对比如下图: ReentrantLock可重入的意义在于持有锁的线程可以继续持有,并且要释放对等的次数后才真正释放该锁。使用方法是:1.先 new 一个实例 static ReentrantLock r=new ReentrantLock(); 2.加锁 r.lock()或 r.lockInterruptibly(); 此处也是个不同,后者可被打断。当 a 线程 lock 后,b 线程阻塞,此时如果是 lockInterruptibly,那么在调用 b.interrupt() 之后,b 线程退出阻塞,并放弃对资源的争抢,进入 catch 块。(如果使用后者,必须 throw interruptable exception 或 catch)3.释放锁 r.unlock() 必须做!何为必须做呢,要放在 finally 里面。以防止异常跳出了正常流程,导致灾难。这里补充一个小知识点,finally 是可以信任的:经过测试,哪怕是发生了 OutofMemoryError ,finally 块中的语句执行也能够得到保证。ReentrantReadWriteLock可重入读写锁(读写锁的一个实现) ReentrantReadWriteLock lock = new ReentrantReadWriteLock() ReadLock r = lock.readLock(); WriteLock w = lock.writeLock(); 两者都有 lock,unlock 方法。写写,写读互斥;读读不互斥。可以实现并发读的高效线程安全代码4.容器类这里就讨论比较常用的两个:BlockingQueueConcurrentHashMap BlockingQueue阻塞队列。该类是 java.util.concurrent 包下的重要类,通过对 Queue 的学习可以得知,这个 queue 是单向队列,可以在队列头添加元素和在队尾删除或取出元素。类似于一个管道,特别适用于先进先出策略的一些应用场景。普通的 queue 接口主要实现有 PriorityQueue(优先队列),有兴趣可以研究BlockingQueue 在队列的基础上添加了多线程协作的功能: 除了传统的 queue 功能(表格左边的两列)之外,还提供了阻塞接口 put 和 take,带超时功能的阻塞接口 offer 和 poll。put 会在队列满的时候阻塞,直到有空间时被唤醒;take 在队 列空的时候阻塞,直到有东西拿的时候才被唤醒。用于生产者-消费者模型尤其好用,堪称神器。常见的阻塞队列有:ArrayListBlockingQueueLinkedListBlockingQueueDelayQueueSynchronousQueue ConcurrentHashMap高效的线程安全哈希 map。请对比 hashTable , concurrentHashMap, HashMap5.管理类管理类的概念比较泛,用于管理线程,本身不是多线程的,但提供了一些机制来利用上述的工具做一些封装。了解到的值得一提的管理类:ThreadPoolExecutor 和 JMX框架下的系统级管理类 ThreadMXBeanThreadPoolExecutor如果不了解这个类,应该了解前面提到的 ExecutorService,开一个自己的线程池非常方便 ExecutorService e = Executors.newCachedThreadPool(); ExecutorService e =Executors.newSingleThreadExecutor(); ExecutorService e = Executors.newFixedThreadPool(3); // 第一种是可变大小线程池,按照任务数来分配线程, // 第二种是单线程池,相当于 FixedThreadPool(1) // 第三种是固定大小线程池。 // 然后运行 e.execute(new MyRunnableImpl()); 该类内部是通过 ThreadPoolExecutor 实现的,掌握该类有助于理解线程池的管理,本质上,他们都是 ThreadPoolExecutor 类的各种实现版本。请参见 javadoc: 翻译一下:corePoolSize: 池内线程初始值与最小值,就算是空闲状态,也会保持该数量线程。maximumPoolSize: 线程最大值,线程的增长始终不会超过该值。keepAliveTime: 当池内线程数高于 corePoolSize 时,经过多少时间多余的空闲线程才会被回收。回收前处于 wait 状态unit:时间单位,可以使用 TimeUnit 的实例,如 TimeUnit.MILLISECONDS workQueue: 待入任务(Runnable)的等待场所,该参数主要影响调度策略,如公平与否,是否产生饿死 (starving)threadFactory: 线程工厂类,有默认实现,如果有自定义的需要则需要自己实现 ThreadFactory 接口并作为参数传入。 欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!

优秀的个人博客,低调大师

你要的Java并发面试题都在这里,20000字答案解析

1、在java中守护线程和本地线程区别?java中的线程分为两种:守护线程(Daemon)和用户线程(User)。任何线程都可以设置为守护线程和用户线程,通过方法Thread.setDaemon(bool on);true则把该线程设置为守护线程,反之则为用户线程。Thread.setDaemon()必须在Thread.start()之前调用,否则运行时会抛出异常。两者的区别: 唯一的区别是判断虚拟机(JVM)何时离开,Daemon是为其他线程提供服务,如果全部的User Thread已经撤离,Daemon 没有可服务的线程,JVM撤离。也可以理解为守护线程是JVM自动创建的线程(但不一定),用户线程是程序创建的线程;比如JVM的垃圾回收线程是一个守护线程,当所有线程已经撤离,不再产生垃圾,守护线程自然就没事可干了,当垃圾回收线程是Java虚拟机上仅剩的线程时,Java虚拟机会自动离开。扩展:Thread Dump打印出来的线程信息,含有daemon字样的线程即为守护进程,可能会有:服务守护进程、编译守护进程、windows下的监听Ctrl+break的守护进程、Finalizer守护进程、引用处理守护进程、GC守护进程。 2、线程与进程的区别?进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元。一个程序至少有一个进程,一个进程至少有一个线程。 3、什么是多线程中的上下文切换?多线程会共同使用一组计算机上的CPU,而线程数大于给程序分配的CPU数量时,为了让各个线程都有执行的机会,就需要轮转使用CPU。不同的线程切换使用CPU发生的切换数据等就是上下文切换。 4、死锁与活锁的区别,死锁与饥饿的区别?死锁:是指两个或两个以上的进程(或线程)在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。 产生死锁的必要条件: 互斥条件:所谓互斥就是进程在某一时间内独占资源。请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 不剥夺条件:进程已获得资源,在末使用完之前,不能强行剥夺。 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。 活锁:任务或者执行者没有被阻塞,由于某些条件没有满足,导致一直重复尝试,失败,尝试,失败。活锁和死锁的区别在于,处于活锁的实体是在不断的改变状态,所谓的“活”, 而处于死锁的实体表现为等待;活锁有可能自行解开,死锁则不能。饥饿:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。Java中导致饥饿的原因: 高优先级线程吞噬所有的低优先级线程的CPU时间。 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。 线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的wait方法),因为其他线程总是被持续地获得唤醒。 5、Java中用到的线程调度算法是什么?采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优先级上,如非特别需要,尽量不要用,防止线程饥饿。 6、什么是线程组,为什么在Java中不推荐使用?ThreadGroup类,可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式。为什么不推荐使用?因为使用有很多的安全隐患吧,没有具体追究,如果需要使用,推荐使用线程池。 7、为什么使用Executor框架?每次执行任务创建线程 new Thread()比较消耗性能,创建一个线程是比较耗时、耗资源的。调用 new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。接使用new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。 8、在Java中Executor和Executors的区别?Executors 工具类的不同方法按照我们的需求创建了不同的线程池,来满足业务的需求。Executor 接口对象能执行我们的线程任务。 ExecutorService接口继承了Executor接口并进行了扩展,提供了更多的方法我们能获得任务执行的状态并且可以获取任务的返回值。使用ThreadPoolExecutor 可以创建自定义线程池。Future 表示异步计算的结果,他提供了检查计算是否完成的方法,以等待计算的完成,并可以使用get()方法获取计算的结果。 9、什么是原子操作?在Java Concurrency API中有哪些原子类(atomic classes)?原子操作(atomic operation)意为”不可被中断的一个或一系列操作” 。 处理器使用基于对缓存加锁或总线加锁的方式来实现多处理器之间的原子操作。 在Java中可以通过锁和循环CAS的方式来实现原子操作。CAS操作——Compare & Set,或是 Compare & Swap,现在几乎所有的CPU指令都支持CAS的原子操作。原子操作是指一个不受其他操作影响的操作任务单元。原子操作是在多线程环境下避免数据不一致必须的手段。 int++并不是一个原子操作,所以当一个线程读取它的值并加1时,另外一个线程有可能会读到之前的值,这就会引发错误。为了解决这个问题,必须保证增加操作是原子的,在JDK1.5之前我们可以使用同步技术来做到这一点。到JDK1.5,java.util.concurrent.atomic包提供了int和long类型的原子包装类,它们可以自动的保证对于他们的操作是原子的并且不需要使用同步。java.util.concurrent这个包里面提供了一组原子类。其基本的特性就是在多线程环境下,当有多个线程同时执行这些类的实例包含的方法时,具有排他性,即当某个线程进入方法,执行其中的指令时,不会被其他线程打断,而别的线程就像自旋锁一样,一直等到该方法执行完成,才由JVM从等待队列中选择一个另一个线程进入,这只是一种逻辑上的理解。原子类:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference 原子数组:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray 原子属性更新器:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater 解决ABA问题的原子类:AtomicMarkableReference(通过引入一个boolean来反映中间有没有变过)AtomicStampedReference(通过引入一个int来累加来反映中间有没有变过) 10、Java Concurrency API中的Lock接口(Lock interface)是什么?对比同步它有什么优势?Lock接口比同步方法和同步块提供了更具扩展性的锁操作。他们允许更灵活的结构,可以具有完全不同的性质,并且可以支持多个相关类的条件对象。它的优势有:可以使锁更公平可以使线程在等待锁的时候响应中断可以让线程尝试获取锁,并在无法获取锁的时候立即返回或者等待一段时间可以在不同的范围,以不同的顺序获取和释放锁 整体上来说Lock是synchronized的扩展版,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition方法)锁操作。另外Lock的实现类基本都支持非公平锁(默认)和公平锁,synchronized只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。 11、什么是Executors框架?Executor框架是一个根据一组执行策略调用,调度,执行和控制的异步任务的框架。无限制的创建线程会引起应用程序内存溢出。所以创建一个线程池是个更好的的解决方案,因为可以限制线程的数量并且可以回收再利用这些线程。利用Executors框架可以非常方便的创建一个线程池。 12、什么是阻塞队列?阻塞队列的实现原理是什么?如何使用阻塞队列来实现生产者-消费者模型?阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。JDK7提供了7个阻塞队列。分别是: ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。 LinkedBlockingQueue :一个由链表结构组成的有界阻塞队列。 PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。 DelayQueue:一个使用优先级队列实现的无界阻塞队列。 SynchronousQueue:一个不存储元素的阻塞队列。 LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。 LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。 Java 5之前实现同步存取时,可以使用普通的一个集合,然后在使用线程的协作和线程同步可以实现生产者,消费者模式,主要的技术就是用好,wait ,notify,notifyAll,sychronized这些关键字。而在java 5之后,可以使用阻塞队列来实现,此方式大大简少了代码量,使得多线程编程更加容易,安全方面也有保障。BlockingQueue接口是Queue的子接口,它的主要用途并不是作为容器,而是作为线程同步的的工具,因此他具有一个很明显的特性,当生产者线程试图向BlockingQueue放入元素时,如果队列已满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞,正是因为它所具有这个特性,所以在程序中多个线程交替向BlockingQueue中放入元素,取出元素,它可以很好的控制线程之间的通信。阻塞队列使用最经典的场景就是socket客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。 13、什么是Callable和Future?Callable接口类似于Runnable,从名字就可以看出来了,但是Runnable不会返回结果,并且无法抛出返回结果的异常,而Callable功能更强大一些,被线程执行后,可以返回值,这个返回值可以被Future拿到,也就是说,Future可以拿到异步执行任务的返回值。 可以认为是带有回调的Runnable。Future接口表示异步任务,是还没有完成的任务给出的未来结果。所以说Callable用于产生结果,Future用于获取结果。 14、什么是FutureTask? 使用ExecutorService启动任务。在Java并发程序中FutureTask表示一个可以取消的异步运算。它有启动和取消运算、查询运算是否完成和取回运算结果等方法。只有当运算完成的时候结果才能取回,如果运算尚未完成get方法将会阻塞。一个FutureTask对象可以对调用了Callable和Runnable的对象进行包装,由于FutureTask也是调用了Runnable接口所以它可以提交给Executor来执行。 15、什么是并发容器的实现?何为同步容器:可以简单地理解为通过synchronized来实现同步的容器,如果有多个线程调用同步容器的方法,它们将会串行执行。比如Vector,Hashtable,以及Collections.synchronizedSet,synchronizedList等方法返回的容器。 可以通过查看Vector,Hashtable等这些同步容器的实现代码,可以看到这些容器实现线程安全的方式就是将它们的状态封装起来,并在需要同步的方法上加上关键字synchronized。并发容器使用了与同步容器完全不同的加锁策略来提供更高的并发性和伸缩性,例如在ConcurrentHashMap中采用了一种粒度更细的加锁机制,可以称为分段锁,在这种锁机制下,允许任意数量的读线程并发地访问map,并且执行读操作的线程和写操作的线程也可以并发的访问map,同时允许一定数量的写操作线程并发地修改map,所以它可以在并发环境下实现更高的吞吐量。 16、多线程同步和互斥有几种实现方法,都是什么?线程同步是指线程之间所具有的一种制约关系,一个线程的执行依赖另一个线程的消息,当它没有得到另一个线程的消息时应等待,直到消息到达时才被唤醒。线程互斥是指对于共享的进程系统资源,在各单个线程访问时的排它性。当有若干个线程都要使用某一共享资源时,任何时刻最多只允许一个线程去使用,其它要使用该资源的线程必须等待,直到占用资源者释放该资源。线程互斥可以看成是一种特殊的线程同步。线程间的同步方法大体可分为两类:用户模式和内核模式。顾名思义,内核模式就是指利用系统内核对象的单一性来进行同步,使用时需要切换内核态与用户态,而用户模式就是不需要切换到内核态,只在用户态完成操作。用户模式下的方法有:原子操作(例如一个单一的全局变量),临界区。内核模式下的方法有:事件,信号量,互斥量。 17、什么是竞争条件?你怎样发现和解决竞争?当多个进程都企图对共享数据进行某种处理,而最后的结果又取决于进程运行的顺序时,则我们认为这发生了竞争条件(race condition)。 18、你将如何使用thread dump?你将如何分析Thread dump? 新建状态(New) 用new语句创建的线程处于新建状态,此时它和其他Java对象一样,仅仅在堆区中被分配了内存。就绪状态(Runnable) 当一个线程对象创建后,其他线程调用它的start()方法,该线程就进入就绪状态,Java虚拟机会为它创建方法调用栈和程序计数器。处于这个状态的线程位于可运行池中,等待获得CPU的使用权。运行状态(Running) 处于这个状态的线程占用CPU,执行程序代码。只有处于就绪状态的线程才有机会转到运行状态。阻塞状态(Blocked) 阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进入就绪状态,它才有机会转到运行状态。 阻塞状态可分为以下3种:① 位于对象等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。② 位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。③ 其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。死亡状态(Dead)当线程退出run()方法时,就进入死亡状态,该线程结束生命周期。 19、为什么我们调用start()方法时会执行run()方法,为什么我们不能直接调用run()方法?当你调用start()方法时你将创建新的线程,并且执行在run()方法里的代码。 但是如果你直接调用run()方法,它不会创建新的线程也不会执行调用线程的代码,只会把run方法当作普通方法去执行。 20、Java中你怎样唤醒一个阻塞的线程?在Java发展史上曾经使用suspend()、resume()方法对于线程进行阻塞唤醒,但随之出现很多问题,比较典型的还是死锁问题。解决方案可以使用以对象为目标的阻塞,即利用Object类的wait()和notify()方法实现线程阻塞。 首先,wait、notify方法是针对对象的,调用任意对象的wait()方法都将导致线程阻塞,阻塞的同时也将释放该对象的锁,相应地,调用任意对象的notify()方法则将随机解除该对象阻塞的线程,但它需要重新获取改对象的锁,直到获取成功才能往下执行;其次,wait、notify方法必须在synchronized块或方法中被调用,并且要保证同步块或方法的锁对象与调用wait、notify方法的对象是同一个,如此一来在调用wait之前当前线程就已经成功获取某对象的锁,执行wait阻塞后当前线程就将之前获取的对象锁释放。 21、在Java中CycliBarriar和CountdownLatch有什么区别?CyclicBarrier可以重复使用,而CountdownLatch不能重复使用。Java的concurrent包里面的CountDownLatch其实可以把它看作一个计数器,只不过这个计数器的操作是原子操作,同时只能有一个线程去操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。你可以向CountDownLatch对象设置一个初始的数字作为计数值,任何调用这个对象上的await()方法都会阻塞,直到这个计数器的计数值被其他的线程减为0为止。 所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。CountDownLatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个CountDownLatch对象的await()方法,其他的任务执行完自己的任务后调用同一个CountDownLatch对象上的countDown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个CountDownLatch对象的计数值减到0为止。CyclicBarrier一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。 22、什么是不可变对象,它对写并发应用有什么帮助?不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)。 不可变对象的类即为不可变类(Immutable Class)。Java平台类库中包含许多不可变类,如String、基本类型的包装类、BigInteger和BigDecimal等。不可变对象天生是线程安全的。它们的常量(域)是在构造函数中创建的。既然它们的状态无法修改,这些常量永远不会变。不可变对象永远是线程安全的。 只有满足如下状态,一个对象才是不可变的; 它的状态不能在创建后再被修改; 所有域都是final类型;并且, 它被正确创建(创建期间没有发生this引用的逸出)。 23、什么是多线程中的上下文切换?在上下文切换过程中,CPU会停止处理当前运行的程序,并保存当前程序运行的具体位置以便之后继续运行。从这个角度来看,上下文切换有点像我们同时阅读几本书,在来回切换书本的同时我们需要记住每本书当前读到的页码。在程序中,上下文切换过程中的“页码”信息是保存在进程控制块(PCB)中的。PCB还经常被称作“切换桢”(switchframe)。“页码”信息会一直保存到CPU的内存中,直到他们被再次使用。 上下文切换是存储和恢复CPU状态的过程,它使得线程执行能够从中断点恢复执行。上下文切换是多任务操作系统和多线程环境的基本特征。 24、Java中用到的线程调度算法是什么?计算机通常只有一个CPU,在任意时刻只能执行一条机器指令,每个线程只有获得CPU的使用权才能执行指令.所谓多线程的并发运行,其实是指从宏观上看,各个线程轮流获得CPU的使用权,分别执行各自的任务.在运行池中,会有多个处于就绪状态的线程在等待CPU,JAVA虚拟机的一项任务就是负责线程的调度,线程调度是指按照特定机制为多个线程分配CPU的使用权。有两种调度模型:分时调度模型和抢占式调度模型。 分时调度模型是指让所有的线程轮流获得cpu的使用权,并且平均分配每个线程占用的CPU的时间片这个也比较好理解。java虚拟机采用抢占式调度模型,是指优先让可运行池中优先级高的线程占用CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。处于运行状态的线程会一直运行,直至它不得不放弃CPU。 25、什么是线程组,为什么在Java中不推荐使用?线程组和线程池是两个不同的概念,他们的作用完全不同,前者是为了方便线程的管理,后者是为了管理线程的生命周期,复用线程,减少创建销毁线程的开销。 26、为什么使用Executor框架比使用应用创建和管理线程好?为什么要使用Executor线程池框架 ?每次执行任务创建线程 new Thread()比较消耗性能,创建一个线程是比较耗时、耗资源的。 调用 new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制的创建,线程之间的相互竞争会导致过多占用系统资源而导致系统瘫痪,还有线程之间的频繁交替也会消耗很多系统资源。 直接使用new Thread() 启动的线程不利于扩展,比如定时执行、定期执行、定时定期执行、线程中断等都不便实现。使用Executor线程池框架的优点 ?能复用已存在并空闲的线程从而减少线程对象的创建从而减少了消亡线程的开销。 可有效控制最大并发线程数,提高系统资源使用率,同时避免过多资源竞争。 框架中已经有定时、定期、单线程、并发数控制等功能。 综上所述使用线程池框架Executor能更好的管理线程、提供系统资源使用率。 27、java中有几种方法可以实现一个线程?继承 Thread 类实现 Runnable 接口实现 Callable 接口,需要实现的是 call() 方法 28、如何停止一个正在运行的线程?使用共享变量的方式 在这种方式中,之所以引入共享变量,是因为该变量可以被多个执行相同任务的线程用来作为是否中断的信号,通知中断线程的执行。使用interrupt方法终止线程 如果一个线程由于等待某些事件的发生而被阻塞,又该怎样停止该线程呢?这种情况经常会发生,比如当一个线程由于需要等候键盘输入而被阻塞,或者调用Thread.join()方法,或者Thread.sleep()方法,在网络中调用ServerSocket.accept()方法,或者调用了DatagramSocket.receive()方法时,都有可能导致线程阻塞,使线程处于处于不可运行状态时,即使主程序中将该线程的共享变量设置为true,但该线程此时根本无法检查循环标志,当然也就无法立即中断。这里我们给出的建议是,不要使用stop()方法,而是使用Thread提供的interrupt()方法,因为该方法虽然不会中断一个正在运行的线程,但是它可以使一个被阻塞的线程抛出一个中断异常,从而使线程提前结束阻塞状态,退出堵塞代码。 29、notify()和notifyAll()有什么区别?当一个线程进入wait之后,就必须等其他线程notify/notifyall,使用notifyall,可以唤醒所有处于wait状态的线程,使其重新进入锁的争夺队列中,而notify只能唤醒一个。如果没把握,建议notifyAll,防止notigy因为信号丢失而造成程序异常。 30、什么是Daemon线程?它有什么意义?所谓后台(daemon)线程,是指在程序运行的时候在后台提供一种通用服务的线程,并且这个线程并不属于程序中不可或缺的部分。因此,当所有的非后台线程结束时,程序也就终止了,同时会杀死进程中的所有后台线程。反过来说, 只要有任何非后台线程还在运行,程序就不会终止。必须在线程启动之前调用setDaemon()方法,才能把它设置为后台线程。注意:后台进程在不执行finally子句的情况下就会终止其run()方法。比如:JVM的垃圾回收线程就是Daemon线程,Finalizer也是守护线程。 31、java如何实现多线程之间的通讯和协作?中断和共享变量 32、什么是可重入锁(ReentrantLock)?举例来说明锁的可重入性 public class UnReentrant{ Lock lock = new Lock(); public void outer(){ lock.lock(); inner(); lock.unlock(); } public void inner(){ lock.lock(); //do something lock.unlock(); } } outer中调用了inner,outer先锁住了lock,这样inner就不能再获取lock。其实调用outer的线程已经获取了lock锁,但是不能在inner中重复利用已经获取的锁资源,这种锁即称之为 不可重入可重入就意味着:线程可以进入任何一个它已经拥有的锁所同步着的代码块。 synchronized、ReentrantLock都是可重入的锁,可重入锁相对来说简化了并发编程的开发。 33、当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?如果其他方法没有synchronized的话,其他线程是可以进入的。所以要开放一个线程安全的对象时,得保证每个方法都是线程安全的。 34、乐观锁和悲观锁的理解及如何实现,有哪些实现方式?悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。再比如Java里面的同步原语synchronized关键字的实现也是悲观锁。乐观锁:顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库提供的类似于write_condition机制,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。乐观锁的实现方式: 使用版本标识来确定读到的数据与提交时的数据是否一致。提交后修改版本标识,不一致时可以采取丢弃和再次尝试的策略。 java中的Compare and Swap即CAS ,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 CAS 操作中包含三个操作数 —— 需要读写的内存位置(V)、进行比较的预期原值(A)和拟写入的新值(B)。如果内存位置V的值与预期原值A相匹配,那么处理器会自动将该位置值更新为新值B。否则处理器不做任何操作。CAS缺点: ABA问题: 比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但可能存在潜藏的问题。从Java1.5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。 循环时间长开销大: 对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。 只能保证一个共享变量的原子操作: 当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。 35、SynchronizedMap和ConcurrentHashMap有什么区别?SynchronizedMap一次锁住整张表来保证线程安全,所以每次只能有一个线程来访为map。ConcurrentHashMap使用分段锁来保证在多线程下的性能。ConcurrentHashMap中则是一次锁住一个桶。ConcurrentHashMap默认将hash表分为16个桶,诸如get,put,remove等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有16个写线程执行,并发性能的提升是显而易见的。 另外ConcurrentHashMap使用了一种不同的迭代方式。在这种迭代方式中,当iterator被创建后集合再发生改变就不再是抛出ConcurrentModificationException,取而代之的是在改变时new新的数据从而不影响原有的数据 ,iterator完成后再将头指针替换为新的数据 ,这样iterator线程可以使用原来老的数据,而写线程也可以并发的完成改变。 36、CopyOnWriteArrayList可以用于什么应用场景?CopyOnWriteArrayList(免锁容器)的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出ConcurrentModificationException。在CopyOnWriteArrayList中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全地执行。由于写操作的时候,需要拷贝数组,会消耗内存,如果原数组的内容比较多的情况下,可能导致young gc或者full gc; 不能用于实时读的场景,像拷贝数组、新增元素都需要时间,所以调用一个set操作后,读取到数据可能还是旧的,虽然CopyOnWriteArrayList 能做到最终一致性,但是还是没法满足实时性要求;CopyOnWriteArrayList透露的思想 读写分离,读和写分开 最终一致性 使用另外开辟空间的思路,来解决并发冲突 37、什么叫线程安全?servlet是线程安全吗?线程安全是编程中的术语,指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。Servlet不是线程安全的,servlet是单实例多线程的,当多个线程同时访问同一个方法,是不能保证共享变量的线程安全性的。 Struts2的action是多实例多线程的,是线程安全的,每个请求过来都会new一个新的action分配给这个请求,请求完成后销毁。 SpringMVC的Controller是线程安全的吗?不是的,和Servlet类似的处理流程Struts2好处是不用考虑线程安全问题;Servlet和SpringMVC需要考虑线程安全问题,但是性能可以提升不用处理太多的gc,可以使用ThreadLocal来处理多线程的问题。 38、volatile有什么用?能否用一句话说明下volatile的应用场景?volatile保证内存可见性和禁止指令重排。volatile用于多线程环境下的单次操作(单次读或者单次写)。 39、为什么代码会重排序?在执行程序时,为了提供性能,处理器和编译器常常会对指令进行重排序,但是不能随意重排序,不是你想怎么排序就怎么排序,它需要满足以下两个条件:在单线程环境下不能改变程序运行的结果;存在数据依赖关系的不允许重排序需要注意的是:重排序不会影响单线程环境的执行结果,但是会破坏多线程的执行语义。 40、在java中wait和sleep方法的不同?最大的不同是在等待时wait会释放锁,而sleep一直持有锁。Wait通常被用于线程间交互,sleep通常被用于暂停执行。直接了解的深入一点吧: 在Java中线程的状态一共被分成6种:初始态:NEW创建一个Thread对象,但还未调用start()启动线程时,线程处于初始态。运行态:RUNNABLE在Java中,运行态包括就绪态和运行态。 就绪态该状态下的线程已经获得执行所需的所有资源,只要CPU分配执行权就能运行。所有就绪态的线程存放在就绪队列中。 运行态获得CPU执行权,正在执行的线程。由于一个CPU同一时刻只能执行一条线程,因此每个CPU每个时刻只有一条运行态的线程。阻塞态当一条正在执行的线程请求某一资源失败时,就会进入阻塞态。而在Java中,阻塞态专指请求锁失败时进入的状态。由一个阻塞队列存放所有阻塞态的线程。处于阻塞态的线程会不断请求资源,一旦请求成功,就会进入就绪队列,等待执行。PS:锁、IO、Socket等都资源。等待态当前线程中调用wait、join、park函数时,当前线程就会进入等待态。也有一个等待队列存放所有等待态的线程。线程处于等待态表示它需要等待其他线程的指示才能继续运行。进入等待态的线程会释放CPU执行权,并释放资源(如:锁)。超时等待态当运行中的线程调用sleep(time)、wait、join、parkNanos、parkUntil时,就会进入该状态;它和等待态一样,并不是因为请求不到资源,而是主动进入,并且进入后需要其他线程唤醒;进入该状态后释放CPU执行权 和 占有的资源。与等待态的区别:到了超时时间后自动进入阻塞队列,开始竞争锁。终止态线程执行结束后的状态。注意:wait()方法会释放CPU执行权 和 占有的锁。sleep(long)方法仅释放CPU使用权,锁仍然占用;线程被放入超时等待队列,与yield相比,它会使线程较长时间得不到运行。yield()方法仅释放CPU执行权,锁仍然占用,线程会被放入就绪队列,会在短时间内再次执行。wait和notify必须配套使用,即必须使用同一把锁调用;wait和notify必须放在一个同步块中调用wait和notify的对象必须是他们所处同步块的锁对象。 41、一个线程运行时发生异常会怎样?如果异常没有被捕获该线程将会停止执行。Thread.UncaughtExceptionHandler是用于处理未捕获异常造成线程突然中断情况的一个内嵌接口。当一个未捕获异常将造成线程中断的时候JVM会使用Thread.getUncaughtExceptionHandler()来查询线程的UncaughtExceptionHandler并将线程和异常作为参数传递给handler的uncaughtException()方法进行处理。 42、如何在两个线程间共享数据?在两个线程间共享变量即可实现共享。一般来说,共享变量要求变量本身是线程安全的,然后在线程内使用的时候,如果有对共享变量的复合操作,那么也得保证复合操作的线程安全性。 43、Java中notify 和 notifyAll有什么区别?notify() 方法不能唤醒某个具体的线程,所以只有一个线程在等待的时候它才有用武之地。而notifyAll()唤醒所有线程并允许他们争夺锁确保了至少有一个线程能继续运行。 44、为什么wait, notify 和 notifyAll这些方法不在thread类里面?一个很明显的原因是JAVA提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。由于wait,notify和notifyAll都是锁级别的操作,所以把他们定义在Object类中因为锁属于对象。 45、什么是ThreadLocal变量?ThreadLocal是Java里一种特殊的变量。每个线程都有一个ThreadLocal就是每个线程都拥有了自己独立的一个变量,竞争条件被彻底消除了。它是为创建代价高昂的对象获取线程安全的好方法,比如你可以用ThreadLocal让SimpleDateFormat变成线程安全的,因为那个类创建代价高昂且每次调用都需要创建不同的实例所以不值得在局部范围使用它,如果为每个线程提供一个自己独有的变量拷贝,将大大提高效率。首先,通过复用减少了代价高昂的对象的创建个数。其次,你在没有使用高代价的同步或者不变性的情况下获得了线程安全。 46、Java中interrupted 和 isInterrupted方法的区别?interrupt interrupt方法用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。 注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。interrupted 查询当前线程的中断状态,并且清除原状态。如果一个线程被中断了,第一次调用interrupted则返回true,第二次和后面的就返回false了。isInterrupted 仅仅是查询当前线程的中断状态 47、为什么wait和notify方法要在同步块中调用?Java API强制要求这样做,如果你不这么做,你的代码会抛出IllegalMonitorStateException异常。还有一个原因是为了避免wait和notify之间产生竞态条件。 48、为什么你应该在循环中检查等待条件?处于等待状态的线程可能会收到错误警报和伪唤醒,如果不在循环中检查等待条件,程序就会在没有满足结束条件的情况下退出。 49、Java中的同步集合与并发集合有什么区别?同步集合与并发集合都为多线程和并发提供了合适的线程安全的集合,不过并发集合的可扩展性更高。在Java1.5之前程序员们只有同步集合来用且在多线程并发的时候会导致争用,阻碍了系统的扩展性。Java5介绍了并发集合像ConcurrentHashMap,不仅提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。 50、什么是线程池? 为什么要使用它?创建线程要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间会变长,而且一个进程能创建的线程数有限。为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程。从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池。 51、怎么检测一个线程是否拥有锁?在java.lang.Thread中有一个方法叫holdsLock(),它返回true如果当且仅当当前线程拥有某个具体对象的锁。 52、你如何在Java中获取线程堆栈?kill -3 [java pid] 不会在当前终端输出,它会输出到代码执行的或指定的地方去。比如,kill -3 tomcat pid, 输出堆栈到log目录下。Jstack [java pid] 这个比较简单,在当前终端显示,也可以重定向到指定文件中。 -JvisualVM:Thread Dump不做说明,打开JvisualVM后,都是界面操作,过程还是很简单的。 53、JVM中哪个参数是用来控制线程的栈堆栈小的?-Xss 每个线程的栈大小 54、Thread类中的yield方法有什么作用?使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。当前线程到了就绪状态,那么接下来哪个线程会从就绪状态变成执行状态呢?可能是当前线程,也可能是其他线程,看系统的分配了。 55、Java中ConcurrentHashMap的并发度是什么?ConcurrentHashMap把实际map划分成若干部分来实现它的可扩展性和线程安全。这种划分是使用并发度获得的,它是ConcurrentHashMap类构造函数的一个可选参数,默认值为16,这样在多线程情况下就能避免争用。在JDK8后,它摒弃了Segment(锁段)的概念,而是启用了一种全新的方式实现,利用CAS算法。同时加入了更多的辅助变量来提高并发度,具体内容还是查看源码吧。 56、Java中Semaphore是什么?Java中的Semaphore是一种新的同步类,它是一个计数信号。从概念上讲,从概念上讲,信号量维护了一个许可集合。如有必要,在许可可用前会阻塞每一个 acquire(),然后再获取该许可。每个 release()添加一个许可,从而可能释放一个正在阻塞的获取者。但是,不使用实际的许可对象,Semaphore只对可用许可的号码进行计数,并采取相应的行动。信号量常常用于多线程的代码中,比如数据库连接池。 57、Java线程池中submit() 和 execute()方法有什么区别?两个方法都可以向线程池提交任务,execute()方法的返回类型是void,它定义在Executor接口中。而submit()方法可以返回持有计算结果的Future对象,它定义在ExecutorService接口中,它扩展了Executor接口,其它线程池类像ThreadPoolExecutor和ScheduledThreadPoolExecutor都有这些方法。 58、什么是阻塞式方法?阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。 59、Java中的ReadWriteLock是什么?读写锁是用来提升并发程序性能的锁分离技术的成果。 60、volatile 变量和 atomic 变量有什么不同?Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操作就不是原子性的。而AtomicInteger类提供的atomic方法可以让这种操作具有原子性如getAndIncrement()方法会原子性的进行增量操作把当前值加一,其它数据类型和引用变量也可以进行相似操作。 61、可以直接调用Thread类的run ()方法么?当然可以。但是如果我们调用了Thread的run()方法,它的行为就会和普通的方法一样,会在当前线程中执行。为了在新的线程中执行我们的代码,必须使用Thread.start()方法。 62、如何让正在运行的线程暂停一段时间?我们可以使用Thread类的Sleep()方法让线程暂停一段时间。需要注意的是,这并不会让线程终止,一旦从休眠中唤醒线程,线程的状态将会被改变为Runnable,并且根据线程调度,它将得到执行。 63、你对线程优先级的理解是什么?每一个线程都是有优先级的,一般来说,高优先级的线程在运行时会具有优先权,但这依赖于线程调度的实现,这个实现是和操作系统相关的(OS dependent)。我们可以定义线程的优先级,但是这并不能保证高优先级的线程会在低优先级的线程前执行。线程优先级是一个int变量(从1-10),1代表最低优先级,10代表最高优先级。java的线程优先级调度会委托给操作系统去处理,所以与具体的操作系统优先级有关,如非特别需要,一般无需设置线程优先级。 64、什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing )?线程调度器是一个操作系统服务,它负责为Runnable状态的线程分配CPU时间。一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现。 同上一个问题,线程调度并不受到Java虚拟机控制,所以由应用程序来控制它是更好的选择(也就是说不要让你的程序依赖于线程的优先级)。时间分片是指将可用的CPU时间分配给可用的Runnable线程的过程。分配CPU时间可以基于线程优先级或者线程等待的时间。 65、你如何确保main()方法所在的线程是Java 程序最后结束的线程?我们可以使用Thread类的join()方法来确保所有程序创建的线程在main()方法退出前结束。 66、线程之间是如何通信的?当线程间是可以共享资源时,线程间通信是协调它们的重要的手段。Object类中wait() otify() otifyAll()方法可以用于线程间通信关于资源的锁的状态。 67、为什么线程通信的方法wait(), notify()和notifyAll()被定义在Object 类里?Java的每个对象中都有一个锁(monitor,也可以成为监视器) 并且wait(),notify()等方法用于等待对象的锁或者通知其他线程对象的监视器可用。在Java的线程中并没有可供任何对象使用的锁和同步器。这就是为什么这些方法是Object类的一部分,这样Java的每一个类都有用于线程间通信的基本方法。 68、为什么wait(), notify()和notifyAll ()必须在同步方法或者同步块中被调用?当一个线程需要调用对象的wait()方法的时候,这个线程必须拥有该对象的锁,接着它就会释放这个对象锁并进入等待状态直到其他线程调用这个对象上的notify()方法。同样的,当一个线程需要调用对象的notify()方法时,它会释放这个对象的锁,以便其他在等待的线程就可以得到这个对象锁。由于所有的这些方法都需要线程持有对象的锁,这样就只能通过同步来实现,所以他们只能在同步方法或者同步块中被调用。 69、为什么Thread类的sleep()和yield ()方法是静态的?Thread类的sleep()和yield()方法将在当前正在执行的线程上运行。所以在其他处于等待状态的线程上调用这些方法是没有意义的。这就是为什么这些方法是静态的。它们可以在当前正在执行的线程中工作,并避免程序员错误的认为可以在其他非运行线程调用这些方法。 70、如何确保线程安全?在Java中可以有很多方法来保证线程安全——同步,使用原子类(atomic concurrent classes),实现并发锁,使用volatile关键字,使用不变类和线程安全类。 71、同步方法和同步块,哪个是更好的选择?同步块是更好的选择,因为它不会锁住整个对象(当然你也可以让它锁住整个对象)。同步方法会锁住整个对象,哪怕这个类中有多个不相关联的同步块,这通常会导致他们停止执行并需要等待获得这个对象上的锁。同步块更要符合开放调用的原则,只在需要锁住的代码块锁住相应的对象,这样从侧面来说也可以避免死锁。 72、如何创建守护线程?使用Thread类的setDaemon(true)方法可以将线程设置为守护线程,需要注意的是,需要在调用start()方法前调用这个方法,否则会抛出IllegalThreadStateException异常。 73、什么是Java Timer 类?如何创建一个有特定时间间隔的任务?java.util.Timer是一个工具类,可以用于安排一个线程在未来的某个特定时间执行。Timer类可以用安排一次性任务或者周期任务。 java.util.TimerTask是一个实现了Runnable接口的抽象类,我们需要去继承这个类来创建我们自己的定时任务并使用Timer去安排它的执行。 欢迎大家一起交流,喜欢文章记得点个赞,感谢支持!

优秀的个人博客,低调大师

BAT等大厂总结的前200页Java面试题都在这里了

内容较多,请大家耐心阅读 基本概念 操作系统中 heap 和 stack 的区别 什么是基于注解的切面实现 什么是 对象/关系 映射集成模块 什么是 Java 的反射机制 什么是 ACID BS与CS的联系与区别 Cookie 和 Session的区别 fail-fast 与 fail-safe 机制有什么区别 get 和 post请求的区别 Interface 与 abstract 类的区别 IOC的优点是什么 IO 和 NIO的区别,NIO优点 Java 8 / Java 7 为我们提供了什么新功能 什么是竞态条件? 举个例子说明。 JRE、JDK、JVM 及 JIT 之间有什么不同 MVC的各个部分都有那些技术来实现?如何实现? RPC 通信和 RMI 区别 什么是 Web Service(Web服务) JSWDL开发包的介绍。JAXP、JAXM的解释。SOAP、UDDI,WSDL解释。 WEB容器主要有哪些功能? 并请列出一些常见的WEB容器名字。 一个”.java”源文件中是否可以包含多个类(不是内部类)?有什么限制 简单说说你了解的类加载器。是否实现过类加载器 解释一下什么叫AOP(面向切面编程) 请简述 Servlet 的生命周期及其相关的方法 请简述一下 Ajax 的原理及实现步骤 简单描述Struts的主要功能 什么是 N 层架构 什么是CORBA?用途是什么 什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言” 什么是正则表达式?用途是什么?哪个包使用正则表达式来实现模式匹配 什么是懒加载(Lazy Loading) 什么是尾递归,为什么需要尾递归 什么是控制反转(Inversion of Control)与依赖注入(Dependency Injection) 关键字 finalize 什么是finalize()方法 finalize()方法什么时候被调用 析构函数(finalization)的目的是什么 final 和 finalize 的区别 final final关键字有哪些用法 final 与 static 关键字可以用于哪里?它们的作用是什么 final, finally, finalize的区别 final、finalize 和 finally 的不同之处? 能否在运行时向 static final 类型的赋值 使用final关键字修饰一个变量时,是引用不能变,还是引用的对象不能变 一个类被声明为final类型,表示了什么意思 throws, throw, try, catch, finally分别代表什么意义 Java 有几种修饰符?分别用来修饰什么 volatile volatile 修饰符的有过什么实践 volatile 变量是什么?volatile 变量和 atomic 变量有什么不同 volatile 类型变量提供什么保证?能使得一个非原子操作变成原子操作吗 能创建 volatile 数组吗? transient变量有什么特点 super什么时候使用 public static void 写成 static public void会怎样 说明一下public static void main(String args[])这段声明里每个关键字的作用 请说出作用域public, private, protected, 以及不写时的区别 sizeof 是Java 的关键字吗 欢迎工作一到五年的Java工程师朋友们加入Java填坑之路:860113481 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代! static static class 与 non static class的区别 static 关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法 静态类型有什么特点 main() 方法为什么必须是静态的?能不能声明 main() 方法为非静态 是否可以从一个静态(static)方法内部发出对非静态(non-static)方法的调用 静态变量在什么时候加载?编译期还是运行期?静态代码块加载的时机呢 成员方法是否可以访问静态变量?为什么静态方法不能访问成员变量 switch switch 语句中的表达式可以是什么类型数据 switch 是否能作用在byte 上,是否能作用在long 上,是否能作用在String上 while 循环和 do 循环有什么不同 操作符 &操作符和&&操作符有什么区别? a = a + b 与 a += b 的区别? 逻辑操作符 (&,|,^)与条件操作符(&&,||)的区别 3*0.1 == 0.3 将会返回什么?true 还是 false? float f=3.4; 是否正确? short s1 = 1; s1 = s1 + 1;有什么错? 数据结构 基础类型(Primitives) 基础类型(Primitives)与封装类型(Wrappers)的区别在哪里 简述九种基本数据类型的大小,以及他们的封装类 int 和 Integer 哪个会占用更多的内存? int 和 Integer 有什么区别?parseInt()函数在什么时候使用到 float和double的默认值是多少 如何去小数四舍五入保留小数点后两位 char 型变量中能不能存贮一个中文汉字,为什么 类型转换 怎样将 bytes 转换为 long 类型 怎么将 byte 转换为 String 如何将数值型字符转换为数字 我们能将 int 强制转换为 byte 类型的变量吗?如果该值大于 byte 类型的范围,将会出现什么现象 能在不进行强制转换的情况下将一个 double 值赋值给 long 类型的变量吗 类型向下转换是什么 数组 如何权衡是使用无序的数组还是有序的数组 怎么判断数组是 null 还是为空 怎么打印数组? 怎样打印数组中的重复元素 Array 和 ArrayList有什么区别?什么时候应该使用Array而不是ArrayList 数组和链表数据结构描述,各自的时间复杂度 数组有没有length()这个方法? String有没有length()这个方法 队列 队列和栈是什么,列出它们的区别 BlockingQueue是什么 简述 ConcurrentLinkedQueue LinkedBlockingQueue 的用处和不同之处。 ArrayList、Vector、LinkedList的存储性能和特性 String StringBuffer ByteBuffer 与 StringBuffer有什么区别 HashMap HashMap的工作原理是什么 内部的数据结构是什么 HashMap 的 table的容量如何确定?loadFactor 是什么? 该容量如何变化?这种变化会带来什么问题? HashMap 实现的数据结构是什么?如何实现 HashMap 和 HashTable、ConcurrentHashMap 的区别 HashMap的遍历方式及效率 HashMap、LinkedMap、TreeMap的区别 如何决定选用HashMap还是TreeMap 如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办 HashMap 是线程安全的吗?并发下使用的 Map 是什么,它们内部原理分别是什么,比如存储方式、 hashcode、扩容、 默认容量等 HashSet HashSet和TreeSet有什么区别 HashSet 内部是如何工作的 WeakHashMap 是怎么工作的? Set Set 里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用 == 还是 equals()? 它们有何区别? TreeMap:TreeMap 是采用什么树实现的?TreeMap、HashMap、LindedHashMap的区别。TreeMap和TreeSet在排序时如何比较元素?Collections工具类中的sort()方法如何比较元素? TreeSet:一个已经构建好的 TreeSet,怎么完成倒排序。 EnumSet 是什么 Hash算法 Hashcode 的作用 简述一致性 Hash 算法 有没有可能 两个不相等的对象有相同的 hashcode?当两个对象 hashcode 相同怎么办?如何获取值对象 为什么在重写 equals 方法的时候需要重写 hashCode 方法?equals与 hashCode 的异同点在哪里 a.hashCode() 有什么用?与 a.equals(b) 有什么关系 hashCode() 和 equals() 方法的重要性体现在什么地方 Object:Object有哪些公用方法?Object类hashcode,equals 设计原则? sun为什么这么设计?Object类的概述 如何在父类中为子类自动完成所有的 hashcode 和 equals 实现?这么做有何优劣。 可以在 hashcode() 中使用随机数字吗? LinkedHashMap LinkedHashMap 和 PriorityQueue 的区别是什么 List List, Set, Map三个接口,存取元素时各有什么特点 List, Set, Map 是否继承自 Collection 接口 遍历一个 List 有哪些不同的方式 LinkedList LinkedList 是单向链表还是双向链表 LinkedList 与 ArrayList 有什么区别 描述下 Java 中集合(Collections),接口(Interfaces),实现(Implementations)的概念。LinkedList 与 ArrayList 的区别是什么? 插入数据时,ArrayList, LinkedList, Vector谁速度较快? ArrayList ArrayList 和 HashMap 的默认大小是多数 ArrayList 和 LinkedList 的区别,什么时候用 ArrayList? ArrayList 和 Set 的区别? ArrayList, LinkedList, Vector的区别 ArrayList是如何实现的,ArrayList 和 LinkedList 的区别 ArrayList如何实现扩容 Array 和 ArrayList 有何区别?什么时候更适合用Array 说出ArraList,Vector, LinkedList的存储性能和特性 Map Map, Set, List, Queue, Stack Map 接口提供了哪些不同的集合视图 为什么 Map 接口不继承 Collection 接口 Collections 介绍Java中的Collection FrameWork。集合类框架的基本接口有哪些 Collections类是什么?Collection 和 Collections的区别?Collection、Map的实现 集合类框架的最佳实践有哪些 为什么 Collection 不从 Cloneable 和 Serializable 接口继承 说出几点 Java 中使用 Collections 的最佳实践? Collections 中 遗留类 (HashTable、Vector) 和 现有类的区别 什么是 B+树,B-树,列出实际的使用场景。 接口 Comparator 与 Comparable 接口是干什么的?列出它们的区别 对象 拷贝(clone) 如何实现对象克隆 深拷贝和浅拷贝区别 深拷贝和浅拷贝如何实现激活机制 写clone()方法时,通常都有一行代码,是什么 比较 在比较对象时,”==” 运算符和 equals 运算有何区别 如果要重写一个对象的equals方法,还要考虑什么 两个对象值相同(x.equals(y) == true),但却可有不同的hash code,这句话对不对 构造器 构造器链是什么 创建对象时构造器的调用顺序 不可变对象 什么是不可变象(immutable object) 为什么 Java 中的 String 是不可变的(Immutable) 如何构建不可变的类结构?关键点在哪里 能创建一个包含可变对象的不可变对象吗 如何对一组对象进行排序 方法 构造器(constructor)是否可被重写(override) 方法可以同时即是 static 又是 synchronized 的吗 abstract 的 method是否可同时是 static,是否可同时是 native,是否可同时是synchronized Java支持哪种参数传递类型 一个对象被当作参数传递到一个方法,是值传递还是引用传递 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递 我们能否重载main()方法 如果main方法被声明为private会怎样 GC 概念 GC是什么?为什么要有GC 什么时候会导致垃圾回收 GC是怎么样运行的 新老以及永久区是什么 GC 有几种方式?怎么配置 什么时候一个对象会被GC? 如何判断一个对象是否存活 System.gc() Runtime.gc()会做什么事情? 能保证 GC 执行吗 垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收? Minor GC 、Major GC、Young GC 与 Full GC分别在什么时候发生 垃圾回收算法的实现原理 如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存? 垃圾回收的最佳做法是什么 GC收集器有哪些 垃圾回收器的基本原理是什么? 串行(serial)收集器和吞吐量(throughput)收集器的区别是什么 Serial 与 Parallel GC之间的不同之处 CMS 收集器 与 G1 收集器的特点与区别 CMS垃圾回收器的工作过程 JVM 中一次完整的 GC 流程是怎样的? 对象如何晋升到老年代 吞吐量优先和响应优先的垃圾收集器选择 GC策略 举个实际的场景,选择一个GC策略 JVM的永久代中会发生垃圾回收吗 收集方法 标记清除、标记整理、复制算法的原理与特点?分别用在什么地方 如果让你优化收集方法,有什么思路 JVM 参数 说说你知道的几种主要的jvm 参数 -XX:+UseCompressedOops 有什么作用 类加载器(ClassLoader) Java 类加载器都有哪些 JVM如何加载字节码文件 内存管理 JVM内存分哪几个区,每个区的作用是什么 一个对象从创建到销毁都是怎么在这些部分里存活和转移的 解释内存中的栈(stack)、堆(heap)和方法区(method area)的用法 JVM中哪个参数是用来控制线程的栈堆栈小 简述内存分配与回收策略 简述重排序,内存屏障,happen-before,主内存,工作内存 Java中存在内存泄漏问题吗?请举例说明 简述 Java 中软引用(SoftReferenc)、弱引用(WeakReference)和虚引用 内存映射缓存区是什么 jstack,jstat,jmap,jconsole怎么用 32 位 JVM 和 64 位 JVM 的最大堆内存分别是多数?32 位和 64 位的 JVM,int 类型变量的长度是多数? 怎样通过 Java 程序来判断 JVM 是 32 位 还是 64 位 JVM自身会维护缓存吗?是不是在堆中进行对象分配,操作系统的堆还是JVM自己管理堆 什么情况下会发生栈内存溢出 双亲委派模型是什么 多线程 基本概念 什么是线程 多线程的优点 多线程的几种实现方式 用 Runnable 还是 Thread 什么是线程安全 Vector, SimpleDateFormat 是线程安全类吗 什么 Java 原型不是线程安全的 哪些集合类是线程安全的 多线程中的忙循环是什么 如何创建一个线程 编写多线程程序有几种实现方式 什么是线程局部变量 线程和进程有什么区别?进程间如何通讯,线程间如何通讯 什么是多线程环境下的伪共享(false sharing) 同步和异步有何异同,在什么情况下分别使用他们?举例说明 Current ConcurrentHashMap 和 Hashtable的区别 ArrayBlockingQueue, CountDownLatch的用法 ConcurrentHashMap的并发度是什么 CyclicBarrier 和 CountDownLatch有什么不同?各自的内部原理和用法是什么 Semaphore的用法 Thread 启动一个线程是调用 run() 还是 start() 方法?start() 和 run() 方法有什么区别 调用start()方法时会执行run()方法,为什么不能直接调用run()方法 sleep() 方法和对象的 wait() 方法都可以让线程暂停执行,它们有什么区别 yield方法有什么作用?sleep() 方法和 yield() 方法有什么区别 Java 中如何停止一个线程 stop() 和 suspend() 方法为何不推荐使用 如何在两个线程间共享数据 如何强制启动一个线程 如何让正在运行的线程暂停一段时间 什么是线程组,为什么在Java中不推荐使用 你是如何调用 wait(方法的)?使用 if 块还是循环?为什么 生命周期 有哪些不同的线程生命周期 线程状态,BLOCKED 和 WAITING 有什么区别 画一个线程的生命周期状态图 ThreadLocal 用途是什么,原理是什么,用的时候要注意什么 ThreadPool 线程池是什么?为什么要使用它 如何创建一个Java线程池 ThreadPool用法与优势 提交任务时,线程池队列已满时会发会生什么 newCache 和 newFixed 有什么区别?简述原理。构造函数的各个参数的含义是什么,比如 coreSize, maxsize 等 线程池的实现策略 线程池的关闭方式有几种,各自的区别是什么 线程池中submit() 和 execute()方法有什么区别? 线程调度 Java中用到的线程调度算法是什么 什么是多线程中的上下文切换 你对线程优先级的理解是什么 什么是线程调度器 (Thread Scheduler) 和时间分片 (Time Slicing) 线程同步 请说出你所知的线程同步的方法 synchronized 的原理是什么 synchronized 和 ReentrantLock 有什么不同 什么场景下可以使用 volatile 替换 synchronized 有T1,T2,T3三个线程,怎么确保它们按顺序执行?怎样保证T2在T1执行完后执行,T3在T2执行完后执行 同步块内的线程抛出异常会发生什么 当一个线程进入一个对象的 synchronized 方法A 之后,其它线程是否可进入此对象的 synchronized 方法B 使用 synchronized 修饰静态方法和非静态方法有什么区别 如何从给定集合那里创建一个 synchronized 的集合 锁 Java Concurrency API 中 的 Lock 接口是什么?对比同步它有什么优势 Lock 与 Synchronized 的区别?Lock 接口比 synchronized 块的优势是什么 ReadWriteLock是什么? 锁机制有什么用 什么是乐观锁(Optimistic Locking)?如何实现乐观锁?如何避免ABA问题 解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁 什么时候应该使用可重入锁 简述锁的等级方法锁、对象锁、类锁 Java中活锁和死锁有什么区别? 什么是死锁(Deadlock)?导致线程死锁的原因?如何确保 N 个线程可以访问 N 个资源同时又不导致死锁 死锁与活锁的区别,死锁与饥饿的区别 怎么检测一个线程是否拥有锁 如何实现分布式锁 有哪些无锁数据结构,他们实现的原理是什么 读写锁可以用于什么应用场景 Executors类是什么? Executor和Executors的区别 什么是Java线程转储(Thread Dump),如何得到它 如何在Java中获取线程堆栈 说出 3 条在 Java 中使用线程的最佳实践 在线程中你怎么处理不可捕捉异常 实际项目中使用多线程举例。你在多线程环境中遇到的常见的问题是什么?你是怎么解决它的 请说出与线程同步以及线程调度相关的方法 程序中有3个 socket,需要多少个线程来处理 假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有 10 个线程同时调用它,如何做到 如何在 Windows 和 Linux 上查找哪个线程使用的 CPU 时间最长 如何确保 main() 方法所在的线程是 Java 程序最后结束的线程 非常多个线程(可能是不同机器),相互之间需要等待协调才能完成某种工作,问怎么设计这种协调方案 你需要实现一个高效的缓存,它允许多个用户读,但只允许一个用户写,以此来保持它的完整性,你会怎样去实现它 异常 基本概念 Error 和 Exception有什么区别 UnsupportedOperationException是什么 NullPointerException 和 ArrayIndexOutOfBoundException 之间有什么相同之处 什么是受检查的异常,什么是运行时异常 运行时异常与一般异常有何异同 简述一个你最常见到的runtime exception(运行时异常) finally finally关键词在异常处理中如何使用 如果执行finally代码块之前方法返回了结果,或者JVM退出了,finally块中的代码还会执行吗 try里有return,finally还执行么?那么紧跟在这个try后的finally {}里的code会不会被执行,什么时候被执行,在return前还是后 在什么情况下,finally语句不会执行 throw 和 throws 有什么区别? OOM你遇到过哪些情况?你是怎么搞定的? SOF你遇到过哪些情况? 既然我们可以用RuntimeException来处理错误,那么你认为为什么Java中还存在检查型异常 当自己创建异常类的时候应该注意什么 导致空指针异常的原因 异常处理 handle or declare 原则应该如何理解 怎么利用 JUnit 来测试一个方法的异常 catch块里别不写代码有什么问题 你曾经自定义实现过异常吗?怎么写的 什么是 异常链 在try块中可以抛出异常吗 JDBC 通过 JDBC 连接数据库有哪几种方式 阐述 JDBC 操作数据库的基本步骤 JDBC 中如何进行事务处理 什么是 JdbcTemplate 什么是 DAO 模块 使用 JDBC 操作数据库时,如何提升读取数据的性能?如何提升更新数据的性能 列出 5 个应该遵循的 JDBC 最佳实践 IO File File类型中定义了什么方法来创建一级目录 File类型中定义了什么方法来判断一个文件是否存在 流 为了提高读写性能,可以采用什么流 Java中有几种类型的流 JDK 为每种类型的流提供了一些抽象类以供继承,分别是哪些类 对文本文件操作用什么I/O流 对各种基本数据类型和String类型的读写,采用什么流 能指定字符编码的 I/O 流类型是什么 序列化 什么是序列化?如何实现 Java 序列化及注意事项 Serializable 与 Externalizable 的区别 Socket socket 选项 TCP NO DELAY 是指什么 Socket 工作在 TCP/IP 协议栈是哪一层 TCP、UDP 区别及 Java 实现方式 说几点 IO 的最佳实践 直接缓冲区与非直接缓冲器有什么区别? 怎么读写 ByteBuffer?ByteBuffer 中的字节序是什么 当用System.in.read(buffer)从键盘输入一行n个字符后,存储在缓冲区buffer中的字节数是多少 如何使用扫描器类(Scanner Class)令牌化 面向对象编程(OOP) 解释下多态性(polymorphism),封装性(encapsulation),内聚(cohesion)以及耦合(coupling) 多态的实现原理 封装、继承和多态是什么 对象封装的原则是什么? 类 获得一个类的类对象有哪些方式 重载(Overload)和重写(Override)的区别。重载的方法能否根据返回类型进行区分? 说出几条 Java 中方法重载的最佳实践 抽象类 抽象类和接口的区别 抽象类中是否可以有静态的main方法 抽象类是否可实现(implements)接口 抽象类是否可继承具体类(concrete class) 匿名类(Anonymous Inner Class) 匿名内部类是否可以继承其它类?是否可以实现接口 内部类 内部类分为几种 内部类可以引用它的包含类(外部类)的成员吗 请说一下 Java 中为什么要引入内部类?还有匿名内部类 继承 继承(Inheritance)与聚合(Aggregation)的区别在哪里 继承和组合之间有什么不同 为什么类只能单继承,接口可以多继承 存在两个类,B 继承 A,C 继承 B,能将 B 转换为 C 么?如 C = (C) B 如果类 a 继承类 b,实现接口c,而类 b 和接口 c 中定义了同名变量,请问会出现什么问题 接口 接口是什么 接口是否可继承接口 为什么要使用接口而不是直接使用具体类?接口有什么优点 泛型 泛型的存在是用来解决什么问题 泛型的常用特点 List能否转为List 工具类 日历 Calendar Class的用途 如何在Java中获取日历类的实例 解释一些日历类中的重要方法 GregorianCalendar 类是什么 SimpleTimeZone 类是什么 Locale类是什么 如何格式化日期对象 如何添加小时(hour)到一个日期对象(Date Objects) 如何将字符串 YYYYMMDD 转换为日期 Math Math.round()什么作用?Math.round(11.5) 等于多少?Math.round(-11.5)等于多少? XML XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式?DOM 和 SAX 解析器有什么不同? Java解析XML的方式 用 jdom 解析 xml 文件时如何解决中文问题?如何解析 你在项目中用到了 XML 技术的哪些方面?如何实现 动态代理 描述动态代理的几种实现方式,分别说出相应的优缺点 设计模式 什么是设计模式(Design Patterns)?你用过哪种设计模式?用在什么场合 你知道哪些商业级设计模式? 哪些设计模式可以增加系统的可扩展性 单例模式 除了单例模式,你在生产环境中还用过什么设计模式? 写 Singleton 单例模式 单例模式的双检锁是什么 如何创建线程安全的 Singleton 什么是类的单例模式 写出三种单例模式实现 适配器模式 适配器模式是什么?什么时候使用 适配器模式和代理模式之前有什么不同 适配器模式和装饰器模式有什么区别 什么时候使用享元模式 什么时候使用组合模式 什么时候使用访问者模式 什么是模板方法模式 请给出1个符合开闭原则的设计模式的例子 开放问题 用一句话概括 Web 编程的特点 Google是如何在一秒内把搜索结果返回给用户 哪种依赖注入方式你建议使用,构造器注入,还是 Setter方法注入 树(二叉或其他)形成许多普通数据结构的基础。请描述一些这样的数据结构以及何时可以使用它们 某一项功能如何设计 线上系统突然变得异常缓慢,你如何查找问题 什么样的项目不适合用框架 新浪微博是如何实现把微博推给订阅者 简要介绍下从浏览器输入 URL 开始到获取到请求界面之后 Java Web 应用中发生了什么 请你谈谈SSH整合 高并发下,如何做到安全的修改同一行数据 12306网站的订票系统如何实现,如何保证不会票不被超卖 网站性能优化如何优化的 聊了下曾经参与设计的服务器架构 请思考一个方案,实现分布式环境下的 countDownLatch 请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存 在你的职业生涯中,算得上最困难的技术挑战是什么 如何写一篇设计文档,目录是什么 大写的O是什么?举几个例子 编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用 解释一下网络应用的模式及其特点 设计一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新 说出数据连接池的工作机制是什么 怎么获取一个文件中单词出现的最高频率 描述一下你最常用的编程风格 如果有机会重新设计你们的产品,你会怎么做 如何搭建一个高可用系统 如何启动时不需输入用户名与密码 如何在基于Java的Web项目中实现文件上传和下载 如何实现一个秒杀系统,保证只有几位用户能买到某件商品。 如何实现负载均衡,有哪些算法可以实现 如何设计一个购物车?想想淘宝的购物车如何实现的 如何设计一套高并发支付方案,架构如何设计 如何设计建立和保持 100w 的长连接 如何避免浏览器缓存。 如何防止缓存雪崩 如果AB两个系统互相依赖,如何解除依 如果有人恶意创建非法连接,怎么解决 如果有几十亿的白名单,每天白天需要高并发查询,晚上需要更新一次,如何设计这个功能 如果系统要使用超大整数(超过long长度范围),请你设计一个数据结构来存储这种超大型数字以及设计一种算法来实现超大整数加法运算) 如果要设计一个图形系统,请你设计基本的图形元件(Point,Line,Rectangle,Triangle)的简单实现 如果让你实现一个并发安全的链表,你会怎么做 应用服务器与WEB 服务器的区别?应用服务器怎么监控性能,各种方式的区别?你使用过的应用服务器优化技术有哪些 大型网站在架构上应当考虑哪些问题 有没有处理过线上问题?出现内存泄露,CPU利用率标高,应用无响应时如何处理的 最近看什么书,印象最深刻的是什么 描述下常用的重构技巧 你使用什么版本管理工具?分支(Branch)与标签(Tag)之间的区别在哪里 你有了解过存在哪些反模式(Anti-Patterns)吗 你用过的网站前端优化的技术有哪些 如何分析Thread dump 你如何理解AOP中的连接点(Joinpoint)、切点(Pointcut)、增强(Advice)、引介(Introduction)、织入(Weaving)、切面(Aspect)这些概念 你是如何处理内存泄露或者栈溢出问题的 你们线上应用的 JVM 参数有哪些 怎么提升系统的QPS和吞吐量 知识面 解释什么是 MESI 协议(缓存一致性) 谈谈 reactor 模型 Java 9 带来了怎样的新功能 Java 与 C++ 对比,C++ 或 Java 中的异常处理机制的简单原理和应用 简单讲讲 Tomcat 结构,以及其类加载器流程 虚拟内存是什么 阐述下 SOLID 原则 请简要讲一下你对测试驱动开发(TDD)的认识 CDN实现原理 Maven 和 ANT 有什么区别 UML中有哪些常用的图 Linux Linux 下 IO 模型有几种,各自的含义是什么。 Linux 系统下你关注过哪些内核参数,说说你知道的 Linux 下用一行命令查看文件的最后五行 平时用到哪些 Linux 命令 用一行命令输出正在运行的 Java 进程 使用什么命令来确定是否有 Tomcat 实例运行在机器上 什么是 N+1 难题 什么是 paxos 算法 什么是 restful,讲讲你理解的 restful 什么是 zab 协议 什么是领域模型(domain model)?贫血模型(anaemic domain model) 和充血模型(rich domain model)有什么区别 什么是领域驱动开发(Domain Driven Development) 介绍一下了解的 Java 领域的 Web Service 框架 Web Server、Web Container 与 Application Server 的区别是什么 微服务(MicroServices)与巨石型应用(Monolithic Applications)之间的区别在哪里 描述 Cookie 和 Session 的作用,区别和各自的应用范围,Session工作原理 你常用的持续集成(Continuous Integration)、静态代码分析(Static Code Analysis)工具有哪些 简述下数据库正则化(Normalizations) KISS,DRY,YAGNI 等原则是什么含义 分布式事务的原理,优缺点,如何使用分布式事务? 布式集群下如何做到唯一序列号 网络 HTTPS 的加密方式是什么,讲讲整个加密解密流程 HTTPS和HTTP的区别 HTTP连接池实现原理 HTTP集群方案 Nginx、lighttpd、Apache三大主流 Web服务器的区别 是否看过框架的一些代码 持久层设计要考虑的问题有哪些?你用过的持久层框架有哪些 数值提升是什么 你能解释一下里氏替换原则吗 你是如何测试一个应用的?知道哪些测试框架 传输层常见编程协议有哪些?并说出各自的特点 编程题 计算加班费 加班10小时以下加班费是时薪的1.5倍。加班10小时或以上,按4元/时算。提示:(一个月工作26天,一天正常工作8小时) 计算1000月薪,加班9小时的加班费 计算2500月薪,加班11小时的加班费 计算1000月薪,加班15小时的加班费 卖东西 一家商场有红苹果和青苹果出售。(红苹果5元/个,青苹果4元/个)。 模拟一个进货。红苹果跟青苹果各进200个。 模拟一个出售。红苹果跟青苹果各买出10个。每卖出一个苹果需要进行统计。 提示:一个苹果是一个单独的实体。 日期提取 有这样一个时间字符串:2008-8-8 20:08:08 , 请编写能够匹配它的正则表达式,并编写Java代码将日期后面的时分秒提取出来,即:20:08:08 线程 8设计4个线程,其中两个线程每次对j增加1,另外两个线程对j每次减少1。写出程序。 用Java写一个多线程程序,如写四个线程,二个加1,二个对一个变量减一,输出 wait-notify 写一段代码来解决生产者-消费者问题 数字 判断101-200之间有多少个素数,并输出所有素数 用最有效率的方法算出2乘以17等于多少 有 1 亿个数字,其中有 2 个是重复的,快速找到它,时间和空间要最优 2 亿个随机生成的无序整数,找出中间大小的值 10 亿个数字里里面找最小的 10 个 1到1亿的自然数,求所有数的拆分后的数字之和,如286 拆分成2、8、6,如1到11拆分后的数字之和 => 1 + … + 9 + 1 + 0 + 1 + 1 一个数如果恰好等于它的因子之和,这个数就称为 “完数 “。例如6=1+2+3.编程 找出1000以内的所有完数 一个数组中所有的元素都出现了三次,只有一个元素出现了一次找到这个元素 一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在 第10次落地时,共经过多少米?第10次反弹多高? 求100-1000内质数的和 求1到100的和的平均数 求s=a+a+aaa+aaaa+aa…a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。 求出1到100的和 算出1到40的质数,放进数组里 显示放组里的数 找出第[5]个数 删除第[9]个数,再显示删除后的第[9]个 有 3n+1 个数字,其中 3n 个中是重复的,只有 1 个是不重复的,怎么找出来。 有一组数1.1.2.3.5.8.13.21.34。写出程序随便输入一个数就能给出和前一组数字同规律的头5个数 计算指定数字的阶乘 开发 Fizz Buzz 给定一个包含 N 个整数的数组,找出丢失的整数 一个排好序的数组,找出两数之和为m的所有组合 将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5。 打印出所有的 “水仙花数 “,所谓 “水仙花数 “是指一个三位数,其各位数字立方和等于该数本身。例如:153是一个 “水仙花数 “,因为153=1的三次方+5的三次方+3的三次方 原地交换两个变量的值 找出4字节整数的中位数 找到整数的平方根 实现斐波那契 网络 用Java Socket编程,读服务器几个字符,再写入本地显示 反射 反射机制提供了什么功能? 反射是如何实现的 哪里用到反射机制 反射中 Class.forName 和 ClassLoader 区别 反射创建类实例的三种方式是什么 如何通过反射调用对象的方法 如何通过反射获取和设置对象私有字段的值 反射机制的优缺点 数据库 写一段 JDBC 连Oracle的程序,并实现数据查询 算法 50个人围坐一圈,当数到三或者三的倍数出圈,问剩下的人是谁,原来的位置是多少 实现一个电梯模拟器用 写一个冒泡排序 写一个折半查找 随机产生20个不能重复的字符并排序 写一个函数,传入 2 个有序的整数数组,返回一个有序的整数数组 写一段代码在遍历 ArrayList 时移除一个元素 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第四个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少 约瑟芬环游戏 正则 请编写一段匹配IP地址的正则表达式 写出一个正则表达式来判断一个字符串是否是一个数字 字符串 写一个方法,入一个文件名和一个字符串,统计这个字符串在这个文件中出现的次数。 写一个程序找出所有字符串的组合,并检查它们是否是回文串 写一个字符串反转函数,输入abcde转换成edcba代码 小游戏,倒转句子中的单词 将GB2312编码的字符串转换为ISO-8859-1编码的字符串 请写一段代码来计算给定文本内字符“A”的个数。分别用迭代和递归两种方式 编写一个截取字符串的函数,输入为一个字符串和字节数,输出为按字节截取的字符串。 但是要保证汉字不被截半个,如“我ABC”4,应该截为“我AB”,输入“我ABC汉DEF”,6,应该输出为“我ABC”而不是“我ABC+汉的半个” 给定 2 个包含单词列表(每行一个)的文件,编程列出交集 打印出一个字符串的所有排列 将一个键盘输入的数字转化成中文输出(例如:输入1234567,输出:一百二拾三万四千五百六拾七) 在Web应用开发过程中经常遇到输出某种编码的字符,如从 GBK 到 ISO8859-1等,如何输出一个某种编码的字符串 日期 计算两个日期之间的差距 欢迎工作一到五年的Java工程师朋友们加入Java填坑之路:860113481 群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

优秀的个人博客,低调大师

python基础(81道题)面试题,再也不用为没有答案发愁了

1、为什么学习Python? 人生苦短....哈哈,自己想吧!!! 2、通过什么途径学习的Python? 官网、网上视频、学习网站 3、Python和Java、PHP、C、C#、C++等其他语言的对比? 1、python代码,简介,明确,优雅,简单易懂2、开发效率高3、可扩展性强 4、简述解释型和编译型编程语言? 解释型:在执行程序时,计算机才一条一条的将代码解释成机器语言给计算机来执行编译型:是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样计算机运行该程序时可以直接以机器语言来运行此程序,运行速度很快。 5、Python解释器种类以及特点? Cpython,IPython,Jpython,pypy,IronpythonPython是一门解释器语言,代码想运行,必须通过解释器执行,Python存在多种解释器,分别基于不同语言开发,每个解释器有不同的特点,但都能正常运行Python代码,以下是常用的五种Python解释器: CPython:当 从Python官方网站下载并安装好Python2.7后,就直接获得了一个官方版本的解 释器:Cpython,这个解释器是用C语言开发的,所以叫 CPython,在命名行下运行python, 就是启动CPython解释器,CPython是使用最广的Python解释器。 IPython:IPython是基于CPython之上的一个交互式解释器,也就是说,IPython只是在交互方 式上有所增强,但是执行Python代码的功能和CPython是完全一样的,好比很多国产浏览器 虽然外观不同,但内核其实是调用了IE。 PyPy:PyPy是另一个Python解释器,它的目标是执行速度,PyPy采用JIT技术, 对Python代进行动态编译,所以可以显著提高 Python代码的执行速度。 Jython:Jython是运行在Java平台上的Python解释器,可以直接把Python代码编译成Java字节码执行。 IronPython:IronPython和Jython类似,只不过IronPython是运行在微软.Net平台上的Python解释器, 可以直接把Python代码编译成.Net的字节码。 在Python的解释器中,使用广泛的是CPython,对于Python的编译,除了可以采用以上解释器 进行编译外,技术高超的开发者还可以按照自己的需求自行编写Python解释器来执行Python代码,十分的方便! 6、位和字节的关系? 一个字节=8位 7、b、B、KB、MB、GB 的关系? 1B(字节) = 8b(位)1KB = 1024B1MB = 1024KB1GB = 1024MB 8、请至少列举5个 PEP8 规范 1、缩进:每一级4个缩进。连续跨行应该使用圆括号或大括号或者使用悬挂缩进。 2、代码长度约束 一行列数:PEP8 规定最大为79列,如果拼接url很容易超限 一个函数:不可以超过30行;直观来讲就是完整显示一个函数一个屏幕就够了,不需要上下拖动 一个类:不要超过200行代码,不要超过10个方法 一个模块:不要超过500行 3、import 不要在一句import中引用多个库 4、命名规范 5、注释 总体原则,错误的注释不如没有注释。所以当一段代码发生变化时,第一件事就是要修改注释! 9、通过代码实现如下转换: 答案: 二进制转换成十进制:v = “0b1111011” print(int('0b1111011',2)) 十进制转换成二进制:v = 18 print(bin(18)) 八进制转换成十进制:v = “011” print(int('011',8)) 十进制转换成八进制:v = 30 print(oct(30)) 十六进制转换成十进制:v = “0x12” print(int('0x12',16)) 十进制转换成十六进制:v = 87 print(hex(87)) 10、请编写一个函数实现将IP地址转换成一个整数。 如 10.3.9.12 转换规则为: 10 00001010 3 00000011 9 00001001 12 00001100 再将以上二进制拼接起来计算十进制结果:00001010 00000011 00001001 00001100 = ? 答案: def func(x): lis = x.strip().split('.') li = [bin(int(i)) for i in lis] li2 = [i.replace('0b',(10-len(i))*'0') for i in li] return int(''.join(li2),2) ret = func('10.3.9.12') print(ret) 11、python递归的最大层数? 一般计算机默认的最大递归深度在1000左右,python最大递归深度一般在4000左右,跟计算 机的性能有关系,这个数不是一个定数,可通过一下方式测试 import sys print(sys.getrecursionlimit()) print(sys.setrecursionlimit(10000)) 12、求结果: v1 = 1 or 3 -------------->1v2 = 1 and 3-------------->3v3 = 0 and 2 and 1-------->0v4 = 0 and 2 or 1--------->1v5 = 0 and 2 or 1 or 4---->1v6 = 0 or Flase and 1----->False 13、ascii、unicode、utf-8、gbk 区别? ASCII码:使用一个字节编码,所以它的范围基本是只有英文字母、数字和一些特殊符号 ,只有256个字符。Unicode:能够表示全世界所有的字节GBK:是只用来编码汉字的,GBK全称《汉字内码扩展规范》,使用双字节编码。UTF-8:是一种针对Unicode的可变长度字符编码,又称万国码。 14、字节码和机器码的区别? 机器码:是电脑CPU直接读取运行的机器指令,运行速度最快,但是非常晦涩难懂字节码:是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。 15、三元运算规则以及应用场景? 规则:为真时的结果 if 判定条件 else 为假时的结果 ```应用场景:在赋值变量的时候,可以直接加判断,然后赋值` 16、列举 Python2和Python3的区别? 1、默认编码:2-->ascii,3-->utf-8 2、print的区别:python2中print是一个语句,不论想输出什么,直接放到print关键字后面即可。python3里,print()是一个函数, 像其他函数一样,print()需要你将要输出的东西作为参数传给它。 3、input的区别: python2有两个全局函数,用在命令行请求用户输入。第一个叫input(),它等待用户输入一个python表达式(然后返回结果)。 第二个叫做raw_input(),用户输入什么他就返回什么。python3 通过input替代了他们。 4、字符串:python2中有两种字符串类型:Unicode字符串和非Unicode字符串。Python3中只有一种类型:Unicode字符串。 5、xrange() python2里,有两种方法获得一定范围内的数字:range(),返回一个列表,还有xrange(),返回一个迭代器。 python3 里,range()返回迭代器,xrange()不再存在。 更多不同:https://www.cnblogs.com/weikunzz/p/6857971.html 17、用一行代码实现数值交换: a = 1 b = 2 答案:a = 1 b = 2 a,b = b,a 18、Python3和Python2中 int 和 long的区别? python2有非浮点数准备的int和long类型。int类型最大值不能超过sys.maxint,而且这个最大值是平台相关的。可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。在python3里,只有一种整数类型int,大多数情况下,和python2中的长整型类似。 19、xrange和range的区别? python2里,有两种方法获得一定范围内的数字:range(),返回一个列表,还有xrange(),返回一个迭代器。python3 里,range()返回迭代器,xrange()不再存在。 20、文件操作时:xreadlines和readlines的区别? readlines返回一个list,xreadlines方法返回一个生成器 21、列举布尔值为False的常见值? 0, [] , () , {} , '' , False , None 22、字符串、列表、元组、字典每个常用的5个方法? 字符串:repleace,strip,split,reverse,upper,lower,join.....列表:append,pop,insert,remove,sort,count,index.....元组:index,count,__len__(),__dir__()字典:get,keys,values,pop,popitems,clear,update,items..... 23、lambda表达式格式以及应用场景? 表达式格式:lambda后面跟一个或多个参数,紧跟一个冒号,以后是一个表达式。冒号前是参数,冒号后是返回值。例如:lambda x : 2x应用场景:经常与一些内置函数相结合使用,比如说map(),filter(),sorted(),reduce()等 24、pass的作用? 1、空语句 do nothing2、保证格式完整3、保证语义完整 25、arg和*kwarg作用? 万能参数,解决了函数参数不固定的问题*arg:会把位置参数转化为tuple**kwarg:会把关键字参数转化为dict 26、is和==的区别? is:判断内存地址是否相等==:判断数值是否相等 27、简述Python的深浅拷贝以及应用场景? copy():浅copy,浅拷贝指仅仅拷贝数据集合的第一层数据deepcopy():深copy,深拷贝指拷贝数据集合的所有层 28、Python垃圾回收机制? python采用的是引用计数机制为主,标记-清除和分代收集(隔代回收、分代回收)两种机制为辅的策略 计数机制 Python的GC模块主要运用了引用计数来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除” 解决容器对象可能产生的循环引用的问题。通过分代回收以空间换取时间进一步提高垃圾回收的效率。 标记-清除: 标记-清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象 缺点:该机制所带来的额外操作和需要回收的内存块成正比。 隔代回收 原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”, 垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾, 就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量, 如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。 29、python的可变类型和不可变类型? 不可变类型(数字、字符串、元组、不可变集合)可变类型(列表、字典、可变集合) 30、求结果: v = dict.fromkeys(['k1','k2'],[]) v['k1'].append(666) print(v) v['k1'] = 777 print(v) 答案:{'k1':[666],'k2':[666]} {'k1':777,'k2':[666]} 解析:formkeys()默认参数为可变数据类型时有坑 31、求结果: def num(): return [lambda x: i*x for i in range(4)] print([m(2) for m in num()]) 答案:[6, 6, 6, 6] 解析: 问题的本质在与python中的属性查找规则,LEGB(local,enclousing,global,bulitin), 在上面的例子中,i就是在闭包作用域(enclousing),而Python的闭包是 迟绑定 , 这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的 所以:[lambda x: i*x for i in range(4)]打印出来是含有四个内存地址的列表,每个内存地址中的i 在在本内存中都没有被定义,而是通过闭包作用域中的i值,当for循环执行结束后,i的值等于3,所以 再执行[m(2) for m in num()]时,每个内存地址中的i值等于3,当x等于2时,打印出来的结果都是6, 从而得到结果[6, 6, 6, 6]。 32、列举常见的内置函数? map,filter,zip,len,bin,oct,hex,int,float,bool,sum,min,max,str,list,tuple,dict,range,next,hash,help,id..... 33、filter、map、reduce的作用? filter(function,iterable)过滤函数map(function,iterable)循环函数reduce(function, iterable)累积函数 34、一行代码实现9*9乘法表。 lis = ['%s*%s=%s'%(i,j,i*j) for i in range(1,10) for j in range(i,10)] 35、如何安装第三方模块?以及用过哪些第三方模块? pip3 imstall 模块名django,Matplotlib,Tornado,PyGame 36、至少列举8个常用模块都有那些? os,sys,time,random,re,hashlib,logging,json,pickle.... 37、re的match和search区别? match:从字符串的开头位置匹配,必须以此为开头search:从开头开始查,找到符合的就返回结果 38、什么是正则的贪婪匹配? 正则表达式一般趋向于最大长度匹配 39、求结果: a. [ i % 2 for i in range(10) ] ===>[0,1,0,1,0,1,0,1,0,1]b. ( i % 2 for i in range(10) )===>返回一个生成器的内存地址 40、求结果: a. 1 or 2 =========>1b. 1 and 2 ========>2c. 1 < (2==2)======>falsed. 1 < 2 == 2======>ture 41、def func(a,b=[]) 这种写法有什么坑? def func(a,b=[]): b.append(a) print(b) 函数的第二个默认参数是一个list,当第一次执行的时候实例化了一个list, 第二次执行还是用第一次执行的时候实例化的地址存储,以后每次实例化都是 42、如何实现 "1,2,3" 变成 ['1','2','3'] ? a = "1,2,3"li = a.split(',') 43、如何实现[‘1’,’2’,’3’]变成[1,2,3] ? li = ['1','2','3']lis = list(map(lambda x:int(x) li)) 44、比较: a = [1,2,3] 和 b = [(1),(2),(3) ] 以及 b = [(1,),(2,),(3,) ] 的区别? a = [1,2,3]正常的列表b = [(1),(2),(3)] 虽然列表的每个元素加上了括号,但是当括号内只有一个元素并且没有逗号时,其数据类型是元素本身的数据类型b = [(1,),(2,),(3,)]列表中的元素类型都是元组类型 45、如何用一行代码生成[1,4,9,16,25,36,49,64,81,100] ? li = [x*x for x in range(1,11)] 46、一行代码实现删除列表中重复的值 ? li = [1, 1, 1, 23, 3, 4, 4]new_li = list(set(li))new_li.sort(key=li.index) 47、如何在函数中设置一个全局变量 ? 使用python的内置语法 globals 全局变量 48、logging模块的作用?以及应用场景? logging模块的作用:1、程序调试2、了解软件程序运行情况,是否正常3、软件程序运行故障分析与问题定位应用场景:网站的运维工作,程序实时监控 49、请用代码简答实现stack 。 def Stack(object): def __init__(self): self.stack = [] def push(self,value): # 进栈 self.stack.append(value) def pop(self): # 出栈 if self.stack: self.stack.pop() else: raise LookupError('stack is empty!') def is_empty(self): # 查看stack是否为空 reture bool(self.stack) def top(self): # 取出stack中最新的值 return self.stack[-1] 50、常用字符串格式化哪几种? 1、%s %d2、format格式化输出3、print(f'内容{变量名}') 51、简述 生成器、迭代器、可迭代对象 以及应用场景? 生成器:在 Python 中,一边循环一边计算的机制,称为 生成器(generator), 通过next()取值,两种表现形式1、将列表生成式的[]改为()2、含有yield关键字的函数 应用场景:优化代码,节省内存 迭代器:是访问集合元素的一种方式。迭代器同时实现了__iter__和__next__方法 可迭代对象:只要实现了__iter__方法的对象就是可迭代对象 52、用Python实现一个二分查找的函数。 lis = [0, 1, 3, 4, 5, 6, 7, 9, 10, 11,12,16,17] def two_find(x, lis, start=0, end=None): if end == None:end = len(lis) - 1 num = (end - start) // 2 + start if end > start: if lis[num] > x: return two_find(x, lis, start=start, end=num) elif lis[num] < x: return two_find(x, lis, start=num + 1, end=end) elif lis[num] == x: return num elif lis[end] == x:return end else:return None print(two_find(17, lis)) 53、谈谈你对闭包的理解? 在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。一般情况下,在我们认知当中,如果一个函数结束,函数的内部所有东西都会释放掉,还给内存,局部变量都会消失。但是闭包是一种特殊情况,如果外函数在结束的时候发现有自己的临时变量将来会在内部函数中用到,就把这个临时变量绑定给了内部函数,然后自己再结束。 54、os和sys模块的作用? os模块负责程序与操作系统的交互,提供了访问操作系统底层的接口;sys模块负责程序与python解释器的交互,提供了一系列的函数和变量,用于操控python的运行时环境。 55、如何生成一个随机数? import random def rdm(n): lis = [] for i in range(n): n = random.randint(1,9) lis.append(str(n)) s = ''.join(lis) return int(s) 56、如何使用python删除一个文件? import osos.remove(r'path') 57、谈谈你对面向对象的理解? 面向对象的程序设计的核心是对象(上帝式思维),要理解对象为何物,必须把自己当成上帝,上帝眼里世间存在的万物皆为对象,不存在的也可以创造出来。对象是特征和技能的结合,其中特征和技能分别对应对象的数据属性和方法属性。优点是:解决了程序的扩展性。对某一个对象单独修改,会立刻反映到整个体系中,如对游戏中一个人物参数的特征和技能修改都很容易。缺点:可控性差,无法向面向过程的程序设计流水线式的可以很精准的预测问题的处理流程与结果,面向对象的程序一旦开始就由对象之间的交互解决问题,即便是上帝也无法预测最终结果。应用场景:需求经常变化的软件,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。 58、Python面向对象中的继承有什么特点? 1:在继承中基类的构造(__init__()方法)不会被自动调用,它需要在其派生类的构造中亲自专门调用。 2:在调用基类的方法时,需要加上基类的类名前缀,且需要带上self参数变量。 区别于在类中调用普通函数时并不需要带上self参数 3:Python总是首先查找对应类型的方法,如果它不能在派生类中找到对应的方法,它才开始到基类中逐个查找。 (先在本类中查找调用的方法,找不到才去基类中找)。 59、面向对象深度优先和广度优先是什么? Python的类可以继承多个类,那么其寻找类方法的方式有两种: 当类是经典类时(主要在python2版本中的没有主动继承object的类),多继承情况下,会按照深度优先方式查找 当类是新式类时(python3版本中的所有类和python2中主动继承object的类),多继承情况下,会按照广度优先方式查找 简单点说就是:经典类是纵向查找,新式类是横向查找 60、面向对象中super的作用? 1、super在面向对象继承类中代指父类,书写方法super(类名,self).属性或者方法或super().属性或者方法 2、super方法可以增加类之间调用的灵活性,当父类名发生变化时不必修改 3、super方法在类的多继承时可以简化代码,避免代码冗余 4、super机制里可以保证公共父类仅被执行一次,执行的顺序遵循MRO,广度优先查询方法 61、是否使用过functools中的函数?其作用是什么? functools用于高阶函数:指那些作用于函数或者返回其他函数的函数。通常情况下,只要是 可以被当做函数调用的对象就是这个模块的目标。 62、列举面向对象中带双下划线的特殊方法,如:new、init __new__:构造方法,创建一个对象,实例化时第一个被执行,返回一个创建好的对象及__init__(self)的self, 只有继承了object的类才会有这个方法 __init__:初始化方法,__init__在__new__的基础上完成一些其它初始化的动作,__init__没有返回值 63、如何判断是函数还是方法? 函数和方法都封装了一些独立的功能,如果在类中定义的函数那就是方法(对象或者类名点方法名调用), 否则就是函数(函数名()直接调用) 64、静态方法和类方法区别? 静态方法:是既不是用类中的属性又不使用对象中的属性,由类或者对象调用的方法,依赖python装饰器@staticmethod来实现 类方法:只使用类中的静态变量,一般都是由类调用,依赖python装饰器@classmethod来实现 65、列举面向对象中的特殊成员以及应用场景? __call__:对象的构造方法,对象加上(),可以触发这个类的__call__方法。 __len__:内置函数的len函数是依赖类中的__len__方法 __eq__:判断值是否相等的时候依赖__eq__方法 __hash__:判断hash值是否相等的时候依赖__hash__方法(拓展:set的去重机制其实就是根据__hash__和__eq__方法实现的) __str__:和str() print() %s 都是息息相关的,返回值一定是字符串类型 __repr__:和 repr() %r都是息息相关的,在没有__str__方法时,__repr__可以完全取代__str__。 __del__ 析构方法,对应着一个对象的删除之前执行的内容 66、1、2、3、4、5 能组成多少个互不相同且无重复的三位数 count = 0 for i in range(1,6): for j in range(1,6): for k in range(1,6): if (i != j) and (i != k) and (j != k): count += 1 if count % 6: print(f'{i}{j}{k}', end='|') else: print(f'{i}{j}{k}') print(count) 67、什么是反射?以及应用场景? 定义:通过用字符串数据类型的变量名来访问这个变量的值,在python面向对象中的反射,通过字符串的形式操作对象相关的属性或方法. 应用场景:用于处理通过用户输入,文件读取,或者网络传输所得到的字符串形式的指令来完成对应的操作 68、metaclass作用?以及应用场景? metaclass,直译为元类,简单的解释就是:当我们定义了类以后,就可以根据这个类创建出实例, 所以:先定义类,然后创建实例。但是如果我们想创建出类呢?那就必须根据metaclass创建出类, 所以:先定义metaclass,然后创建类。换句话说,你可以把类看成是metaclass创建出来的“实例” 69、用尽量多的方法实现单例模式。 1、基于__new__()方法 class Person: def __new__(cls, *args, **kwargs): if not hasattr(cls,cls._instance): # cls._instance = object.__new__(cls) cls._instance = super().__new__(cls) return cls._instance 2、基于模块导入方式,现在一个py文件中写好一个类,实例化一个对象。以后用这个类直接导入这个模块就是单例模式。 3、基于装饰器方法实现 def singleton(cls, *args, **kwargs): instance_dic = {} def inner(*args, **kwargs): if cls not in instance_dic: instance_dic['cls'] = cls(*args, **kwargs) return instance_dic['cls'] return inner @singleton class Person: pass 70、装饰器的写法以及应用场景。 装饰器的写法: def wrapper(func): def inner(*args,**kwargs): '被装饰之前的操作' ret = func(*args,**kwargs) '被装饰之后的操作' return ret return inner 装饰器的应用场景: 比如注册登录、插入日志,性能测试,事务处理,缓存等等场景 71、异常处理写法以及如何主动跑出异常(应用场景) 异常处理的常规写法: try: 执行的主体函数 except Exception as e: print(str(e)) 主动抛出异常: raise TypeError('出现了不可思议的异常')#TypeError可以是任意的错误类型 72、什么是面向对象的mro MRO(Method Resolution Order 方法解析顺序)是面向对象中用于查询类的多继承的继承顺序的方法, 它是基于算法来实现的,不同的算法实现的MRO的顺序不同 73、isinstance作用以及应用场景? isinstance作用是来判断一个对象是否是一个已知的类型 74、写代码并实现: Given an array of integers, return indices of the two numbers such that they add up to a specific target. You may assume that each input would have exactly one solution, and you may not use the same element twice. Example: Given nums = [2, 7, 11, 15], target = 9, Because nums[0] + nums[1] = 2 + 7 = 9, return [0, 1] 代码实现 def func(li,target): try: for i in range(0,len(li)): num = target-li[i] if num in li: return [i,li.index(num)] except:print('li类型为数组类型,内的元素需是整型,target也为整型,请检查') else:return None 75、json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型? 1、可以处理的数据类型是 string、int、list、tuple、dict、bool、null 2、定制支持datetime类型 --------------------------官方文档的memo----------------------------------------------- >>> import json >>> class ComplexEncoder(json.JSONEncoder): ... def default(self, obj): ... if isinstance(obj, complex): ... return [obj.real, obj.imag] ... return json.JSONEncoder.default(self, obj) ... >>> dumps(2 + 1j, cls=ComplexEncoder) '[2.0, 1.0]' >>> ComplexEncoder().encode(2 + 1j) '[2.0, 1.0]' >>> list(ComplexEncoder().iterencode(2 + 1j)) ['[', '2.0', ', ', '1.0', ']'] ---------------------------------------------------------------------------------------- import json import datetime ret = datetime.datetime.now() class CJsonEncoder(json.JSONEncoder): def default(self, obj): if isinstance(obj, datetime.date): return obj.strftime('%Y-%m-%d %H:%M:%S') else: return json.JSONEncoder.default(self, obj) print(json.dumps(ret,cls=CJsonEncoder)) 76、json序列化时,默认遇到中文会转换成unicode,如果想要保留中文怎么办? 在序列化是将json.dumps中的默认参数ensure_ascii改为False就可以保留中文了 json.dumps(obj,ensure_ascii=False) 77、什么是断言?应用场景? assert 条件,'自定义错误提示(可有可无)' 例:assert 1 == 0,'这是一个低级的错误' 合约式设计是断言的经典应用,在一个正确的程序里,所有的前置条件和后置条件都将得到处理。 78、使用代码实现查看列举目录下的所有文件。 方法一:递归处理 import os url = r'C:\Users\Mr.Wang\PycharmProjects\untitled\前段学习' def check_file(url,li = []): if os.path.isdir(url): file_list = os.listdir(url) for ret in file_list: base_url = os.path.join(url,ret) if os.path.isfile(base_url): li.append(ret) else: check_file(base_url) return li else:return os.path.basename(url) 方法二:堆栈的思想处理 import os url = r'C:\Users\Mr.Wang\PycharmProjects\untitled\python基础' lis = [url] while lis: url = lis.pop() ret_list = os.listdir(url) for name in ret_list: abs_path = os.path.join(url,name) if os.path.isdir(abs_path): lis.append(abs_path) else:print(name) 79、简述 yield和yield from关键字。 yield 是一个类似 return 的关键字,只是这个函数返回的是个生成器当你调用这个函数的时候, 函数内部的代码并不立马执行 ,这个函数只是返回一个生成器对象,当你使用for进行迭代的时候, 函数中的代码才会执行 yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来, 这样二者可以直接发送和产出值,还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。 有了这个结构,协程可以通过以前不可能的方式委托职责。 更多解析详见:http://blog.gusibi.com/post/python-coroutine-yield-from/ 80、代码实现六位随机验证码 import random s = '' for i in range(6): num = random.randint(0,9) alpha1 = chr(random.randint(65,90)) alpha2 = chr(random.randint(97,122)) ret = random.choice([num,alpha1,alpha2]) s += str(ret) print(s) 81、代码实现随机发红包功能 import random def red_packge(money,num): li = random.sample(range(1,money*100),num-1) li.extend([0,money*100]) li.sort() return [(li[index+1]-li[index])/100 for index in range(num)] ret = red_packge(100,10) print(ret) --------------------------生成器版------------------------------------------- import random def red_packge(money,num): li = random.sample(range(1,money*100),num-1) li.extend([0,money*100]) li.sort() for index in range(num): yield (li[index+1]-li[index])/100 ret = red_packge(100,10) print(ret) ---------------------------九九八十一难后继续闯关东:------------------------------- 1、请尽可能列举python列表的成员方法,并给出列表操作的答案: (1) a=[1, 2, 3, 4, 5], a[::2]=? a[-2:]=? a[::2]=[1,3,5], a[-2:] = [4,5] (2)一行代码实现对列表a中的偶数位置的元素进行加3后求和? sum([i+3 for i in a[::2]]) (3)将列表a的元素顺序打乱,再对a进行排序得到列表b,然后把a和b按元素顺序构造一个字典d。 import random random.shuffle(a) b=a.sort() d={} for i in range(len(a)):d[a[i]] = b[i] 2、 Python自省 自省就是面向对象的语言所写的程序在运行时,就能知道对象的类型。也就是程序运行时能够获得对象的类型。比如type(),dir(),getattr(),hasattr(),isinstance()。 3、Python是如何进行内存管理的? 从三个方面来说,一对象的引用计数机制,二垃圾回收机制,三内存池机制 一、对象的引用计数机制 Python内部使用引用计数,来保持追踪内存中的对象,所有对象都有引用计数。 引用计数增加的情况: 1,一个对象分配一个新名称 2,将其放入一个容器中(如列表、元组或字典) 引用计数减少的情况: 1,使用del语句对对象别名显示的销毁 2,引用超出作用域或被重新赋值 sys.getrefcount( )函数可以获得对象的当前引用计数 多数情况下,引用计数比你猜测得要大得多。对于不可变数据(如数字和字符串),解释器会在程序的不同部分共享内存,以便节约内存。 二、垃圾回收 1,当一个对象的引用计数归零时,它将被垃圾收集机制处理掉。 2,当两个对象a和b相互引用时,del语句可以减少a和b的引用计数,并销毁用于引用底层对象的名称。然而由于每个对象都包含一个对其他对象的应用,因此引用计数不会归零,对象也不会销毁。(从而导致内存泄露)。为解决这一问题,解释器会定期执行一个循环检测器,搜索不可访问对象的循环并删除它们。 三、内存池机制 Python提供了对内存的垃圾收集机制,但是它将不用的内存放到内存池而不是返回给操作系统。 1,Pymalloc机制。为了加速Python的执行效率,Python引入了一个内存池机制,用于管理对小块内存的申请和释放。 2,Python中所有小于256个字节的对象都使用pymalloc实现的分配器,而大的对象则使用系统的malloc。 3,对于Python对象,如整数,浮点数和List,都有其独立的私有内存池,对象间不共享他们的内存池。也就是说如果你分配又释放了大量的整数,用于缓存这些整数的内存就不能再分配给浮点数。 4、介绍一下except的用法和作用? try…except…except…[else…][finally…] -- 执行try下的语句,如果引发异常,则执行过程会跳到except语句。对每个except分支顺序尝试执行,如果引发的异常与except中的异常组匹配,执行相应的语句。如果所有的except都不匹配,则异常会传递到下一个调用本代码的最高层try代码中。 -- try下的语句正常执行,则执行else块代码。如果发生异常,就不会执行 -- 如果存在finally语句,最后总是会执行。 5、如何用Python来进行查询和替换一个文本字符串? 可以使用re模块中的sub()函数或者subn()函数来进行查询和替换,比replace的功能更强大!!! 格式:sub(replacement, string[,count=0])(replacement是被替换成的文本,string是需要被替换的文本,count是一个可选参数,指最大被替换的数量) import re p=re.compile("blue|white|red") print(p.sub('colour','blue socks and red shoes')) print(p.sub('colour','blue socks and red shoes',count=1)) subn()方法执行的效果跟sub()一样,不过它会返回一个二维数组,包括替换后的新的字符串和总共替换的数量 6、有没有一个工具可以帮助查找python的bug和进行静态的代码分析? PyChecker是一个python代码的静态分析工具,它可以帮助查找python代码的bug, 会对代码的复杂度和格式提出警告 Pylint是另外一个工具可以进行codingstandard检查

优秀的个人博客,低调大师

Python机器学习算法面试题,唯一的缺点就是资料太充足,史上最全!

朴素贝叶斯P(A∩B)=P(A)P(B|A)=P(B)P(A|B) 所以有:P(A|B)=P(B|A)*P(A)/P(B) 对于给出的待分类项,求解在此项出现的条件下各个目标类别出现的概率,哪个最大,就认为此待分类项属于哪个类别 工作原理 假设现在有样本x=(a1,a2,a3,…an)这个待分类项(并认为x里面的特征独立)再假设现在有分类目标Y={y1,y2,y3,y4..yn}那么max(P(y1|x),P(y2|x),P(y3|x)..P(yn|x))中的最大者就是最终的分类类别而P(yi|x)=p(x|yi)*P(yi)/P(x)因为x对于每个分类目标来说都一样,所以就是求max(P(x|yi)*p(yi))P(x|yi)p(yi)=p(yi)PI(P(ai|yi)) (PI表示连乘)而具体的p(ai|yi)和p(yi)都是能从训练样本中统计出来p(ai|yi)表示该类别下该特征出现的概率p(yi)表示全部类别中这个这个类别出现的概率好的,就是这么工作的^_^工作流程 准备阶段确定特征属性,并对每个特征属性进行适当划分,然后由人工对一部分待分类项进行分类,形成训练样本。训练阶段计算每个类别在训练样本中的出现频率及每个特征属性划分对每个类别的条件概率估计应用阶段使用分类器进行分类,输入是分类器和待分类样本,输出是样本属于的分类类别属性特征 特征为离散值时直接统计即可(表示统计概率)特征为连续值的时候假定特征符合高斯分布:g(x,n,u)那么p(ak|yi)=g(xk,ni,ui)Laplace校准(拉普拉斯校验) 当某个类别下某个特征划分没有出现时,会有P(a|y)=0,就是导致分类器质量降低,所以此时引入Laplace校验,就是对没类别下所有划分的计数加1。 遇到特征之间不独立问题 参考改进的贝叶斯网络,使用DAG来进行概率图的描述 优缺点 朴素贝叶斯的优点: 对小规模的数据表现很好,适合多分类任务,适合增量式训练。缺点:对输入数据的表达形式很敏感(离散、连续,值极大极小之类的)。逻辑回归和线性回归 LR回归是一个线性的二分类模型,主要是计算在某个样本特征下事件发生的概率,比如根据用户的浏览购买情况作为特征来计算它是否会购买这个商品,抑或是它是否会点击这个商品。然后LR的最终值是根据一个线性和函数再通过一个sigmod函数来求得,这个线性和函数权重与特征值的累加以及加上偏置求出来的,所以在训练LR时也就是在训练线性和函数的各个权重值w。 关于这个权重值w一般使用最大似然法来估计,比如yi=1的概率是pi,则yi=0的概率是1-pi,那么观测概率为p(yi)=pi^yi(1-pi)^(1-yi)这个这个最大似然函数为(hw(xi)^yi(1-hw(xi))^(1-yi))连乘,对这个似然函数取对数之后就会得到的表达式L(w)=sigma(yilog(hw(xi))-(1-yi)log(1-hw(xi)))=sigma(yi(wxi)-log(1+exp(wxi))),估计这个L(w)的极大值就可以得到w的估计值。 所以求解问题就变成了这个最大似然函数的最优化问题,这里通常会采样随机梯度下降法和拟牛顿迭代法来进行优化 梯度下降法 如果hw(x)=1/(1-e^(-wx)), 则cost function=-1/m sigma(yilog(hw(xi)+(1-yi)*log(1-hw(xi)))=j(w) 这里就成了就min(j(w)) 所以更新w的过程为 w:=w-lamea*j(w)’ (求导) w:=w-lamea 1/msigmam-yi)*xi) 直到j(w)不能再的时候停止 梯度下降法的最大问题就是会陷入局部最优,并且每次在对当前样本计算cost的时候都需要去遍历全部样本才能得到cost值,这样计算速度就会慢很多(虽然在计算的时候可以转为矩阵乘法去更新整个w值) 所以现在好多框架(mahout)中一般使用随机梯度下降法,它在计算cost的时候只计算当前的代价,最终cost是在全部样本迭代一遍之求和得出,还有他在更新当前的参数w的时候并不是依次遍历样本,而是从所有的样本中随机选择一条进行计算,它方法收敛速度快(一般是使用最大迭代次数),并且还可以避免局部最优,并且还很容易并行(使用参数服务器的方式进行并行) 这里SGD可以改进的地方就是使用动态的梯度值alpha=0.04*(1.0+n+i)+Rate 其他优化方法 拟牛顿法(记得是需要使用Hessian矩阵和cholesky分解)BFGSL-BFGS优缺点:无需选择学习率α,更快,但是更复杂关于LR的过拟合问题: 如果我们有很多的特性,在训练集上拟合得很好,但是在预测集上却达不到这种效果 减少feature个数(人工定义留多少个feature、算法选取这些feature) 正则化(留下所有的feature,但对于部分feature定义其parameter非常小),在cost上加 lamea(sigma(w^2)),同时w的更新变为w:=w-rate 1/msigmam-yi)xi+ (lamea/m)w。注意:这里的w0不受正则化影响关于LR的多分类:softmax softmax:假设离散型随机变量Y的取值集合是{1,2,..,k},则多分类的LR为 P(Y=a|x)=exp(wax)/(1-1到k求和(wkx)) 1 这里会输出当前样本下属于哪一类的概率,并且满足全部概率加起来=1 关于softmax和k个LR的选择 如果类别之间是否互斥(比如音乐只能属于古典音乐、乡村音乐、摇滚月的一种)就用softmax 否则类别之前有联系(比如一首歌曲可能有影视原声,也可能包含人声,或者是舞曲),这个时候使用k个LR更为合适 优缺点: Logistic回归优点: 实现简单;分类时计算量非常小,速度很快,存储资源低;缺点: 容易欠拟合,一般准确度不太高只能处理两分类问题(在此基础上衍生出来的softmax可以用于多分类),且必须线性可分;KNN算法 给一个训练数据集和一个新的实例,在训练数据集中找出与这个新实例最近的k个训练实例,然后统计最近的k个训练实例中所属类别计数最多的那个类,就是新实例的类 三要素: k值的选择距离的度量(常见的距离度量有欧式距离,马氏距离等)分类决策规则 (多数表决规则)k值的选择 k值越小表明模型越复杂,更加容易过拟合但是k值越大,模型越简单,如果k=N的时候就表明无论什么点都是训练集中类别最多的那个类所以一般k会取一个较小的值,然后用过交叉验证来确定这里所谓的交叉验证就是将样本划分一部分出来为预测样本,比如95%训练,5%预测,然后k分别取1,2,3,4,5之类的,进行预测,计算最后的分类误差,选择误差最小的kKNN的回归 在找到最近的k个实例之后,可以计算这k个实例的平均值作为预测值。或者还可以给这k个实例添加一个权重再求平均值,这个权重与度量距离成反比(越近权重越大)。 优缺点: KNN算法的优点: 思想简单,理论成熟,既可以用来做分类也可以用来做回归;可用于非线性分类;训练时间复杂度为O(n);准确度高,对数据没有假设,对outlier不敏感;缺点: 计算量大;样本不平衡问题(即有些类别的样本数量很多,而其它样本的数量很少);需要大量的内存;KD树 KD树是一个二叉树,表示对K维空间的一个划分,可以进行快速检索(那KNN计算的时候不需要对全样本进行距离的计算了) 构造KD树 在k维的空间上循环找子区域的中位数进行划分的过程。 假设现在有K维空间的数据集T={x1,x2,x3,…xn},xi={a1,a2,a3..ak} 首先构造根节点,以坐标a1的中位数b为切分点,将根结点对应的矩形局域划分为两个区域,区域1中a1b构造叶子节点,分别以上面两个区域中a2的中位数作为切分点,再次将他们两两划分,作为深度1的叶子节点,(如果a2=中位数,则a2的实例落在切分面)不断重复2的操作,深度为j的叶子节点划分的时候,索取的ai 的i=j%k+1,直到两个子区域没有实例时停止KD树的搜索 首先从根节点开始递归往下找到包含x的叶子节点,每一层都是找对应的xi将这个叶子节点认为是当前的“近似最近点”递归向上回退,如果以x圆心,以“近似最近点”为半径的球与根节点的另一半子区域边界相交,则说明另一半子区域中存在与x更近的点,则进入另一个子区域中查找该点并且更新”近似最近点“重复3的步骤,直到另一子区域与球体不相交或者退回根节点最后更新的”近似最近点“与x真正的最近点KD树进行KNN查找 通过KD树的搜索找到与搜索目标最近的点,这样KNN的搜索就可以被限制在空间的局部区域上了,可以大大增加效率。 KD树搜索的复杂度 当实例随机分布的时候,搜索的复杂度为log(N),N为实例的个数,KD树更加适用于实例数量远大于空间维度的KNN搜索,如果实例的空间维度与实例个数差不多时,它的效率基于等于线性扫描。 SVM、SMO 对于样本点(xi,yi)以及svm的超平面:wix+b=0 函数间隔:yi(wxi+b)几何间隔:yi(wxi+b)/||w||,其中||w||为w的L2范数,几何间隔不会因为参数比例的改变而改变svm的基本想法就是求解能正确划分训练样本并且其几何间隔最大化的超平面。线性SVM问题 yi(wxi+b)/||w||>=d (使用几何间隔) 求max(d) 那么假设d’=d||w|| 则将问题转为:yi(wxi+b)>=1,max(d’/||w||) 由于d’的成比例增减不会影响实际间距,所以这里的取d’=1,又因为max(1/||w||)=min(1/2||w||^2) 所以最终的问题就变为了 yi(wxi+b)>=1,min(1/2*||w||^2) 这样就变成了一个凸的二次规划化,可以将其转换为拉格朗日函数,然后使用对偶算法来求解 对偶求解 L(w,b,a)=1/2||w||^2-sigma(aiyi(wxi+b))+sigma(ai) 其中a={a1,a2..an}为拉格朗日向量 根据对偶性质 原始问题就是求对偶问题的极大极小max[a]min[w,b]L(w,b,a) 先求L对w,b的极小,再求对a的极大 求min[w,b]L(w,b,a): L’(w)=w-sigma(aiyixi)=0 L’(b)=sigma(aiyi)=0; 代入后可得min[w,b]L(w,b,a)=-1/2*sigma(sigma(aiajyiyj(xi·xj)))+sigma(ai) 求min[w,b]L(w,b,a)对a的极大 max[a] -1/2*sigma(sigma(aiajyiyj(xi·xj)))+sigma(ai) sigma(aiyi)=0 转成等价的对偶形式就是 min[a] 1/2*sigma(sigma(aiajyiyj(xi·xj)))-sigma(ai) sigma(aiyi)=0 假如求解出来的a为a^=(a1,a2,…an) 则得到最优的w,b分别为 w^=sigma(aiyixi) b^=yj-sigma(aiyi(xi·xj)) 所以,最终的决策分类面为 f=sign(sigma(aiyi(x·xi))+b^ 也就是说,分类决策函数只依赖于输入x与训练样本的输入的内积 与分离超平面最近的样本点称为支持向量损失函数 经验损失函数:sigma(1-yi(wxi+b)) (注意,如果该值小于0时直接取0即可) 合页损失函数:sigma(1-yi(wi+b)) + leama||w||^2 后面的是L2正则项 为什么要引入对偶算法 对偶问题往往更加容易求解(结合拉格朗日和kkt条件)可以很自然的引用核函数(拉格朗日表达式里面有内积,而核函数也是通过内积进行映射的)核函数 将输入特征x(线性不可分)映射到高维特征R空间,可以在R空间上让SVM进行线性可以变,这就是核函数的作用 多项式核函数:K(x,z)=(x*z+1)^p高斯核函数:K(x,z)=exp(-(x-z)^2/a^2) a为均值字符串核函数:好像用于文本匹配、检索之类的,不懂SVM优缺点 优点: 使用核函数可以向高维空间进行映射使用核函数可以解决非线性的分类分类思想很简单,就是将样本与决策面的间隔最大化分类效果较好缺点: 对大规模数据训练比较困难,因为它是用二次规划来求解的无法直接支持多分类,但是可以使用间接的方法来做SMO SMO是用于快速求解SVM的 它选择凸二次规划的两个变量,其他的变量保持不变,然后根据这两个变量构建一个二次规划问题,这个二次规划关于这两个变量解会更加的接近原始二次规划的解,通过这样的子问题划分可以大大增加整个算法的计算速度,关于这两个变量: 其中一个是严重违反KKT条件的一个变量另一个变量是根据自由约束确定,好像是求剩余变量的最大化来确定的。SVM多分类问题 直接法直接在目标函数上进行修改,将多个分类面的参数求解合并到一个最优化问题中,通过求解该优化就可以实现多分类(计算复杂度很高,实现起来较为困难)间接法一对多其中某个类为一类,其余n-1个类为另一个类,比如A,B,C,D四个类,第一次A为一个类,{B,C,D}为一个类训练一个分类器,第二次B为一个类,{A,C,D}为另一个类,按这方式共需要训练4个分类器,最后在测试的时候将测试样本经过这4个分类器f1(x),f2(x),f3(x)和f4(x),取其最大值为分类器(这种方式由于是1对M分类,会存在偏置,很不实用)一对一(libsvm实现的方式)任意两个类都训练一个分类器,那么n个类就需要n*(n-1)/2个svm分类器。还是以A,B,C,D为例,那么需要{A,B},{A,C},{A,D},{B,C},{B,D},{C,D}为目标共6个分类器,然后在预测的将测试样本通过这6个分类器之后进行投票选择最终结果。(这种方法虽好,但是需要n*(n-1)/2个分类器代价太大,不过有好像使用循环图来进行改进)决策树 决策树是一颗依托决策而建立起来的树。 ID3 首先是针对当前的集合,计算每个特征的信息增益然后选择信息增益最大的特征作为当前节点的决策决策特征根据特征不同的类别划分到不同的子节点(比如年龄特征有青年,中年,老年,则划分到3颗子树)然后继续对子节点进行递归,直到所有特征都被划分S(C,ai)=-sigma(pilog(pi)) 一个属性中某个类别的熵 pi=P(yi|ai) pi表示ai情况下发生yi的概率,也即是统计概率 S(C,A)=sigma(P(A=ai)S(ai)) 整个属性的熵,为各个类别的比例与各自熵的加权求和 Gain(C,A)=S(C)-S(C,A) 增益表示分类目标的熵减去当前属性的熵,增益越大,分类能力越强 (这里前者叫做经验熵,表示数据集分类C的不确定性,后者就是经验条件熵,表示在给定A的条件下对数据集分类C的不确定性,两者相减叫做互信息,决策树的增益等价于互信息) 比如说当前属性是是否拥有房产,分类是是否能偿还债务 现在: 有用房产为7个,4个能偿还债务,3个无法偿还债务然后无房产为3个,其中1个能偿还债务,2个无法偿还债务然后S(有房产)=-(4/7log4/7+3/7log3/7) S(无房产)=-(1/3log1/3+2/3log2/3) 其中S(分类)=-(5/10log5/10+5/10log5/10) 最终的增益=S(分类)-(7/10S(有房产)+3/10S(无房产)) 最大越好 关于损失函数 设树的叶子节点个数为T,t为其中一个叶子节点,该叶子节点有Nt个样本,其中k类的样本有Ntk个,H(t)为叶子节点上的经验熵,则损失函数定义为 Ct(T)=sigma(Nt*H(t))+ lamdba |T| 其中H(t)=sigma(Ntk/Nt*log(Ntk/Nt)) 代入可以得到Ct(T)=sigma(sigma(Ntk*log(Ntk/Nt)))+lamdba|T| 最终有Ct(T)=C(T)+ lamdba|T| lamdba|T|为正则化项,leama是用于调节比率 决策树的生成只考虑了信息增益 C4.5 它是ID3的一个改进算法,使用信息增益率来进行属性的选择 splitInformation(S,A)=-sigma(|Si|/|S|*log2(|Si|/|S|)) GainRatio(S,A)=Gain(S,A)/splitInformation(S,A) 优缺点: 准确率高,但是子构造树的过程中需要进行多次的扫描和排序,所以它的运算效率较低 Cart 分类回归树(Classification And Regression Tree)是一个决策二叉树,在通过递归的方式建立,每个节点在分裂的时候都是希望通过最好的方式将剩余的样本划分成两类,这里的分类指标: 分类树:基尼指数最小化(gini_index)回归树:平方误差最小化分类树: 首先是根据当前特征计算他们的基尼增益选择基尼增益最小的特征作为划分特征从该特征中查找基尼指数最小的分类类别作为最优划分点将当前样本划分成两类,一类是划分特征的类别等于最优划分点,另一类就是不等于针对这两类递归进行上述的划分工作,直达所有叶子指向同一样本目标或者叶子个数小于一定的阈值gini用来度量分布不均匀性(或者说不纯),总体的类别越杂乱,GINI指数就越大(跟熵的概念很相似) gini(ai)=1-sigma(pi^2) pi当前数据集中第i类样本的比例 gini越小,表示样本分布越均匀(0的时候就表示只有一类了),越大越不均匀 基尼增益gini_gain=sigma(Ni/N*gini(ai)) 表示当前属性的一个混乱 Ni/N表示当前类别占所有类别的概率 最终Cart选择GiniGain最小的特征作为划分特征 以ID3中的贷款的那棵树为样例: gini(有房产)=1-((3/7)^2+(4/7)^2) //基尼指数 gini(无房产)=1-((1/3)^2+(2/3)^2) gini_gain=7/10gini(有房产)+3/10gini(无房产) //基尼增益 回归树: 回归树是以平方误差最小化的准则划分为两块区域遍历特征计算最优的划分点s,使其最小化的平方误差是:min{min(R1.sigma((yi-c1)^2))+min(R2.sigma((yi-c2)^2))}计算根据s划分到左侧和右侧子树的目标值与预测值之差的平方和最小,这里的预测值是两个子树上输入xi样本对应yi的均值找到最小的划分特征j以及其最优的划分点s,根据特征j以及划分点s将现有的样本划分为两个区域,一个是在特征j上小于等于s,另一个在在特征j上大于sR1(j)={x|x(j)<=s}、R2(j)={x|x(j)>s}进入两个子区域按上述方法继续划分,直到到达停止条件这里面的最小化我记得可以使用最小二乘法来求关于剪枝:用独立的验证数据集对训练集生长的树进行剪枝(事后剪枝)。 停止条件 直到每个叶子节点都只有一种类型的记录时停止,(这种方式很容易过拟合)另一种时当叶子节点的记录树小于一定的阈值或者节点的信息增益小于一定的阈值时停止关于特征与目标值 特征离散 目标值离散:可以使用ID3,cart特征连续 目标值离散:将连续的特征离散化 可以使用ID3,cart特征离散 目标值连续决策树的分类与回归 分类树输出叶子节点中所属类别最多的那一类回归树输出叶子节点中各个样本值的平均值理想的决策树 叶子节点数尽量少叶子节点的深度尽量小(太深可能会过拟合)解决决策树的过拟合 剪枝前置剪枝:在分裂节点的时候设计比较苛刻的条件,如不满足则直接停止分裂(这样干决策树无法到最优,也无法得到比较好的效果)后置剪枝:在树建立完之后,用单个节点代替子树,节点的分类采用子树中主要的分类(这种方法比较浪费前面的建立过程)交叉验证随机森林优缺点 优点: 计算量简单,可解释性强,比较适合处理有缺失属性值的样本,能够处理不相关的特征;缺点:单颗决策树分类能力弱,并且对连续值变量难以处理;容易过拟合(后续出现了随机森林,减小了过拟合现象);随机森林RF 随机森林是有很多随机得决策树构成,它们之间没有关联。得到RF以后,在预测时分别对每一个决策树进行判断,最后使用Bagging的思想进行结果的输出(也就是投票的思想) 学习过程 现在有N个训练样本,每个样本的特征为M个,需要建K颗树从N个训练样本中有放回的取N个样本作为一组训练集(其余未取到的样本作为预测分类,评估其误差)从M个特征中取m个特征左右子集特征(m<对采样的数据使用完全分裂的方式来建立决策树,这样的决策树每个节点要么无法分裂,要么所有的样本都指向同一个分类重复2的过程K次,即可建立森林预测过程 将预测样本输入到K颗树分别进行预测如果是分类问题,直接使用投票的方式选择分类频次最高的类别如果是回归问题,使用分类之后的均值作为结果参数问题 这里的一般取m=sqrt(M)关于树的个数K,一般都需要成百上千,但是也有具体的样本有关(比如特征数量)树的最大深度,(太深可能可能导致过拟合??)节点上的最小样本数、最小信息增益泛化误差估计 使用oob(out-of-bag)进行泛化误差的估计,将各个树的未采样样本作为预测样本(大约有36.8%),使用已经建立好的森林对各个预测样本进行预测,预测完之后最后统计误分得个数占总预测样本的比率作为RF的oob误分率。 学习算法 ID3算法:处理离散值的量C45算法:处理连续值的量Cart算法:离散和连续 两者都合适?关于CART Cart可以通过特征的选择迭代建立一颗分类树,使得每次的分类平面能最好的将剩余数据分为两类 gini=1-sigma(pi^2),表示每个类别出现的概率和与1的差值, 分类问题:argmax(Gini-GiniLeft-GiniRight) 回归问题argmax(Var-VarLeft-VarRight) 查找最佳特征f已经最佳属性阈值th 小于th的在左边,大于th的在右边子树 优缺点 能够处理大量特征的分类,并且还不用做特征选择在训练完成之后能给出哪些feature的比较重要训练速度很快很容易并行实现相对来说较为简单GBDT GBDT的精髓在于训练的时候都是以上一颗树的残差为目标,这个残差就是上一个树的预测值与真实值的差值。比如,当前样本年龄是18岁,那么第一颗会去按18岁来训练,但是训练完之后预测的年龄为12岁,差值为6,所以第二颗树的会以6岁来进行训练,假如训练完之后预测出来 Boosting的好处就是每一步的参加就是变相了增加了分错instance的权重,而对已经对的instance趋向于0,这样后面的树就可以更加关注错分的instance的训练了 Shrinkage Shrinkage认为,每次走一小步逐步逼近的结果要比每次迈一大步逼近结果更加容易避免过拟合。 y(1 ~ i) = y(1 ~ i-1) + step * yi 就像我们做互联网,总是先解决60%用户的需求凑合着,再解决35%用户的需求,最后才关注那5%人的需求,这样就能逐渐把产品做好.调参 树的个数 100~10000叶子的深度 3~8学习速率 0.01~1叶子上最大节点树 20训练采样比例 0.5~1训练特征采样比例 sqrt(num)优缺点: 优点: 精度高能处理非线性数据能处理多特征类型适合低维稠密数据缺点:并行麻烦(因为上下两颗树有联系)多分类的时候 复杂度很大BP 最小二乘法 最小二乘法是一种数学的优化技术,通过求最小化平方误差来寻找最佳的函数匹配 假设现在有二维的观测数据(x1,y1),(x2,y2)…(xn,yn),求y=a+bx的拟合。 现设yi=a+bxi+ki 如果有a,b能得到sigma(|ki|)最小,则该线比较理想 所以先变为求min(sigma(ki)) ,这个与min(sigma(ki^2))等价 而ki=yi-(a+bxi) 那么现设f=sigma((yi-(a+bxi))^2)求其最小即可 上述就是最小二乘原则,估计a,b的方法称为最小二乘法先求f对a,b的偏导: f’(a)=-2*sigma(yi-(a+bxi))=0 f’(b)=-2xisigma(yi-(a+bxi))=0 现设:X=sigma(xi)/n Y=sigma(yi)/ 则代入上述偏导: an+bnX=nY anX+bsigma(xi^2)=sigma(xiyi) 求该行列式: |n ,nX | |nX,sigma(xi^2)| =n*sigma((xi-X))!=0 所以有唯一解 最后记: l(xx)=sigma((xi-X)^2) l(yy)=sigma((yi-Y)^2) l(xy)=sigma((xi-X)(yi-Y)) 则b=l(xy)/l(xx) a=Y-bX 百度文库-最小二乘法 EM EM用于隐含变量的概率模型的极大似然估计,它一般分为两步:第一步求期望(E),第二步求极大(M), 如果概率模型的变量都是观测变量,那么给定数据之后就可以直接使用极大似然法或者贝叶斯估计模型参数。 但是当模型含有隐含变量的时候就不能简单的用这些方法来估计,EM就是一种含有隐含变量的概率模型参数的极大似然估计法。 应用到的地方:混合高斯模型、混合朴素贝叶斯模型、因子分析模型 Bagging 从N样本中有放回的采样N个样本对这N个样本在全属性上建立分类器(CART,SVM)重复上面的步骤,建立m个分类器预测的时候使用投票的方法得到结果Boosting boosting在训练的时候会给样本加一个权重,然后使loss function尽量去考虑那些分错类的样本(比如给分错类的样本的权重值加大) 凸优化 在机器学习中往往是最终要求解某个函数的最优值,但是一般情况下,任意一个函数的最优值求解比较困难,但是对于凸函数来说就可以有效的求解出全局最优值。 凸集 一个集合C是,当前仅当任意x,y属于C且0<=theta<=1,都有thetax+(1-theta)y属于C 用通俗的话来说C集合线段上的任意两点也在C集合中 凸函数 一个函数f其定义域(D(f))是凸集,并且对任意x,y属于D(f)和0<=theta<=1都有 f(thetax+(1-theta)y)<=thetaf(x)+(1-theta)f(y) —这个貌似叫做jensen不等式 用通俗的话来说就是曲线上任意两点的割线都在曲线的上方 常见的凸函数有: 指数函数f(x)=a^x a>1负对数函数-logax a>1,x>0开口向上的二次函数等凸函数的判定: 如果f是一阶可导,对于任意数据域内的x,y满足f(y)>=f(x)+f’(x)(y-x)如果f是二阶可导,凸优化应用举例 SVM:其中由max|w| 转向min(1/2*|w|^2)最小二乘法?LR的损失函数sigma(yilog(hw(x))+(1-yi)(log(1-hw(x))))

资源下载

更多资源
优质分享App

优质分享App

近一个月的开发和优化,本站点的第一个app全新上线。该app采用极致压缩,本体才4.36MB。系统里面做了大量数据访问、缓存优化。方便用户在手机上查看文章。后续会推出HarmonyOS的适配版本。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。