synchronized锁和Lock锁
synchronized
对象锁-使用方式1
public class Demo2 {
public static void main(String[] args) {
Demo2 demo = new Demo2();
Thread t1 = new Thread(()->{
demo.a();
});
Thread t2 = new Thread(()->{
demo.b();
});
t1.start();
t2.start();
}
public synchronized void a() {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public synchronized void b() {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
上边代码示例中t1和t2两个线程使用同一个Demo2对象分别调用a,b两个方法。两个线程的执行是串行的。因为synchronized在方法上则默认使用当前对象锁。
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
Demo2 demo = new Demo2();
demo.a();
});
Thread t2 = new Thread(()->{
Demo2 demo = new Demo2();
demo.b();
});
t1.start();
t2.start();
}
public synchronized void a() {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public synchronized void b() {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
在每个线程内创建自己的Demo2对象,两个线程则是并行的。
对象锁-使用方式2
public class Demo2 {
public static void main(String[] args) {
Object lock = new Object();
Demo2 demo = new Demo2();
Thread t1 = new Thread(() -> {
demo.a(lock);
});
Thread t2 = new Thread(() -> {
demo.b(lock);
});
t1.start();
t2.start();
}
public void a(Object lock) {
synchronized (lock) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public void b(Object lock) {
synchronized (lock) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
将synchronized转移到方法内部,组成一个同步代码块,同步代码块接收一个对象类型的参数。在main中创建了一个Object对象,将这个对象分别传递给两个线程的a,b方法。执行结果是串行的,因为两个线程使用的是同一个对象,即同一个锁。
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Demo2 demo = new Demo2();
demo.a();
});
Thread t2 = new Thread(() -> {
Demo2 demo = new Demo2();
demo.b();
});
t1.start();
t2.start();
}
public void a() {
synchronized (this) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public void b() {
synchronized (this) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
将代码稍稍修改,每个线程都使用自己的Demo2对象,而同步代码块中写this,这表示每个线程的锁都是自己的Demo2对象,所以结果是并行执行。同步代码块中的对象可以是任意对象,但是使用同一个对象的方法必然会互斥。
类锁-使用方式1
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Demo2.a();
});
Thread t2 = new Thread(() -> {
Demo2.b();
});
t1.start();
t2.start();
}
public synchronized static void a() {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public synchronized static void b() {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
在static方法上使用synchronizedz则表示synchronized使用的是当前类对象。以上代码是串行执行的。
类锁-使用方式2
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Demo2.a();
});
Thread t2 = new Thread(() -> {
Demo2.b();
});
t1.start();
t2.start();
}
public static void a() {
synchronized (Demo2.class) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void b() {
synchronized (Demo2.class) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
在static方法内部使用synchronized代码块,代码块中只能接收类对象,以上代码传的是同一个类对象Demo2.class。所以执行结果是串行的。
public class Demo2 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
Demo2.a();
});
Thread t2 = new Thread(() -> {
Demo2.b();
});
t1.start();
t2.start();
}
public static void a() {
synchronized (Demo2.class) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public static void b() {
synchronized (Object.class) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
将代码修改为a和b分别持有Demo2.class和Object.class执行结果是并行的。这种写法可以接收任意的class。
Lock锁
在JDK5有了java.util.concurrent包,也就是大家说的JUC,Lock接口在JUC的子包java.util.concurrent.locks中。ReentrantLock是其中的一个实现。
lock()-unlock()
public class Test1 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Test1 test1 = new Test1();
Thread t1 = new Thread(() -> {
test1.a(lock);
});
Thread t2 = new Thread(() -> {
test1.b(lock);
});
t1.start();
t2.start();
}
public void a(Lock lock) {
try {
lock.lock();
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void b(Lock lock) {
try {
lock.lock();
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
a,b两个方法使用的是同一个ReentrantLock对象,只有同一个lock才能有锁的效果,以上代码是串行执行的。lock()加锁,unlock()解锁。它们是同一组操作。
public class Test1 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
ReentrantLock lock2 = new ReentrantLock();
Test1 test1 = new Test1();
Thread t1 = new Thread(() -> {
test1.a(lock);
});
Thread t2 = new Thread(() -> {
test1.b(lock2);
});
t1.start();
t2.start();
}
public void a(Lock lock) {
try {
lock.lock();
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public void b(Lock lock) {
try {
lock.lock();
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
如果在增加一个ReentrantLock对象,a,b两个方法使用的不是同一个,运行结果就是并行的。
tryLock()
public class Test2 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Test2 test1 = new Test2();
Thread t1 = new Thread(() -> {
test1.a(lock);
});
Thread t2 = new Thread(() -> {
test1.b(lock);
});
t1.start();
t2.start();
}
public void a(Lock lock) {
boolean b = lock.tryLock();
if (b) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
}
public void b(Lock lock) {
boolean b = lock.tryLock();
if (b) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
}
}
如果一个线程通过tryLock获取锁失败则会返回false,返回true则表示拿到了锁。
tryLock(long time, TimeUnit unit)
public class Test3 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Test3 test1 = new Test3();
Thread t1 = new Thread(() -> {
test1.a(lock);
});
Thread t2 = new Thread(() -> {
test1.b(lock);
});
t1.start();
t2.start();
}
public void a(Lock lock) {
boolean b = false;
try {
b = lock.tryLock(2, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (b) {
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
}
public void b(Lock lock) {
boolean b = false;
try {
b = lock.tryLock(2, TimeUnit.SECONDS);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (b) {
try {
System.out.println("bbbbbbbbbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
} else {
System.out.println("获取锁失败");
}
}
}
在设定的time时间内如果还是没有获取到锁,才返回false。
lockInterruptibly()
public class Test4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Test4 test1 = new Test4();
Thread t1 = new Thread(() -> {
test1.a(lock);
});
Thread t2 = new Thread(() -> {
test1.b(lock);
});
t1.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e) {
}
t2.start();
/* t1.interrupt();执行结果
aaaaaaaaaa
Exception in thread "Thread-0" java.lang.RuntimeException: aaaaaaaaaaaaaaaa打断施法java.lang.InterruptedException: sleep interrupted
at com.dfsn.cloud.consumer.locks.Test4.a(Test4.java:51)
at com.dfsn.cloud.consumer.locks.Test4.lambda$main$0(Test4.java:15)
at java.lang.Thread.run(Thread.java:748)
bbbbbbbbb*/
// t1.interrupt();
/* t2.interrupt();的执行结果
aaaaaaaaaa
Exception in thread "Thread-1" java.lang.RuntimeException: bbbbbbbbbbbb打断施法java.lang.InterruptedException
at com.dfsn.cloud.consumer.locks.Test4.b(Test4.java:52)
at com.dfsn.cloud.consumer.locks.Test4.lambda$main$1(Test4.java:19)
at java.lang.Thread.run(Thread.java:748)*/
// t2.interrupt();
}
public void a(Lock lock) {
try {
lock.lockInterruptibly();
} catch (Exception e) {
throw new RuntimeException(e);
}
try {
System.out.println("aaaaaaaaaa");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException("aaaaaaaaaaaaaaaa打断施法" + e);
} finally {
lock.unlock();
}
}
public void b(Lock lock) {
try {
lock.lockInterruptibly();
} catch (Exception e) {
throw new RuntimeException("bbbbbbbbbbbb打断施法" + e);
}
try {
System.out.println("bbbbbbbbb");
TimeUnit.SECONDS.sleep(3);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
这个方法牵扯到Thread类中的interrupted()方法。对启动的线程使用interrupted方法,则该线程会接收到一个中断的信号,该信号不会停止线程的运行,但如果线程内有调用wait,join,sleep等方法,则会收到一InterruptedException异常。
使用lockInterruptibly()获取锁,然后调用线程的interrupted()如果当前线程已经获取到了锁正在运行中,线程则会接收一个中断信号量,如上所说。但如果当前线程没有获取到锁,处于等待状态,则lockInterruptibly()也会抛出一个InterruptedException。
公平锁和非公平锁
public class Demo5 {
private Lock lock = new ReentrantLock(false); //如果不传递等于传递false,可以传递true表示公平锁
public static void main(String[] args) throws InterruptedException {
final Demo5 test = new Demo5();
test.lock.lock();
Thread t1 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t1.start();
Thread t2 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t2.start();
Thread t3 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t3.start();
Thread t4 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t4.start();
Thread t5 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t5.start();
Thread t6 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t6.start();
test.lock.unlock();
}
创建ReentrantLock时如果不传递参数则默认是false非公平锁。如果是公平锁得情况下会根据线程的等待顺序分配锁,而非公平锁则是随机分配。
ReentrantLock的其他方法
public class Demo6 extends ReentrantLock{
public static void main(String[] args) throws InterruptedException {
final Demo6 test = new Demo6();
test.lock();
Thread t1 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t1.start();
Thread t2 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t2.start();
Thread t3 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t3.start();
Thread t4 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t4.start();
Thread t5 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t5.start();
Thread t6 = new Thread() {
@Override
public void run() {
test.insert(Thread.currentThread());
}
};
t6.start();
System.out.println("查询此锁是否由当前线程持有。"+test.isLocked());
System.out.println("查询此锁是否由任何线程持有。"+test.isLocked());
System.out.println("如果此锁的公平设置为true,则返回 true 。"+test.isFair());
System.out.println("查询是否有线程正在等待获取此锁。"+test.hasQueuedThreads());
System.out.println("查询给定线程是否等待获取此锁。"+test.hasQueuedThread(t1));
System.out.println("返回等待获取此锁的线程数的估计。"+test.getQueueLength());
System.out.println("返回当前拥有此锁的线程,如果不拥有,则返回 null 。"+test.getOwner().getName());
System.out.println("查询当前线程对此锁的暂停数量。"+test.getHoldCount());
System.out.println("返回包含可能正在等待获取此锁的线程的集合。"+test.getQueuedThreads().size());
test.unlock();
System.out.println("查询此锁是否由当前线程持有。"+test.isLocked());
Thread.sleep(2000);
System.out.println("查询是否有线程正在等待获取此锁。"+test.hasQueuedThreads());
System.out.println("查询给定线程是否等待获取此锁。"+test.hasQueuedThread(t1));
System.out.println("返回等待获取此锁的线程数的估计。"+test.getQueueLength());
System.out.println("返回当前拥有此锁的线程,如果不拥有,则返回 null 。"+test.getOwner());
System.out.println("查询当前线程对此锁的暂停数量。"+test.getHoldCount());
System.out.println("返回包含可能正在等待获取此锁的线程的集合。"+test.getQueuedThreads().size());
}
public void insert(Thread thread) {
lock();
try {
System.out.println(thread.getName() + "得到了锁");
Thread.sleep(1000);
} catch (Exception e) {
System.out.println(e);
} finally {
System.out.println(thread.getName() + "释放了锁");
unlock();
}
}
}