1、synchronized
1.1、synchronized的三种应用方式
一. 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁。
二. 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁。
三. 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象。
1.2、synchronized的字节码指令
synchronized同步块使用了monitorenter和monitorexit指令实现同步,这两个指令,本质上都是对一个对象的监视器(monitor)进行获取,这个过程是排他的,也就是说同一时刻只能有一个线程获取到由synchronized所保护对象的监视器。
线程执行到monitorenter指令时,会尝试获取对象所对应的monitor所有权,也就是尝试获取对象的锁,而执行monitorexit,就是释放monitor的所有权。
1.3多线程编程的简单实现
第一步:创建一个资源类
第二步:创建多个线程,调用资源类的方法
例如:
public class SaleTicket {
public static void main(String[] args) {
//第二步:创建多个线程,调用资源类的方法
Ticket ticket = new Ticket();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<40;i++){
ticket.sale();
}
}
},"ThreadA").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<40;i++){
ticket.sale();
}
}
},"ThreadB").start();
new Thread(new Runnable() {
@Override
public void run() {
for(int i=0;i<40;i++){
ticket.sale();
}
}
},"ThreadC").start();
}
}
//第一步:创建一个资源类
class Ticket{
private int number=30;
public synchronized void sale(){
if(number>0){
System.out.println(Thread.currentThread().getName()+":卖出第"+number--+"张票,剩下"+number+"张票");
}
}
}
运行结果:
2、Lock接口
2.1、定义
Lock锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对
象。Lock提供了比synchronized更多的功能。
2.2、Lock与Synchronized的区别
- Lock 是-个接口,而synchronized是Java中的关键字, synchronized是内
置的语言实现; - synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock0去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;
- Lock 可以让等待锁的线程响应中断,而synchronized却不行,使用
synchronized时,等待的线程会一直等待 下去,不能够响应中断; - 通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。
- Lock 可以提高多个线程进行读操作的效率。在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争) , 此时Lock的性能要远远优于synchronized。
2.3、示例
import java.util.concurrent.locks.ReentrantLock;
//创建多个线程调用资源类的方法
public class LockSaleTicket {
public static void main(String[] args) {
LTicket lTicket = new LTicket();
new Thread(()->{
lTicket.sale();
},"线程A").start();
new Thread(()->{
for (int i = 0; i <30 ; i++) {
lTicket.sale();
}
},"线程B").start();
new Thread(()->{
lTicket.sale();
},"线程C").start();
}
}
//第一步:创建一个资源类
class LTicket{
int number=30;
private final ReentrantLock lock =new ReentrantLock();
public void sale(){
//上锁
lock.lock();
try{
//判断是否有票
if(number>0){
System.out.println(Thread.currentThread().getName()+":卖出第"+number--+"张票,剩下"+number+"张票");
}
}finally {
//解锁
lock.unlock();
}
}
}