多线程2.0
synchronize同步原理:
所有对象都默认有一个单一的锁,JVM实时监控每个对象上面的锁情况,当第一次进入synchronized代码块时,就会在该代码块的对象锁上加一,当还没离开同步代码块时,其他任务线程访问该代码块时,发向对象上的锁不是0,就无法获得对象锁而处于等待阻塞状态,直到上一个任务离开时,对象锁会自动减一,直到为0了,下一个任务线程才有机会获得对象锁而执行任务
线程中的资源共享:
当多个线程访问同一共享资源时,如果不加以同步处理,很有可能使他们访问出错;例如,把你餐桌上的食物是共享资源,你正伸手去拿食物,突然食物不见了,这就造成了共享资源混乱;java提供给我们的处理的办法就是上锁:synchronized,使用该关键字把你需要的同步方法包起来,则以后有线程要访问你这段代码,就必须获得该对象实例的同步锁(此锁是java内部对象生成的,隐式提供给我们的,该类的其他对象访问这段代码还是可以的;如果同步的方法是静态方法,则要获取该类的同步锁才行),使用方法如下:
public void test() {
...
synchronized(this) {
// todo your code
}
...
}
此时,其效果等同于
public synchronized void test() {
// todo your code
}
和同步锁相关的三个方法:wait()/notify()/sleep()
wait()表示当前线程释放同步锁,进入线程等待池,是否能够CPU,其他线程可以抢占此同步锁
notify()唤醒因为调用wait()方法而进入休眠的线程,从而使该剧线程有机会再次获得同步锁
sleep()同样也是使线程进入休眠,释放CPU,但是不释放同步锁
wait和notify必须要在synchronized代码块中才调用
与上面对应locked的方式对应有:
await() / signal()和signalAll();并且signal能实现公平的通知,而notify是随机唤醒某个线程
java5提供了新的同步方法lock和unlock:
虽然代码量比synchronized多,但控制精度更高,更准确;并发量大的时候是个不错的选择;使用也更加灵活
使用方法:
class LockTest extends TestTemplate{
ReentrantLock lock=new ReentrantLock();
public LockTest(String _id,int _round,int _threadNum,CyclicBarrier _cb){
super( _id, _round, _threadNum, _cb);
}
/**
* synchronized关键字不在方法签名里面,所以不涉及重载问题
*/
@Override
long getValue() {
try{
lock.lock();
return super.countValue;
}finally{
lock.unlock();
}
}
@Override
void sumValue() {
try{
lock.lock();
super.countValue+=preInit[index++%round];
}finally{
lock.unlock();
}
}
}
使用lock方法的好处:
1、ReentrantLock 拥有Synchronized相同的并发性和内存语义,此外还多了 锁投票,定时锁等候和中断锁等候
线程A和B都要获取对象O的锁定,假设A获取了对象O锁,B将等待A释放对O的锁定,
如果使用 synchronized ,如果A不释放,B将一直等下去,不能被中断
如果使用ReentrantLock,如果A不释放,可以使B在等待了足够长的时间以后,中断等待,而干别的事情
ReentrantLock获取锁定与三种方式:
a) lock(), 如果获取了锁立即返回,如果别的线程持有锁,当前线程则一直处于休眠状态,直到获取锁
b) tryLock(), 如果获取了锁立即返回true,如果别的线程正持有锁,立即返回false;
c)tryLock(long timeout,TimeUnit unit), 如果获取了锁定立即返回true,如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false;
d) lockInterruptibly:如果获取了锁定立即返回,如果没有获取锁定,当前线程处于休眠状态,直到或者锁定,或者当前线程被别的线程中断
2、synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定,但是使用Lock则不行,lock是通过代码实现的,要保证锁定一定会被释放,就必须将unLock()放到finally{}中
3、在资源竞争不是很激烈的情况下,Synchronized的性能要优于ReetrantLock,但是在资源竞争很激烈的情况下,Synchronized的性能会下降几十倍,但是ReetrantLock的性能能维持常态;
原子性:
即不可被中断的操作属性,要么做完,要么不做;java的基本类型除了64位的double/long类型除外,其他的类型操作都是原子性的;
Java5提供了一些原子性变量类,对他操作时可以保证原子性的操作,AtomicInteger/AtomicLong/AtomicReference;优化代码时使用,一般情况下还是用synchronzied和显示同步锁lock来同步;
volatile:
被volatile修饰的域,那么它会告诉编译器,在编译此段域的时候,对域的读取和写入不要用一些缓存来优化,我直接读取此域的内存地址上的数据;如果多个任务同时访问同一个域,并且至少有一个是写入操作,那么这个域可以设置为volatile;
临界区:
多个线程同时访问方法内的部分代码,而不是全部代码,将这段代码抽离出来进行同步处理,这段代码叫临界区
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】