浅谈Java 并发的底层实现
前言
Java 的并发处理一直是大家关注的焦点。在Java的并发处理中,涉及到的概念非常多,如线程、锁、CAS等。事实上,这些概念并不是“简单概念”,而是与Java虚拟机和CPU等底层机制紧密相关的。
本文将从底层实现的角度对Java并发进行讲解,重点介绍线程的创建、锁的实现以及原子性的保证,增加大家对Java并发底层实现的认识。
线程的创建
Java创建线程有两种方式:直接继承Thread类和实现Runnable接口。
以继承Thread类的方式创建线程为例,Java会为每个线程分配一个栈空间,栈空间中包括线程执行过程中所需的所有方法栈帧,而栈空间的大小是在创建线程时指定的。因此,如果同时创建大量线程,会导致栈空间被耗尽,从而引发OutOfMemoryException异常。
例如,下面的代码创建了10个线程,且每个线程的栈空间是100MB,因此需要占用1GB的内存空间:
public class MyThread extends Thread{
@Override
public void run(){
// 执行线程任务
}
}
public static void main(String[] args){
for(int i=0;i<10;i++){
MyThread t=new MyThread();
t.setStackSize(100*1024*1024);
t.start();
}
}
锁的实现
Java中的锁有两种,分别是synchronized和ReentrantLock。这里以synchronized为例,介绍Java锁的底层实现。
在Java虚拟机中,每个对象都有一个监视器锁(也可以称作管程或者内部锁),synchronized关键字就是利用了这个锁来实现同步。当一个线程获取了一个对象的监视器锁之后,其他线程将无法通过关键字synchronized访问该对象。
值得注意的是,可以使用关键字synchronized修饰方法或者代码块。如果修饰方法,则监视器锁会被自动加在方法的对象上。如果修饰代码块,则需要手动指定要进行同步的监视器对象。
下面的示例中,使用synchronized修饰代码块实现了线程同步:
public class Account{
private int balance;
// 取钱方法
public void withdraw(int amount){
synchronized(this){
if(balance>=amount){
balance-=amount;
}
}
}
// 存钱方法
public void deposit(int amount){
synchronized(this){
balance+=amount;
}
}
}
原子性的保证
在Java中,原子操作指的是一组操作中的任何一个操作都不会被线程抢占的操作。Java提供了多种支持原子操作的类,例如AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference等。
AtomicBoolean、AtomicInteger、AtomicLong等都是通过底层的CAS(Compare-and-Swap)操作来保证原子性的。CAS操作是CPU级别的原子操作,可以保证内存操作的原子性。当多个线程同时执行CAS操作时,只有一个线程能够成功执行该操作,保证了CAS操作的原子性。
下面是一个使用AtomicLong实现原子自增的例子:
public class AtomicCounter{
private AtomicLong counter=new AtomicLong(0);
public void increase(){
counter.incrementAndGet();
}
public long get(){
return counter.get();
}
}
总结
本文从线程的创建、锁的实现以及原子性的保障三个方面,对Java并发的底层实现进行了讲解。通过学习本文,相信读者对Java并发底层机制有更好的了解。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:浅谈Java 并发的底层实现 - Python技术站