1:Synchronized 是Java的一个关键字,而Lock是java.util.concurrent.Locks 包下的一个接口;
2:Synchronized 使用过后,会自动释放锁,而Lock需要手动上锁、手动释放锁;(粒度可控)
3:Lock提供了更多的实现方法,而且可响应中断、可定时, 而synchronized 关键字不能响应中断;
4:synchronized关键字是非公平锁,即,不能保证等待锁的那些线程们的顺序,而Lock的子类ReentrantLock默认是非公平锁,但是可通过一个布尔参数的构造方法实例化出一个公平锁;
5:synchronized无法判断是否已经获取到锁,而Lock通过tryLock()方法可以判断是否已获取到锁;
6:Lock可以通过分别定义读写锁提高多个线程读操作的效率。
7:二者的底层实现不一样,synchronized是同步阻塞,采用的是悲观并发策略;Lock是同步非阻塞,采用的是乐观并发策略
公平锁就是:先等待的线程,先获得锁。 非公平锁就是:不能够保证 等待锁的 那些线程们的顺序, 公平锁因为需要维护一个等待锁资源的队列,所以性能相对低下
悲观锁的思想是"先悲观地认为会发生冲突",在访问数据之前会将其锁定,直到操作完成后才会释放锁。确保在整个数据访问过程中,只有一个线程或进程能够访问数据,其他的线程或进程需要等待。
乐观锁的思想是"先乐观地认为不会发生冲突",在访问数据时不加锁,而在数据更新时进行冲突检测。多个线程或进程可以同时访问数据,但在提交更新时,会检查是否有其他线程或进程对数据进行了修改。如果发现冲突,就进行处理。乐观锁一般会使用版本号机制和CAS算法实现。通过版本号一致与否,即给数据加上版本,来同步更新数据以及加上版本号。
总的来说,悲观锁是先锁定资源再进行操作,保证数据的一致性,但并发性较差;而乐观锁是先进行操作再检查冲突,允许多个线程或进程同时访问,但是在更新的时候会判断一下在此期间其他线程或进程有没有去更新这个数据,可以使用版本号机制和CAS算法实现。选择使用哪种锁取决于具体的场景和需求。
一个线程在某一个时间点处于一个状态,这些状态不反应操作系统的线程状态。
相同点:
不同点:
Runnable适合于不需要返回结果、不需要处理异常的简单场景;
Callable更适合需要返回结果、需要处理异常的复杂场景。
读锁和写锁,其中读锁允许多个线程同时获得,因为读操作本身是线程安全的,而写锁则是互斥锁,不允许多个线程同时获得写锁,并且写操作和读操作也是互斥的。
OOM(out of memory(存储器,内存)): 当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程
不推荐使用的原因
这个线程池到时没有上个线程池豪横了, 它定死了线程数量, 所以线程数量是不会超出的,但是它的任务队列是无界的LinkedBlockingQueue, 对于加进来的任务处理不过来就会存入任务队列中, 并且无限制的存入队列。 这个线程池感觉就是家里有地, 无论来多少货都往里面装。这个线程池如果使用不当很容易导致OOM
补充:LinkedBlockingQueue 是Java中的一个阻塞队列,它是基于链表实现的,具有 无界限制 (但可以设置容量大小)。它的特点是当队列为空时,从队列中获取元素的操作会被阻塞;当队列已满时,往队列里添加元素的操作会被阻塞。
这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
不推荐使用的原因
这个线程池只有一个线程, 比newFixedThreadPool还穷, 但是任务队列和上面一样, 没有限制, 很容易就使用不当导致OOM
这个是定时任务的线程池, 没有定义线程创建数量的上线, 同时任务队列也没有定义上限, 如果前一次定时任务还没有完成, 后一个定时任务的运行时间到了, 它也会运行, 线程不够就创建。 这样如果定时任务运行的时间过长, 就会导致前后两个定时任务同时执行,如果他们之间有锁,还有可能出现死锁, 此时灾难就发生了。
线程池支持定时以及周期性执行任务的需求,以创建一个固定大小的线程池,该线程池可以在指定的延迟时间后执行任务,也可以按照一定的时间间隔周期性地执行任务。
不推荐使用的原因
这个线程池到时没有上个线程池豪横了, 它定死了线程数量, 所以线程数量是不会超出的,但是它的任务队列是无界的LinkedBlockingQueue, 对于加进来的任务处理不过来就会存入任务队列中, 并且无限制的存入队列。 这个线程池感觉就是家里有地, 无论来多少货都往里面装。这个线程池如果使用不当很容易导致OOM
如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲( 60 秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说 JVM )能够创建的最大线程大小。
不推荐使用使用的原因:
最大线程数是Integer.MAX_VALUE, 并且任务队列是SynchronousQueue。 也就是说这个线程池对任务来者不拒,线程不够用就创建一个, 感觉就像一个豪横的富豪。 这就是问题所在了, 如果同一时刻应用的来了大量的任务, 这个线程池很容易就创建过多的线程, 而创建线程又是一个很耗性能的事情, 这就容易导致应用卡顿或者直接OOM
补充:SynchronousQueue 是Java中的一个阻塞队列,它不存储元素,而是在生产者线程将元素放入队列时,必须等待消费者线程将元素取出后才能继续执行。也就是说,每个插入操作必须等待一个对应的移除操作。
1.newFixedThreadPool(固定大小的线程池)
2.newSingleThreadExecutor(单线程的线程池)
3.newScheduledThreadPool(可调度的线程池 周期 线程池)
4.newCachedThreadPool(可缓存的线程池) 不限制最大线程数
为什么不推荐:
任务队列无限制,容易导致oom
OOM: 当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(注:非exception,因为这个问题已经严重到不足以被应用处理)。
当某个线程获取到锁后,发现当前还不满足执行的条件,就可以调用对象锁的wait方法,进入等待状态。 直到某个时刻,外在条件满足了,就可以由其他线程通过调用notify ()或者notifyAll ()方法,来唤醒此线程。
@Transactional注解只能在在public修饰的方法下使用。
类内部直接访问类内部的方法。
解决方法:在该Service类中使用AopContext.currentProxy()获取代理对象
MySQL中,MyISAM引擎不支持事物,InnoDB 支持事物
@Transactional 注解默认只处理运行时异常( RuntimeException 和 error),而不会处理受检异常( Exception 的子类)。当抛出未被捕获的运行时异常时,Spring 会触发事务回滚操作,将之前的操作撤销;而对于未被捕获的受检异常,Spring 不会触发事务回滚操作。如果需要处理受检异常并触发事务回滚,可以通过 rollbackFor 和 noRollbackFor 属性来指定需要回滚或不需要回滚的异常类型。
事务声明传播属性为不支持事务,spring提供的一种传播机制,表示以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
事务管理是通过try…catch捕获异常才能做回滚的,如果业务代码的异常try…catch并没有向外抛出,这时出现异常后事务就不会回滚而使得事务的一致性没法满足,进而事务失效
Token:采用 Token 方式实现 SSO,其原理是在用户登录之后,认证服务器生成一个 Token,并将该 Token 存储在共享的存储中(例如 Redis、数据库等),然后将该 Token 返回给用户。用户在访问其他应用系统时,该系统会向认证服务器发送请求,并带上 Token。认证服务器通过 Token 来判断该用户是否已经登录过,如果已经登录过,则返回一个新的 Token 给应用系统,否则需要用户重新登录。
单点登录(Single Sign-On,简称 SSO)是指用户只需要登录一次,即可在多个应用系统中访问受授权的资源。其原理是在用户登录之后,服务器会颁发一个令牌(Token),并将该令牌保存在共享的存储中(例如 Redis、数据库等),然后在用户访问其他应用系统时,该系统会向认证服务器发送请求,如果该用户已经登录过,则认证服务器会颁发一个新的令牌,旧的令牌作废,否则需要用户重新登录。
实现多个设备的登录,可以使用hash结构存储