-
jvm对java多线程做了很多优化,这里对java线程的多个同步方式做一个记录。 synchronized关键字
被该关键字修饰的方法块或方法执行时,会先尝试获取锁,如果其它线程已获取锁,则等待线程释放锁,多个线程等待锁时,不是按照等待获取锁的线程顺序来获取锁,这是一个重量级锁,底层会使用monitorenter、monitorexit指令,jvm会采取一定的优化措施
private void incr(){
synchronized (SyncService.class){
int i=10;
i++;
_num++;
}
}
public void noSync() throws InterruptedException {
Thread[] ths=new Thread[THREAD_COUNT];
for(int i=0;i<THREAD_COUNT;i++){
ths[i]=new Thread(new Runnable() {
@Override
public void run() {
for(int i1=0;i1<1000;i1++){
incr();
}
cd.countDown();
}
});
ths[i].start();
}
cd.await();
System.out.println(_num);
}
-
ReentrantLock
可重入锁,和synchronized关键字一样属于重量级锁,它更灵活一点,可以控制加锁、解锁的时机,已获取锁的线程可以重复获取锁,但必须以同样的次数来解锁。
private ReentrantLock _lock=new ReentrantLock();
private void incr(){
try{
_lock.lock();
_lock.lock();
int i=10;
i++;
_num++;
}finally {
_lock.unlock();
_lock.unlock();
}
}
public void noSync() throws InterruptedException {
Thread[] ths=new Thread[THREAD_COUNT];
for(int i=0;i<THREAD_COUNT;i++){
ths[i]=new Thread(new Runnable() {
@Override
public void run() {
for(int i1=0;i1<1000;i1++){
incr();
}
cd.countDown();
}
});
ths[i].start();
}
cd.await();
System.out.println(_num);
}
-
ReentrantReadWriteLock 读写锁,共享读互斥写,可以多个线程获取读锁,但写锁同一时刻只能一个线程拥有,当有线程拥有读锁时,获取写锁的线程阻塞,当写锁加锁时,获取读锁的线程阻塞等待。
适用于读多于写的场景下。
private ReentrantReadWriteLock _rwLock=new ReentrantReadWriteLock();
private void incr(){
try{
_rwLock.writeLock().lock();
int i=10;
i++;
_num++;
}finally {
_rwLock.writeLock().unlock();
}
}
private void readNum(){
try{
Thread.sleep(1000);
_rwLock.readLock().lock();
System.out.println(String.format("threadid %d num %d",Thread.currentThread().getId(),_num));
}catch (Exception ex){
ex.printStackTrace();
}
finally {
_rwLock.readLock().unlock();
}
}
public void noSync() throws InterruptedException {
Thread[] ths=new Thread[THREAD_COUNT];
for(int i=0;i<THREAD_COUNT;i++){
ths[i]=new Thread(new Runnable() {
@Override
public void run() {
for(int i1=0;i1<1000;i1++){
readNum();
}
cd.countDown();
}
});
ths[i].start();
}
while (true){
Thread.sleep(2000);
System.out.println("开始写");
incr();
System.out.println("结束写");
}
}
-
StampedLock 乐观锁,同读写锁区别在于,读的同时允许写,但这样就造成了共享资源的不一致,所以需要在读操作里判断是否有写操作,如果有则使用悲观读锁,来重新读取一次。
private double x, y;
private final StampedLock sl = new StampedLock();
void move(double deltaX, double deltaY) {
long stamp = sl.writeLock(); //获取写锁
try {
x += deltaX;
y += deltaY;
} finally {
sl.unlockWrite(stamp);
}
}
double distanceFromOrigin() { // A read-only method
long stamp = sl.tryOptimisticRead(); //获取乐观读锁,不阻塞写锁的获取
double currentX = x, currentY = y;
//根据时间量stamp判断读取后,数据是否被改写,如果是则重新获取悲观读锁,重新读取一次。
if (!sl.validate(stamp)) {
stamp = sl.readLock();
try {
currentX = x;
currentY = y;
} finally {
sl.unlockRead(stamp);
}
}
return Math.sqrt(currentX * currentX + currentY * currentY);
}
-
Semaphore 信号量,可以控制共享资源被多个线程访问的数量。
private final Semaphore available = new Semaphore(3, true); //初始化信号量3
private void readNum(){
try{
available.acquire(); //获取锁,成功则信号量减1
System.out.println(String.format("threadid %d num %d",Thread.currentThread().getId(),_num));
Thread.sleep(3000);
}catch (Exception ex){
ex.printStackTrace();
}
finally {
available.release(); //释放锁,成功则信号量加1
}
}
-
CountDownLatch 计数器,初始化一个数量,调用await在数量减少为0前将等待,用于任务依赖于其它任务完成后的场景。
private CountDownLatch cd=new CountDownLatch(THREAD_COUNT);
public void noSync() throws InterruptedException {
Thread[] ths=new Thread[THREAD_COUNT];
for(int i=0;i<THREAD_COUNT;i++){
ths[i]=new Thread(new Runnable() {
@Override
public void run() {
readNum();
cd.countDown(); //任务完成后计数器减1
}
});
ths[i].start();
}
cd.await(); //等待计数器为0
}
-
CyclicBarrier 屏障,和CountDownLatch类似,提供一个屏障(条件点),当所有线程到达该点时,一起执行。
-
private CyclicBarrier cb=new CyclicBarrier(THREAD_COUNT, new Runnable() { @Override public void run() { System.out.println(String.format("所有线程已到达3秒后一起执行")); try{ Thread.sleep(3000); }catch (Exception ex){ ex.printStackTrace(); } } }); public void noSync() throws InterruptedException { Thread[] ths=new Thread[THREAD_COUNT]; for(int i=0;i<THREAD_COUNT;i++){ ths[i]=new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(2000); cb.await();//等待所有线程到位后执行后续代码 System.out.println(String.format("threadid %d",Thread.currentThread().getId())); }catch (Exception ex){ ex.printStackTrace(); } cd.countDown(); //任务完成后计数器减1 } }); ths[i].start(); } cd.await(); //等待计数器为0 }