目录
锁是什么
锁是用来控制多个线程访问共享资源的方式,一般来说,一个锁能够防止多个线程同时访问共享资源。
我们这里以电影院售票为例
传统的锁synchronized
package com.qjx.eightlock;
import java.util.concurrent.TimeUnit;
/*
电影院有30张票,用四个窗口A B C D进行出售
*/
public class SaleTicketSyn {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
saleTicket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
saleTicket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
saleTicket.sale();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
saleTicket.sale();
}
}, "D").start();
}
}
class SaleTicket {
//这里我们定义有30张票
private static int ticket = 30;
private int num = 1;
public synchronized void sale() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + num++ + "张票,剩余" + --ticket + "张票");
}
}
}
这里得到的结果就是ABCD四个窗口在随机售票
这里我们注意一个点,我们这里的锁锁住的是sale方法。因为我们这里的主线程是正常执行的不会受到其他线程的影响,那么如果我们在线程for循环中的 saleTicket.sale();之前加上输出语句,并且在sale方法里面sleep睡眠两秒,那么一开始会先开始执行这些输出语句,到执行方法的时候,就会才被阻塞,所以这里我们得出,一个锁能够防止多个线程同时访问共享资源。如下面代码所示
package com.qjx;
import java.util.concurrent.TimeUnit;
/*
电影院有30张票,用四个窗口A B C D进行出售
*/
public class SaleTicketSyn {
public static void main(String[] args) {
SaleTicket saleTicket = new SaleTicket();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName()+"获得了锁");
saleTicket.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName()+"获得了锁");
saleTicket.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName()+"获得了锁");
saleTicket.sale();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
System.out.println(Thread.currentThread().getName()+"获得了锁");
saleTicket.sale();
}
}, "D").start();
}
}
class SaleTicket {
//这里我们定义有30张票
private static int ticket = 30;
private int num = 1;
public synchronized void sale() {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + num++ + "张票,剩余" + --ticket + "张票");
}
}
}
Lock
package com.qjx;
import java.sql.Time;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SaleTicketLock {
public static void main(String[] args) {
Sale sale = new Sale();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
sale.sale();
}
}, "A").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
sale.sale();
}
}, "B").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
sale.sale();
}
}, "C").start();
new Thread(() -> {
for (int i = 0; i < 40; i++) {
sale.sale();
}
}, "D").start();
// for (int i = 0; i < 40; i++) {
// int j = (int)(1+Math.random()*(4));
// new Thread(()->{
// sale.sale();
// },String.valueOf(j)).start();
// }
}
}
class Sale {
private int ticket = 30;
private int num = 1;
Lock lock = new ReentrantLock();
// Condition condition = lock.newCondition();
public void sale() {
lock.lock();
try {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + num++ + "张票,剩余" + --ticket + "张票");
}
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
lock.unlock();
}
}
}
lock.tryLock()使用,这个方法可以用来判断是否获取到了锁,里面可以加参数。如果是没有获取到锁的话是会返回false,如果是成功获取到了锁的话就是返回true。如果trylock里面设置了超时时间 tryLock(long time, TimeUnit unit),那么在规定时间内如果没有获取到锁的话就会直接返回false
package com.qjx;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author jc
*
*/
public class TryLock {
//实例化Lock对象
Lock lock = new ReentrantLock();
/**
* @param args
*/
public static void main(String[] args) {
//实例化本类对象,目的是调用runThread方法
TryLock tl = new TryLock();
//匿名对象创建线程1,并重写run方法,启动线程
new Thread(){
public void run(){
tl.runThread(Thread.currentThread());
}
}.start();
//匿名对象创建线程2,并重写run方法,启动线程
new Thread(){
public void run(){
tl.runThread(Thread.currentThread());
}
}.start();
}
//线程共同调用方法
public void runThread(Thread t){
//lock对象调用trylock()方法尝试获取锁
try {
if(lock.tryLock(2L, TimeUnit.SECONDS)){
//获锁成功代码段
System.out.println("线程"+t.getName()+"获取锁成功");
try {
//执行的代码
Thread.sleep(5000);
} catch (Exception e) {
//异常处理内容,比如中断异常,需要恢复等
} finally {
//获取锁成功之后,一定记住加finally并unlock()方法,释放锁
System.out.println("线程"+t.getName()+"释放锁");
lock.unlock();
}
}else{
//获锁失败代码段
//具体获取锁失败的回复响应
System.out.println("线程"+t.getName()+"获取锁失败");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* output
* 线程Thread-0获取锁成功
* 线程Thread-1获取锁失败
* 线程Thread-0释放锁
*/
Lock和Synchronized的区别
1.Synchronized能用在静态方法,方法,和代码块上,lock只能用在代码块上
2.Synchronized自动获取锁和释放锁,遇到异常会自动释放锁,lock手动获取锁和释放锁,如果没有使用unlock去释放锁就有可能会造成死锁
3.Synchronized无法知道是否获取了锁,但是lock能知道是否获取了锁(通过trylock能判断是否获取到了锁)