JAVA如何在线程中加锁(四种方法)

本文详细介绍了JAVA多线程中如何加锁,包括synchronized关键字和Lock锁的使用。讲解了线程的生命周期,强调了在多线程操作共享资源时加锁的重要性。对synchronized的两种用法进行了总结,并分析了死锁问题。接着介绍了Lock接口及其实现类ReentrantLock,展示了Lock锁操作的三部曲,并对比了synchronized与Lock的区别。最后探讨了线程间的通信问题及解决方案。
摘要由CSDN通过智能技术生成

JAVA多线程锁

线程的生命周期

在这里插入图片描述
​ 总共六种状态,可归结为五种,线程的最终是死亡,阻塞不是最终状态,只是一个临时状态。只有调用了start方法,线程才进入就绪阶段。

//新生 ​ NEW,
//运行​ RUNNABLE,
//阻塞​ BLOCKED,
//等待,死死地等​ WAITING,
//超时等待​ TIMED_WAITING,
//终止​ TERMINATED;
Wait/sleep的区别
1、来自于不同的类
wait—>object
Sleep—>Thread//一般不使用,企业一般使用TimeUnit.time.sleep( )
2、关于锁的释放
wait会释放锁,sleep不会释放锁,必须sleep足够的时间
3、适用范围是不同的
Wait:必须在同步代码块中使用
sleep:可以在任何地方使用
4、是否需要捕获异常
wait:不需捕获异常
sleep:需要捕获异常——如果等待过久
需要使用锁的原因:当多个线程操作一个共同资源(共享数据)的时候,可能会出现错误异常。
举例:售票员问题,一共一百张票,三个售票窗口进行售票,错误展示:

//多线程模拟售票问题
import java.util.concurrent.TimeUnit;

public class Windows {
   
    public static void main(String[] args) {
   
        //创建三个线程,线程操纵资源类
        Ticket ticket = new Ticket();
        for(int i=0;i<3;i++){
   
            new Thread(()->{
   
                try {
   
                    ticket.sell();
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            },"售票员"+String.valueOf(i)).start();
        }
    }
}
//资源类
class Ticket{
   
    private int num=100;//总共100张票
    public void sell() throws InterruptedException {
   
        while(num>0){
   
            System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(num--));
            //为了创造一些异常,让线程到此处sleep阻塞一下1
            TimeUnit.SECONDS.sleep(1);
        }
    }
}

在这里插入图片描述

如何解决此类,多线程操作同一数据的问题?

解决办法①synchronized关键字②lock锁

Synchronized关键字

①synchronized关键字修饰方法②synchronized关键字修饰代码块

synchronized关键字修饰方法

代码中只需要将资源类被调用的方法前加上修饰的关键字:synchronized

使用条件:如果操作共享数据的代码完整的声明在一个方法中,可以使用此方法,将方法声明为同步的

关于同步方法的总结:

  1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明
  2. 非静态的同步方法,其同步监视器是:this(当前类的对象)

​ 静态同步方法:同步监视器是:当前类本身(类.class( ))

//资源类
class Ticket{
   
    private int num=100;//总共100张票
    public synchronized void sell() throws InterruptedException {
   
        //此时用synchronized修饰方法sell(),其同步监视器是:this(当前类的对象),由于使用的是
        while(num>0){
   
            System.out.println(Thread.currentThread().getName()+"卖票,票号为:"+(num--));
            //为了创造一些异常,让线程到此处sleep阻塞一下
            TimeUnit.MILLISECONDS.sleep(10);
        }
    }
}

此时不会出现重票问题!

synchronized关键字修饰代码块

使用格式:
synchronized(同步监视器){
//需要被同步的代码
}

说明:1. 操作共享数据的代码,即为需要被同步的代码——>此时注意不可包含多了,也不可包含少了

  1. 共享数据:多个线程共同操作的变量—例子中的票数num
  2. 同步监视器:俗称:锁,可以为任何一个类的对象。要求:多个线程必须要共用同一把锁
//资源类
class Ticket{
   
    private int num=100;//总共100张票
    public void sell() throws InterruptedException {
   
        synchronized (this){
   
            //此时使用synchronized修饰代码块
            while (num > 0) {
   
                System.out.println(Thread.currentThread().getName() + "卖票,票号为:" + (num--));
                //为了创造一些异常,让线程到此处sleep阻塞一下1
                TimeUnit.MILLISECONDS.sleep(10);
            }
        }
    }
}

需要注意的点:
死锁:
1:不同线程分别占用对方需要的同步资源不放,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
2:出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态无法继续
使用同步时,要避免出现死锁
解决办法:
1:专门的算法、原则
2:尽量减少同步资源的定义
3:尽量避免嵌套同步

Lock锁

1:从JDK5开始,Java提供了更加强大的同步机制——通过显示定义同步锁对象来实现同步,同步锁使用Lock对象充当
2:java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具,锁提供了对共享资源的独占访问,每次只能有一个线程和lock对象加锁,线程开始访问共享资源之前应当先获得Lock对象
3:ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁

lock锁操作三部曲:
1、newReentrantLock();
2、lock.lock();//加锁(try内部)
3、finally=>lock.unlock();//解锁
API中官方解释:
在这里插入图片描述

  • 27
    点赞
  • 172
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值