所谓JUC是java.util.concurrent 工具包的简称这是一个处理线程的工具包可以实现多线程高并发的内容
1 复习synchronized
LOCK接口不让等待的线程一直无期限地等待,可以通过设置一定的时间或者是中断处理机制
讲解LOCK接口之前涉及另一个锁机制synchronized
结合synchronized多线程的编程步骤主要是:(高内聚低耦合)
Thread.currentThread().getName()当前线程的名字
1.创建资源类,在资源类中创建属性和操作方法
//第一步 创建资源类,定义属性和和操作方法
class Ticket {
//票数
private int number = 30;
//操作方法:卖票
public synchronized void sale() {
//判断:是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" : 卖出:"+(number--)+" 剩下:"+number);
}
}
}
2.创建多个线程,调用资源类的操作方法
public class SaleTicket {
//第二步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
//创建Ticket对象
Ticket ticket = new Ticket();
//创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
//调用卖票方法
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"AA").start();
new Thread(new Runnable() {
@Override
public void run() {
//调用卖票方法
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"BB").start();
new Thread(new Runnable() {
@Override
public void run() {
//调用卖票方法
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"CC").start();
}
}
具体创建线程的方法是new Thread(new Runnable({ //重写run方法},线程名)
new Thread(new Runnable() {
@Override
public void run() {
//调用卖票方法
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"AA").start();
或者可以通过lambda表达式这样创建
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
}
},"AA").start();
代码执行的时候,AA、BB与CC三个线程执行堆叠在一起,这是因为不同线程不同对象启动执行,都可以访问
2 Lock接口
为锁和等待条件提供一个框架的接口和类,不同于内置同步和监视器, LOCK是类,可通过类实现同步访问,多个接口实现类:可重入锁等
lock的编程步骤同synchronized
创建资源类,在资源类中船舰属性和操作方法
创建多个线程,调用资源类的操作方法
可重入锁的代码定义private final ReentrantLock lock = new ReentrantLock(true);
上锁lock.lock();
解锁lock.unlock();
上锁与解锁中的代码如果出现异常,解锁会执行不了,所以最好加try..finally
具体代码示列
//第一步 创建资源类,定义属性和操作方法
class LTicket {
//票数量
private int number = 30;
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock(true);
//卖票方法
public void sale() {
//上锁
lock.lock();
try {
//判断是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" :卖出"+(number--)+" 剩余:"+number);
}
} finally {
//解锁
lock.unlock();
}
}
}
public class LSaleTicket {
//第二步 创建多个线程,调用资源类的操作方法
//创建三个线程
public static void main(String[] args) {
LTicket ticket = new LTicket();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"AA").start();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"BB").start();
new Thread(()-> {
for (int i = 0; i < 40; i++) {
ticket.sale();
}
},"CC").start();
}
}
3 Lock方法
lock常用接口
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
lock()方法用来获取锁
- 如果锁已被其他线程获取,则进行等待
- 发生异常不会自动解锁,需用在 try{}catch{}块中进行
Condition 类也可以实现等待/通知模式lock.lock(); try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 }
关键字 synchronized 与 wait()/notify()这两个方法一起使用可以实现等待/通知模式,Lock 锁的 newContition()方法返回 Condition 对象,Condition 类也可以实现等待/通知模式
用 notify()通知时,JVM 会随机唤醒某个等待的线程, 使用 Condition 类可以进行选择性通知, Condition 比较常用的两个方法:
- await()会使当前线程等待,同时会释放锁,当其他线程调用 signal()时,线程会重新获得锁并继续执行
- signal()用于唤醒一个等待的线程
ReentrantLock 是唯一实现了 Lock 接口的类,并且 ReentrantLock 提供了更多的方法
ReentrantLock可重入锁
ReentrantReadWriteLock 里面提供了很多丰富的方法,不过最主要的有两个方法:readLock()和 writeLock()用来获取读锁和写锁
writeLock();来获取读锁
readLock();获取写锁
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
假设有一个线程已经占用了读锁,则此时其他线程如果要申请写锁,则申请写锁的线程会一直等待释放读锁,反之同理
4 两者差异
synchronized与lock的异同:
- synchronized是java关键字,内置,而lock不是内置,是一个类,可以实现同步访问且比synchronized中的方法更加丰富
- synchronized不会手动释放锁,而lock需手动释放锁(不解锁会出现死锁,需要在 finally 块中释放锁)
- lock等待锁的线程会相应中断,而synchronized不会相应,只会一直等待
- 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到
- Lock 可以提高多个线程进行读操作的效率(当多个线程竞争的时候)