- java中常用的独占锁有两种:Synchronized,ReenTrantLock。两种锁在性能上差别不大,但ReenTrantLock手动加锁,解锁,更加灵活,功能更加丰富
独占锁,可重入锁
public class TestReenTrantLock { static Lock lock = new ReentrantLock(true); public static void main(String[] args) { //可重入锁:一个线程多次获取同一个对象上的锁。可重入锁的意义之一在于防止死锁 实现原理:当线程获取锁时,jvm将记录锁的占有者,并将锁中的计数器加1, 同一个线程再次获得锁,计数器再次加1。未被占用的锁,计数器为0。 当线程退出同步块,计数器值将递减,直到计数器值为0,锁被释放。其他线程才能获得锁 独占锁:一个锁一次只能被一个线程占有 method(); method1(); lock.unlock(); lock.unlock(); } public static void method(){ lock.lock(); System.out.println("第一次获取锁"); } public static void method1(){ lock.lock(); System.out.println("第二次获取锁"); } }
这里在method中获取了TestReenTrantLock类锁,调用method1时再一次获取了TestReenTrantLock类锁,这就是可重入锁
- 可以实现公平锁
public class FairReenTrantLock {
/**
* 公平锁:当锁可用时,在锁上等待时间最长的线程获取锁的使用权
* 无参数或为fasle,为非公平锁
* 非公平锁:随机获得锁的使用权
*/
static Lock lock = new ReentrantLock(true);
public static void main(String[] args) {
for (int i=0; i<5; i++){
new Thread(new ThreadDemo()).start();
}
}
static class ThreadDemo implements Runnable{
public ThreadDemo(){
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(2);
}catch (Exception e){
e.printStackTrace();
}
for(int i=0; i<2; i++){
lock.lock();
System.out.println("获得锁的线程"+Thread.currentThread().getName());
lock.unlock();
}
}
}
}
可以响应线程中断
首先了解一下 public void interrupt();(属于ThreadL类)
public class TestInterrupt {
public static void main(String[] args) throws InterruptedException {
Thread testThread = new TestThread();
testThread.start();
testThread.interrupt();
}
/*
*下面测试的三个方法都会阻塞线程,如果没有使用这个三个方法,线程会直接走完,
*而且isInterrupted()为true
*使用了其中一个,会抛出InterruptedException,isInterrupted()为false
*/
static class TestThread extends Thread {
@Override
public void run() {
System.out.println("线程开始运行");
try {
//TimeUnit.SECONDS.sleep(5);
//wait();
join();
}catch (Exception e){
System.out.println("线程发生异常"+e);
System.out.println(Thread.currentThread().isInterrupted());
}
System.out.println("线程结束运行");
}
}
}
- 响应中断案例
public class LockInterruptibly {
static Lock firstLock = new ReentrantLock();
static Lock secondLock = new ReentrantLock();
public static void main(String[] args) {
Thread firstthread = new Thread(new ThreadDemo(firstLock,secondLock));
Thread secondThread = new Thread(new ThreadDemo(secondLock,firstLock));
firstthread.start();
secondThread.start();
firstthread.interrupt();
}
static class ThreadDemo implements Runnable {
private Lock firstLock;
private Lock secondLock;
public ThreadDemo(Lock firstLock,Lock secondLock){
this.firstLock = firstLock;
this.secondLock = secondLock;
}
//会中断处于等待的线程,释放锁,然后另一个线程获取锁,继续走
@Override
public void run() {
try {
firstLock.lockInterruptibly();
TimeUnit.SECONDS.sleep(1);
secondLock.lockInterruptibly();
}catch (Exception e){
e.printStackTrace();
}finally {
firstLock.unlock();
secondLock.unlock();
System.out.println(Thread.currentThread().getName()+"正常结束");
}
}
}
}
- 不响应中断案例
public class TestSynchronized {
static Object resource1 = new Object();
static Object resource2 = new Object();
public static void main(String[] args) {
Thread thread1 = new TestSynchronizedThread(resource1,resource2);
Thread thread2 = new TestSynchronizedThread(resource2,resource1);
thread1.start();
thread2.start();
thread1.interrupt();//中断线程,
}
static class TestSynchronizedThread extends Thread {
Object resource1;
Object resource2;
public TestSynchronizedThread(Object resource1,Object resource2){
this.resource1 = resource1;
this.resource2 = resource2;
}
//即使跑出异常,也不会中断线程,两个线程继续互相等待资源,造成死锁
@Override
public void run() {
synchronized (resource1){
System.out.println(Thread.currentThread().getName()+"--111");
try {
TimeUnit.SECONDS.sleep(1);
System.out.println(Thread.currentThread().getName()+"--222");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName()+"--333");
}
System.out.println(Thread.currentThread().getName()+"--444");
synchronized (resource2){
System.out.println(555);
}
}
}
}
}
- 获取锁时,限时等待
public class TestTryLock {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
Thread thread = new Thread() {
@Override
public void run() {
if(lock.tryLock()){//获取锁的结果立即返回,true为成功获取锁,false表示失败
System.out.println("线程获取到锁");
}
try {
if(lock.tryLock(5, TimeUnit.SECONDS)){
System.out.println("为了获取锁,最多等待5s");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
thread.start();
}
}
- 利用condition实现等待通知机制,
public class TestCondition {
static Lock lock = new ReentrantLock();
static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
lock.lock();
System.out.println("主线程获取锁");
new Thread(new TestConditionThread()).start();
condition.await();//主线程等待,释放锁
System.out.println("主线程被唤醒");
lock.unlock();
}
//使用Condition之前必须先获取锁,调用condition的await()使当前线程释放锁,处于等待状态
//直到有其他线程调用同一个condition的signal()才被唤醒
static class TestConditionThread extends Thread {
@Override
public void run() {
System.out.println("启动子线程"+Thread.currentThread().getName());
lock.lock();//子线程获取锁
System.out.println("子线程获取到锁");
condition.signal();//子线程释放锁,唤醒主线程
lock.unlock();
}
}
}
- condition实现阻塞队列
public class TestBlockingQueue {
private int size;
private ReentrantLock lock = new ReentrantLock();
Condition full = lock.newCondition();
Condition empty = lock.newCondition();
private LinkedList<Integer> list = new LinkedList<>();
public TestBlockingQueue(int size){
this.size = size;
}
/**
* 利用ReentrantLock实现阻塞队列,队列满时,入队阻塞
* @param data
*/
public void enqueue(Integer data){
try {
lock.lock();
while (list.size() == size){
full.await();//队列满了,阻塞当前线程,阻止入队
}
list.add(data);
System.out.println("入队成功"+data);
empty.signal();
}catch (Exception e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
public void dequeue(){
try {
lock.lock();
while(list.size() == 0){
empty.await();//队列为空,阻塞出队
}
Integer e = list.removeFirst();
System.out.println("出队成功"+e);
full.signal();
}catch (Exception e){
}finally{
lock.unlock();
}
}
public static void main(String[] args) {
TestBlockingQueue testBlockingQueue = new TestBlockingQueue(3);
for(int i=0; i<9; i++){
int data = i;
new Thread(new Runnable() {
@Override
public void run() {
testBlockingQueue.enqueue(data);
}
}).start();
}
for(int i=0; i<5; i++){
new Thread(new Runnable() {
@Override
public void run() {
testBlockingQueue.dequeue();
}
}).start();
}
}
}
synchronized
作用:解决多个线程之间访问资源时线程安全问题,保证被它修饰的代码,同一时刻只有一个线程访问。
重量级锁:监视器锁(monitor)依赖底层操作系统的Mutex Lock来实现,java线程是映射在操作系统原生线程之上的。因此挂起或唤醒一个线程,需要操作系统帮忙。操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要很长时间,耗时成本较高。但是java6以后对synchronized做了大量的优化
Synchronized的使用方式
1)修饰普通方法给当前实例对象加锁,想要进入方法需要先获取当前实例对象的锁,Synchronized(this)
2)修饰静态方法,给类对象加锁,想要进入方法需要先获取类对象的锁Synchronized(class),实例对象锁和类锁并不互斥,互相没有影响
3)修饰代码块,对给定的对象加锁,可以是实例对象,也可以是类对象Synchronized(this|object)
4)尽量不要使用Synchronized(Stirng)因为jvm中字符串常量池具有缓存功能
5)如果类中存在共享成员变量,即使方法加了Synchronized,也会出现线程安全问题,因为Synchronized锁住的方法和代码片段,不会锁住属性
public class SynchronizedTest {
List list = Collections.synchronizedList(new ArrayList<>());
//A,B线程执行这个方法
public synchronized Boolean putIfAbsent(Integer i){
//A线程执行完这一步,B线程开始执行,B执行完成后,A再次执行,这时会重复添加元素
boolean flag = !list.contains(i);
if(flag){
list.add(i);
}
return flag;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。