1、synchronized和Lock有什么区别?哪个可重入?哪个效率高?

synchronized和Lock都用于线程同步的场景中。

synchronized是jdk的关键字,用来构造同步代码块或者同步方法。同步代码块的锁是synchronized括号中的对象,同步方法的锁是当前类实例或者当前类的Class实例,取决于同步方法是实例方法还是静态方法。如果一个线程获得锁,那么就执行同步代码块或者同步方法。如果不能获取锁,那么线程会阻塞,直到获取锁,然后执行同步代码块或者同步方法。synchronized的锁不用我们写代码手动释放,在同步代码块或者同步方法执行完毕或抛异常的情况下会自动释放锁。

Lock是jdk的一个接口,常用实现类是ReentrantLock。Lock有4个获取锁的方法:

void lock():锁被另一个线程持有的话,当前线程会一直阻塞,直到获取锁。

void lockInterruptibly() throws InterruptedException:锁被另一个线程持有的话,当前线程会一直阻塞,直到获取锁或者被其他线程中断。

boolean tryLock():返回boolean类型。如果获取到锁的话,就返回true,否则返回false。线程不会阻塞。

boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException:锁被另一个线程持有的话,当前线程会一直阻塞,直到获取锁或者到指定时间或者被其他线程中断。

其实,只有tryLock()无参方法会直接返回,另外3个方法都会阻塞线程。

锁不会自动释放,即使抛异常,所以我们必须写代码手动释放锁。调用unlock()方法释放锁,且必须写在finally块中,防止抛异常而执行不到。

调用Lock实例的newCondition()方法可生成Condition实例,Condition实例的await()方法会使调用线程释放锁并进入该Condition实例的等待队列中,而signal()方法会唤醒等待在当前Condition实例上的一个线程,await()方法、signal()方法配合使用,可以实现线程间通信。一个Lock实例又可生成多个Condition实例,多个Condition实例配合使用,可以实现多个线程按照一定顺序执行的效果。

synchronized是非公平锁。ReentrantLock既可以公平,又可以非公平,取决于构造器传参,默认是非公平锁,效率比公平锁要高。公平锁就是说获得锁的线程遵循先来后到的原则,而非公平锁对线程先后顺序无所谓。

synchronized是可重入的,如创建单例时就有一种双检锁模式,同步代码块或者同步方法执行完后,不管获取了几次锁,锁都释放了。Lock的实现类ReentrantLock也是可重入锁,同一个线程可以多次获取到这个锁,释放锁的时候必须释放多次,unlock()方法执行次数必须与各种lock()方法执行次数一样,否则锁释放不掉。

ReentrantLock内部是用AQS实现的。在各种lock()方法中均调用了AQS的compareAndSetState()方法,而compareAndSetState()方法内部调用的是Unsafe(全类名是sun.misc.Unsafe)实例的CompareAndSwapInt()native方法。

2、CAS、AQS

CAS:compareAndSwap的简称,比较并替换,是实现并发算法时常用的一种技术。AQS和原子类(AtomicInteger、AtomicBoolean等Atomic开头的类)的实现,大量使用了CAS。

CAS有3个操作数:当前内存值、预期值、要修改的新值。比较内存值和预期值,如果相等,就把值设为新值,返回true,否则什么都不做,返回false。CAS实际上是利用处理器提供的CMPXCHG指令实现的,处理器执行CMPXCHG指令是原子操作。

CAS最大的问题是ABA问题。CAS在操作值的时候会检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。这就是CAS的ABA问题。常见的解决思路是使用版本号。在值前面追加上版本号,每次变量更新的时候把版本号加1,那么A-B-A 就会变成1A-2B-3A。

AQS见另一篇文章:https://www.cnblogs.com/koushr/p/5873444.html

3、ConcurrentHashMap怎么实现的?

从jdk1.8开始,ConcurrentHashMap采用CAS+synchronized来保证并发更新的安全,底层是数组+链表+红黑树的存储结构。

在首次put时要调用initTable()方法初始化Node数组(Node有4个成员变量:int hash,K key,volatile V value,volatile Node<K, V> next),initTable()方法中用了CAS,保证在多线程情况下只有一个线程会创建Node数组。

根据key的hash值找到在Node数组中的位置,如果该位置处的元素为null,说明这个位置还没插入元素,则利用Unsafe的compareAndSwapObject实例方法插入Node节点。如果CAS成功,则Node节点正常插入,否则表示有其它线程提前插入了节点,当前线程自旋重新尝试在这个位置插入节点。

在put一个重复key或者一个hash值和现在某个key的hash值一样的key时,putVal()方法会执行一个同步代码块,这个同步代码块的锁是ConcurrentHash实例key对应的Node实例,如此同时只有一个线程可以去更新value或者在链表中新增一个Node节点。

4、垃圾回收器有哪些?有什么不同?

5、postgresql相对于mysql有什么优点?

1)pg在GIS领域绝对领先。

2)pg支持窗口函数或者说是分析函数。而mysql直到8才支持,5.7及以前都不支持。

3)pg原生支持递归查询,用with recursive即可,而mysql没那么简单,需要自己写存储过程。

4)pg支持表达式索引和条件索引。mysq不支持。

表达式索引:

筛选条件是where lower(name) = 'zhangsan';

如果在name列上加索引,查询语句肯定是不会走此索引的。所以可以给lower(name)加索引,脚本如下:

create index idx_lvl_trace_info_name_lower on lvl_trace_info(lower(name));

条件索引:

create index idx_lvl_trace_info_updated_date_2019 on lvl_trace_info(updated_date) where updated_date > '2019-01-01 00:00:00';

则下面语句会走索引

