Lock类:jdk1.5引入
锁的概念:
线程安全问题:首先单线程不会有线程安全问题,当出现多线程,就会产生资源共用问题,导致数据不一致性,数据存于内存之中,没有线程可以改变当前共享数据值,下个线程就会直接饮用一杯改变的值。
总结:多个线程同时访问一个资源时,会导致程序运行结果并不是想看到的结果。
多线程执行时,会涉及多个线程访问并操作同一资源,为保证一致性,就加上锁,每个线程独立完成后,其他线程在进行处理。
采用:synchronized 和 Lock
synchronized :锁针对的是对象,这点得明确,而非线程和方法。
对于synchronized方法或者synchronized代码块,当出现异常时,JVM会自动释放当前线程占用的锁,因此不会由于异常导致出现死锁现象。
解析synchronized锁流程(每个对象拥有的锁属性):
1、线程进入对象,放入等待的Set中
2、对象资源可分配,进入使用
3、当线程进入wait,释放资源,放入wait的Set中
4、唤醒后,可继续分配资源
5、执行完毕,释放资源
实现方法:
lock:获取锁,阻塞式
lockInterruptibly:可中断获取锁,可以中断线程。
tryLock:试获取锁,成功true,失败false
unLock:释放锁
Lock接口的实现基本都是通过聚合了一个AbstractQueuedSynchronizer同步器的子类来完成线程访问控制
AbstractQueuedSynchronizer同步器:是用来构建锁或者其他同步组件的基础框架,它使用了一个内置的int成员变量表示同步状态。
接口方法:
getState:获取当前同步状态
setState:设置当前同步状态
compareAndSetState:设置同步状态,同时保证了原子性
实现锁类:ReentrantLock - 可重入锁
1、可重入,识别获取锁的线程为当前占据锁的线程,可再次获取;再重新获取N次后,再释放N次,下个线程可以直接获取。
2、可设置公平和非公平;公平性与否是针对锁获取顺序;公平,先启动线程先获取锁执行,相对损耗性能。
synchronized 和 reentranLock
少量线程访问采用默认synchronized ,方便识别和运行速度优于reentranLock
大批量线程采用reentranlock,可用性和可选择性大大提升
辅助类
1、CountDownLatch,类似于计步器的工具,当多个线程并发,A线程要等待B,C线程执行完毕后,A才能执行。
定义:
public CountDownLatch(int count) { }; //参数count为计数值
方法:
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
运用:
package com.ly.thread;
import com.ly.thread.bean.Runner;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchTest {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(2);
Runner runner = new Runner(0,0,0);
new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
runner.setC(runner.getA()+runner.getB());
System.out.println(runner.getC());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runner.setA(200);
countDownLatch.countDown();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
runner.setB(20);
countDownLatch.countDown();
}
}).start();
}
}
结果:
2、CyclicBarrier,回环栅栏,让一组线程都达到一个状态后,再次同时执行,因为是状态,使用之后,即线程都达到barrier状态后,对象可以再次使用。和countDownLatch比,是一个主要线程(自己设定)进入等待其他线程执行完毕;
1、有明显主次关系,countDownLatch;线程达到某一状态,CyclicBarrier
定义:
public CyclicBarrier(int parties) {
// parties可以等待的线程数
}
public CyclicBarrier(int parties, Runnable barrierAction) {
//Runnable barrierAction 都到达barrier状态后,随机分配一条线程执行barrierAction
}
方法:
public int await() throws InterruptedException, BrokenBarrierException { };//进入状态等待,所有县城达到后继续运行
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
运用:(对比CountDownLatch实现)
package com.ly.thread;
import com.ly.thread.bean.Runner;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
public class CountDownLatchTest {
public static void main(String[] args) {
// CountDownLatch countDownLatch = new CountDownLatch(2);
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
Runner runner = new Runner(0,0,0);
new Thread(new Runnable() {
@Override
public void run() {
try {
// countDownLatch.await();
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
runner.setC(runner.getA()+runner.getB());
System.out.println(runner.getC());
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runner.setA(200);
// countDownLatch.countDown();
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
runner.setB(20);
// countDownLatch.countDown();
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}).start();
}
}
3、Semaphore,信号量;控制同时访问的线程数量;可以理解为锁。一个任务可以同时被多少线程分配。
定义:
public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可
sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
方法:
public void acquire() throws InterruptedException { } //获取一个许可,无法获取进入等待
public void acquire(int permits) throws InterruptedException { } //获取permits个许可
public void release() { } //释放一个许可,必须配合acquire方法
public void release(int permits) { } //释放permits个许可
public boolean tryAcquire() { }; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
//非阻塞获取
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
运用:
package com.ly.thread;
import java.util.concurrent.Semaphore;
/**
* 信号量
**/
public class SemaphoreTest {
public static void main(String[] args) {
int N = 10;
Semaphore semaphore = new Semaphore(5);
for(int i=0 ;i < N;i++){
new Taskkk(semaphore).start();
}
}
}
class Taskkk extends Thread{
Semaphore semaphore;
public Taskkk(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
Thread.sleep(5000);
System.out.println("执行OK");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
}
}
结果: