呀?这就是锁(一)?
java常用锁
Synchroized(自旋锁,轻量级锁)
package com.LockDemo;
public class SynchronizedDemo {
static Object o = new Object();
//synchronized 修饰 m方法
static synchronized void m1() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} //两个线程会等待,锁生效 此时锁定的对象是 m1 方法
//synchronized 修饰对象
void m2(){
synchronized(this){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//两个线程会等待,锁生效 此时锁定的是 sd 对象
//synchronized 修饰对象
void m3(){
synchronized(this){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}//不等待,因为两个线程不争用对象资源
static synchronized void m4(){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}//如果有static 关键字,则会被加锁,反之,锁消失 ,证明在这种情况下,锁定的静态的 m4 方法,而非静态的 m4 每个属于每个线程特有的不加锁
void m5(){
synchronized (o){
System.out.println(Thread.currentThread().getName());
try {
//o = new Object(); //o 指向一个新的对象 ,此时synchronized指向的o变成未加锁状态,第二个线程会直接进来,
Thread.sleep(10000);
o = new Object(); //此时再释放,则会等待10s
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} //o在sleep前指向新的o,synchronized立刻失效,直接输出两个线程名字,反之则等待后输出
public static void main(String[] args) {
//m2 使用
SynchronizedDemo sd = new SynchronizedDemo();
new Thread(()->{
/*
m1(); //m1
sd.m2(); //m2
new SynchronizedDemo().m3(); //m3
*/
new SynchronizedDemo().m5();
}).start();
//m4 第二种调用
/*
Runnable r = new Runnable() {
@Override
public void run() {
new SynchronizedDemo().m4();
}
};
new Thread(r).start();
new Thread(r).start();
*/
new SynchronizedDemo().m5(); //使用m4方式需要注释
}
}
总结:
synchronized肯定会对一个对象加锁来实现并发,如果没有达到想要的目的,需要分析加锁对象是不是竞争线程都需要,如果你在TheadA给A加上了锁,但是ThreadB却不会使用A,那么这个锁就没有意义,并且在锁定过程中,对象不能更改,如果更改就相当于把锁加到了一个没有引用的对象上,没有意义,此时新的指向已经是无锁对象了,
备注:
异常会把锁丢失,所以需要异常处理严谨一些。
ReentrantLock (互斥锁,独占锁)
package com;
import java.util.concurrent.locks.ReentrantLock;
/**
* ReentrantLock和synchronized的基本用法相同,所以首先要理解synchronied的实现原理,
* 这里主要讲述ReentranrLock相对于synchronized增强的部分
*/
public class ReeLockDemo {
static ReentrantLock reeLock = new ReentrantLock();
//普通加锁解锁
static void m1(){
try {
//System.out.println(Thread.currentThread().getName()+"1"); //此处可以直接输出,即不加锁部分
reeLock.lock();
System.out.println(Thread.currentThread().getName());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
reeLock.unlock(); //必须要消除锁
}
}//此时锁定的reeLock.lock()到reeLock.unlock()中间的部分
//尝试加锁
static void m2(){
boolean locked = false;
try {
locked = reeLock.tryLock();
if(locked){
System.out.println(Thread.currentThread().getName());
Thread.sleep(10000);
}
System.out.println(Thread.currentThread().getName()+locked); //这里输出Thread-0 false,因为此时的锁被main线程占用
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if(locked) reeLock.unlock(); //必须要消除锁,必须加上判断,不然如果没有获得锁便会直接报措,错误是
/* Exception in thread "Thread-0" java.lang.IllegalMonitorStateException 锁状态不合法,因为没有锁,直接释放当然状态不对
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
*/
}
}
//等待锁资源可以被打断了
static void m3(){
boolean locked = false;
try {
reeLock.lockInterruptibly();
System.out.println(Thread.currentThread().getName());
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if(locked) reeLock.unlock();
}
/*
输出结果
main
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at com.ReeLockDemo.m3(ReeLockDemo.java:53)
at com.ReeLockDemo.lambda$main$0(ReeLockDemo.java:68)
at java.lang.Thread.run(Thread.java:748)
明显可以看到t1线程没有再等待锁,打断等待状态
*/
}
//真的公平吗?
static void m4(){
ReentrantLock fairLock = new ReentrantLock(true); //这里我们设定的是公平锁
try {
fairLock.lock();
System.out.println(Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}finally {
fairLock.unlock(); //必须要消除锁
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
//
//如果有sleep结果是这样的,节选一部分
/*
Thread-1
Thread-0
Thread-1
Thread-0
Thread-1
Thread-0
*/
//如果没有sleep结果是这样的,节选一部分
/*
Thread-0
Thread-0
Thread-0
Thread-1
Thread-1
Thread-1
Thread-1
*/
/*
从上面的结果不难发现,这个公平好像和想的不一样,这是因为,再系统内部是否公平是根据排队的顺序来的,即便是ThreadA刚刚被执行了一次,如果他第二次排队又排到
ThreadB的前面,那么他还是会继续执行,这样想就容易明白了
*/
}
}
public static void main(String[] args) {
/*
Thread t1 = new Thread(()->{
//m1();
m3();
});//.start();
//m1();
//m2();
//执行m3方法
t1.start();
//首先指向main中m3 ,2S 后释放
try {
m3();
Thread.sleep(2000);
t1.interrupt(); //t1不再等待锁
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
//m5
Thread t1 = new Thread(()->{
while(true){
m4();
}
});
Thread t2 = new Thread(()->{
while(true){
m4();
}
});
t1.start();
t2.start();
}
}
总结:
ReentrantLock和synchronized相比更加的灵活,并且需要显示的加锁和释放锁,所以也需要我们对它有更加深刻的理解,才能再使用的过程中有的放矢,有效的提升工作效率。