select * from lvl_trace_info where updated_date between '2019-01-01' and '2019-06-01';

而下面语句不会走索引

select * from lvl_trace_info where updated_date between '2018-01-01' and '2019-01-01';

5)pg原生支持存储emoji。而mysql需要指定字符集是utf8mb4,才能存储emoji。

6)mysql文本类型有tinytext、text、mediumtext、longtext,分别可支持不同长度的文本,在用的时候,需要我们预估文本长度,然后选择一个合适的类型。而pg文本类型没有那么多,只有一个text,可以支持任意长度,方便使用。

7)mysql可以通过select @@global.tx_isolation;及select @@tx_isolation;查看全局和当前会话的事务隔离级别。mysql的默认事务隔离级别是repeatable-read。

pg可以通过show default_transaction_isolation;及show transaction_isolation;查看默认事务隔离级别和当前会话事务隔离级别。

6、mysql主键索引和二级索引有什么区别?

InnoDB引擎的mysql,主键索引是聚簇索引,索引数据和行数据都存储在同一个B+树中。该B+树根据索引数据进行创建,数据存储在叶子节点中。数据的物理顺序和索引逻辑顺序一致。一个表最多只能有一个聚簇索引,因为数据物理顺序不可能有多个顺序。

二级索引,如普通索引、唯一索引、复合索引,是非聚簇索引。非聚簇索引,索引和数据分开存储。索引B+树只存储索引数据及其与主键的映射。

举个例子,表lvl_trace_info有主键字段id,普通索引字段updated_date,及其他。

根据主键id的查询语句:

select * from lvl_trace_info where id = 99;

这个查询语句会去主键索引树上查数据,因为行数据存储在叶子节点上,所以一下子就把数据查出来了。

根据普通索引列updated_date的查询语句:

select * from lvl_trace_info where updated_date > current_date;

这个查询语句会去updated_date对应的二级索引数上查找满足updated_date > current_date的updated_date对应的主键id,然后再根据这些主键id去主键索引树上查找对应的行数据。总结起来,需要先查询二级索引树,再查询主键索引数。

7、mybatis一二级缓存。

mybatis一级缓存是SqlSession级别的。SqlSession是mybatis-xxx.jar包中的一个接口,全类名是org.apache.ibatis.session.SqlSession。在操作数据库时需要构造SqlSession对象,查询结果会保存到SqlSession对象中。如果下一次查询还是用的此SqlSession对象,且查询条件一样,则不会去查数据库,而是直接从SqlSession对象中取出数据,并返回。反之,如果每次查询都是用的新的SqlSession对象,或是同一个SqlSession对象但查询条件不一样,或是同一个SqlSession对象但查询之间穿插了增删改,则一级缓存就用不上。增删改操作会清空一级缓存数据。

mybatis二级缓存是多个SqlSession共享的。默认关闭,不建议使用。如果要使用的话,首先需要打开总开关,在mybatis的配置文件中添加

<settings>
    <!--开启二级缓存-->
    <setting name="cacheEnabled" value="true"/>
</settings>

然后,哪个mapper想用二级缓存,就在哪个mapper下添加<cache />,具体是在<mapper namespace="xxx">下面添加<cache />。

如果二级缓存开启的话,一条查询语句会首先从二级缓存中查找数据,如果二级缓存没有,则去一级缓存中查找数据,如果能拿到的话,就把数据放到二级缓存中,同时返回。如果一级缓存也没有,则会去查库,把查询结果放到一级缓存、二级缓存中,并返回。

8、线上某接口响应慢,用什么工具或者方法定位问题?

9、从kafka的架构设计角度解释一下kafka为什么能够支持高并发?

10、spark reduceByKey、groupByKey有什么区别?常用的方法有哪些?

11、spark从kafka拉取数据有哪几种方式?

利用KafkaUtils工具类(org.apache.spark.streaming.kafka.KafkaUtils,在spark-streaming-kafka-xxx.jar包中),有3种方式:

1)createDirectStream()一系列重载方法:

2)createStream()一系列重载方法:

3)createRDD()一系列重载方法:

最新文章

  1. 浅谈:javascript的面向对象编程之基础知识的介绍
  2. 让Visual Studio Code对jQuery支持智能提示!
  3. AngularJS多模块开发
  4. 面向对象的Javascript(4):重载
  5. svn 切换默认用户名
  6. WCF之事务
  7. ios7自带的晃动效果
  8. 在InnoDB,记录在 non-clustered indexes(也被称为secondary indexes) 包含了主键值
  9. PYTHON线程知识再研习A
  10. [转]使用sklearn进行集成学习——理论
  11. pta总结3
  12. iOS -- Effective Objective-C 阅读笔记 (6)
  13. Node.js、npm、vue-cli 的安装配置环境变量
  14. 20. Web proxies (网页代理 4个)
  15. 关于 ul 嵌套 li 并且再嵌套 a 的 BUG
  16. free命令常用参数详解
  17. Using Option Files
  18. vue核心之虚拟DOM
  19. ipconfig/all详解
  20. C++随记

热门文章

  1. javascript处理json字符串
  2. ajax工作原理,Jsonp原理
  3. 用Vue来实现音乐播放器(九):歌单数据接口分析
  4. AssertionError: View function mapping is overwriting an existing endpoint function: insertCase
  5. 008-elasticsearch5.4.3【二】ES使用、ES客户端、索引操作【增加、删除】、文档操作【crud】
  6. Jmeter之Switch Controller
  7. 《计算机程式设计》Week4 课堂笔记
  8. Postman Tests脚本的使用
  9. 如何给vue 日期控件赋值
  10. Java基础/利用fastjson序列化对象为JSON