发表于: 2019-12-20 22:12:05

1 1149


今天做了什么:

微信文章前台修改了一些细节

文章列表显示点赞和收藏状态,看上去简单其实实现起来还挺麻烦的

方案是sql查询文章时外左连接article_like和article_collection表:

select article.* , article_like.sid , article_collection.sid from article 
left join article_like on article_like.aid=article.id and article_like.sid=(select id from student where openId=#{openId})
left join article_collection on article_collection.aid=article.id and article_collection.sid=(select id from student where openId=#{openId})

收获:

面试时发现集合框架线程方面挺薄弱的,针对性补充一下:

Java集合类由两个类派生:CollectionMap,它们是Java集合框架的根接口.

List,Set,Map和Collection接口:
  • List和Set继承自Collection接口,Map是独立接口
  • List:可重复,有序
  • Set:不允许重复(用对象的equals()方法来区分元素是否重复)。
  • Map是键值对映射容器,key不重复,value可重复.
Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为O(1),而基于排序树版本的实现在插入或删除元素时会按照元素或元素的键(key)构成排序树从而达到排序和去重的效果。


List:

ArrayList,Vector和LinkedList的存储性能和特性:
  • ArrayList 和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢.
  • Vector中的方法由于添加了synchronized修饰,底层数据结构是数组,线程安全,效率低,已经是Java中的遗留容器。
  • LinkedList使用双向链表实现存储.( 将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高)索引慢插入块.
由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装饰者模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现)。


Set:

TreeSet,LinkedHashSet和HashSet:
1.主要功能:
  • TreeSet主要功能用于排序.底层数据结构是红黑树
  • LinkedHashSet主要功能用于保证FIFO即有序的集合.底层数据结构是链表和哈希表
  • HashSet只是通用的存储数据的集合.底层数据结构是哈希表
2.相同点
  • 三者都实现Set interface,都不包含重复元素
  • 都不是线程安全.(若要使用线程安全用Collections.synchronizedSet()方法)
3.区别
  • 从性能上:HashSet插入最快,LinkedHashSet其次,TreeSet最慢,因为内部实现排序
  • 从排序上:HashSet不保证有序,LinkedHashSet保证FIFO即按插入顺序排序,TreeSet内部实现排序,也可以自定义排序规则
  • 从null上:HashSet和LinkedHasSet允许null,TreeSet插入null会报NullPointerException
4.TreeSet排序方式比较:
  • 自然排序:要比较的类实现Comparable接口,重写CompareTo方法
  • 比较器排序:创建一个比较器类,继承Comparator接口,重写Compare方法,构造方法中注入比较器:TreeSet(Comparator<? superE> comparator)


Map:

  • HashMap:线程不安全,效率较高,允许null值.(如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap)
  • TreeMap:有序.另外两者无序
  • HashTable:同步,线程安全(所有public方法声明中都有synchronized关键字) 不允许null值
HashMap的底层实现
1.8前:数组+链表 组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为 O(1),因为最新的 Entry 会插入链表头部,急需要简单改变引用链即可,而对于查找操作来讲,此时就需要遍历链表,然后通过 key 对象的 equals 方法逐一比对查找.
1.8后: 当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
( 红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。)
Collection与Collections:
前者是Set与List等容器的父接口.后者是一个工具类,提供了一系列的静态方法来辅助容器操作, 包括对容器的搜索、排序、线程安全化等(之前提到的synchronized方法).
TreeMap和TreeSet在排序时如何比较元素? Collections工具类中的sort()方法如何比较元素? 
TreeSet要求存放的对象所属的类必须实现Comparable接口( 该接口提供了比较元素的compareTo()方法,当插入元素时会回调该方法比较元素的大小).TreeMap也要求存放的键值对映射的键必须实现Comparable接口.

Collections工具类的sort方法有两种重载的形式,第一种要求传入的待排序容器中存放的对象比较实现Comparable接口;第二种不强制性的要求容器中的元素必须可比较,但是要求传入第二个参数,参数是Comparator接口的子类型(需要重写compare方法实现元素的比较),相当于一个临时定义的排序规则,其实就是通过接口注比较元素大小的算法,也是对回调模式的应用(Java中对函数式编程的支持)


线程

线程池TreadPoolExecutor参数:
  • 1.核心线程corePoolSize:一直存活;当线程数小于核心线程数,创建新线程
  • 2.阻塞队列queueCapacity:当核心线程数达到最大,新任务将放入任务队列
  • 3.最大线程数maxPoolSize:当任务队列已满,创建新线程,直至达到最大线程数
  • 4.线程空闲时间keepAliveTime:空闲时间达到一定值时线程退出,若允许核心线程超时,核心线程也会退出
  • 5.允许核心线程超时allowCoreThreadTimeout
  • 6.任务拒绝处理器rejectedExecutionHandler


关闭线程的方法:

  • 1.stop().不推荐
  • 2.通过volatile类型的boolean变量来控制循环
  • 3.interrrupt()方法


sleep()与对象的wait()区别:
sleep()方法是线程类的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的依然保持,因此休眠时间结束后会自动恢复.
wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),如果线程重新获得对象的锁就可以进入就绪状态。

总结:一个保持锁,一个放弃锁.


问题:

1.使用缓存时如何保持缓存数据与数据库同步?


明天计划:

联调后台管理部分.




返回列表 返回列表
评论

    分享到