JAVA并发之多线程基础(3)

670 阅读2分钟

上篇文章中讲到了重入锁以及对应的条件操作,详情见JAVA并发之多线程基础(2)。这篇文章我们就继续谈JDK中含有的并发操作类。

Semaphore

对于大部分的锁来说,线程之间的都是互斥的,排他的,只允许一个线程进入临界区中来。但是信号量这里允许多个线程进入临界区。可以广义上面看做是一个共享锁。

  1. acquire()这个是去拿一个信号量,使得该线程可以进入到临界区。
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);//是去拿去一个信号量许可
    }

同时一个线程也可以去拿去多个信号量。

public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);//拿取多个信号量许可
    }
  1. release()方法是释放当前线程的信号量。
public void release() {
        sync.releaseShared(1);
    }
  1. tryAcquire()是尝试获取信号量,获取不到就去做其他的事情。
public boolean tryAcquire() {
        return sync.nonfairTryAcquireShared(1) >= 0;
    }

同时它也有根据时间尝试获取信号量的方法:

public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }

当然,信号量自身里面也有公平信号和非公平信号,里面使用到的也是AQS。JDK下面的并发操作类中最主要的核心也就是AQS。下面有个小的Demo给大家看看使用情况。

package com.montos.lock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
/**
 * 信号量
 * 
 * @author Montos
 *
 *         2019年5月30日
 *
 */
public class SemaphoreDemo implements Runnable {
	final Semaphore semaphore = new Semaphore(5);

	@Override
	public void run() {
		try {
			semaphore.acquire();
			Thread.sleep(2000);
			System.out.println(Thread.currentThread().getId() + " thread is done");
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			semaphore.release();
		}
	}
	public static void main(String[] args) {
		ExecutorService exec = Executors.newFixedThreadPool(20);
		final SemaphoreDemo demo = new SemaphoreDemo();
		for (int i = 0; i < 20; i++) {
			exec.submit(demo);
		}
	}
}

ReadWriteLock

读写锁是为了更大的增加线程的执行效率的。上面介绍的锁无论是读还是写都会进入到对应的拿锁阶段,而这个时候读写锁就很好的体现了它的作用。它进行功能上面的划分,使得读写分开进行。

读锁与写锁之间关联:

非阻塞 阻塞
阻塞 阻塞

看下一个小的demo:

package com.montos.lock;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadLockDemo {

	public static ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

	public static void main(String[] args) {
		// 同时读、写
		ExecutorService service = Executors.newCachedThreadPool();
		service.execute(new Runnable() {
			@Override
			public void run() {
				readFile(Thread.currentThread());
			}
		});
		service.execute(new Runnable() {
			@Override
			public void run() {
				writeFile(Thread.currentThread());
			}
		});
	}
	// 读操作
	public static void readFile(Thread thread) {
		lock.readLock().lock();
		boolean readLock = lock.isWriteLocked();
		if (!readLock) {
			System.out.println("当前为读锁!");
		}
		try {
			for (int i = 0; i < 5; i++) {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(thread.getName() + ":正在进行读操作……");
			}
			System.out.println(thread.getName() + ":读操作完毕!");
		} finally {
			System.out.println("释放读锁!");
			lock.readLock().unlock();
		}
	}
	// 写操作
	public static void writeFile(Thread thread) {
		lock.writeLock().lock();
		boolean writeLock = lock.isWriteLocked();
		if (writeLock) {
			System.out.println("当前为写锁!");
		}
		try {
			for (int i = 0; i < 5; i++) {
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(thread.getName() + ":正在进行写操作……");
			}
			System.out.println(thread.getName() + ":写操作完毕!");
		} finally {
			System.out.println("释放写锁!");
			lock.writeLock().unlock();
		}
	}
}

以上介绍的是JDK中两种锁的操作方式以及他们的特点,具体在业务中的使用还是得需要看具体的需求,考虑是否有必要去使用。