悲观锁:
总是假设最坏的情况:每次去拿数据的时候,都认为值会被别人修改。因此给值加上锁,这样别的线程就不能访问了。如synchronized和ReentrantLock就是悲观锁。适用于多写情形。
乐观锁:
总是假设最好的情况:每次去拿数据的时候,都认为值不会被别人修改。因此不会给值加锁。但是在更新的时候,会去判断这个数据是否被更新过。可以使用版本号机制和CAS算法实现。乐观锁适用于多读情形,这样可以提高吞吐量。
乐观锁的实现方式:
1.版本号机制:
一般在数据表加一个数据版本号(version),表示被修改的次数。每修改一次就+1一次。当线程A要修改数据时,在读取数据的同时也会读取version的值,之后将读出来的version和当前数据库中的version进行对比,相等才能更新。不然,重试更新操作,直到更新成功。
2.CAS算法:
CAS是一种有名的无锁算法,CAS有三个操作数,旧的预期值A,当前内存值V,要修改的新值B。当A=V时,说明值没有被修改过(忽略CAS的“ABA”问题),那么可以将新B赋给V,否则什么也不做。一般情况下是一个自旋操作,即不断重试。
乐观锁的缺点:
1CAS的“ABA”问题。
2.自旋CAS,不成功就会一直循环执行,直到成功。如果长时间不成功,会给CPU带来非常大的执行开销。
3.CAS只对单个共享变量有效,当操作涉及多个共享变量时,无效。
2.run()和start()的区别:
1.调用start()方法才是真正启动多线程,而且无需等待run方法体代码运行完毕,就可以直接运行后面的代码。
2.run()方法当做普通方法来调用,程序还是要按顺序执行,要等run方法体代码运行完毕,才能执行下面的代码。
如果直接调用run()方法,这也只是调用一个方法而已,程序中依然只有主线程这一个线程,程序也依然只有一条执行路径,那么也就失去了开辟线程的意义了。
3.多线程断点续传原理:
在本地下载过程中要使用数据库实时存储,到底存储到文件的哪个位置了。这样点击开始继续传递时,才能通过Http的GET请求中的setRequestProperty("Range","bytes=startIndex-endIndex")方法,告诉服务器,数据从哪里开始,到哪里结束。同时在本地的文件写入时,RandomAccessFile的seek()方法也支持在文件的任意位置进行写入操作。同时通过广播或事件总线机制将子线程的进度告知Activity的进度条。关于断点续传的HTTP状态码是206。
参考文章:https://juejin.im/post/5e5c5c52f265da575f4e7558#heading-134