Java线程Lock同步锁

一、Lock锁的基本使用

创建一个ReentrantLock实例作为锁对象。然后,我们创建两个线程t1和t2,它们都会调用acquireLock()方法获取锁并执行同步的代码块。最后,我们可以看到两个线程交替地获取到锁、执行同步代码块并释放锁。

需要注意的是,在使用Lock接口时要注意在finally块中释放锁,以确保在任何情况下都能正常释放锁。否则可能会导致线程出现死锁的情况。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    private static Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            acquireLock();
        });

        Thread t2 = new Thread(() -> {
            acquireLock();
        });

        t1.start();
        t2.start();
    }

    private static void acquireLock() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " 获取到了锁");
            // 执行同步的代码块
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + " 释放了锁");
            lock.unlock(); // 释放锁
        }
    }
}

输出:

Thread-0 获取到了锁
Thread-0 释放了锁
Thread-1 获取到了锁
Thread-1 释放了锁

多线程之同步锁-lock

模拟窗口售票

package com.expgiga.JUC;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
 * 一、用于解决多线程安全问题的方式:
 * 1.同步代码块 synchronized 隐式锁
 * 2.同步方法 synchronized 隐式锁
 * 3.同步锁Lock (jdk1.5以后) 显示锁
 * 注意:显示锁,需要通过lock()方式上锁,必须通过unlock()方式进行释放锁
 */
public class TestLock {
  public static void main(String[] args) {
    Ticket ticket = new Ticket();
    new Thread(ticket, "1号窗口").start();
    new Thread(ticket, "2号窗口").start();
    new Thread(ticket, "3号窗口").start();
  }
}
class Ticket implements Runnable {
  private int tick = 100;
  private Lock lock = new ReentrantLock();
  @Override
  public void run() {
    while (true) {
      lock.lock();
      try {
        if (tick > 0) {
          try {
            Thread.sleep(200);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          System.out.println(Thread.currentThread().getName() + " 完成售票,余票为 " + --tick);
        }
      } finally {
        lock.unlock();
      }
    }
  }
}


二、Condition类

Condition类提供了以下方法:

  1. await():使当前线程等待,直到被其他线程调用signal()或signalAll()方法唤醒。

  2. awaitUninterruptibly():类似于await()方法,但是在等待期间不会响应线程中断。

  3. await(long time, TimeUnit unit):使当前线程等待一段时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

  4. awaitNanos(long nanosTimeout):使当前线程等待一段纳秒时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

  5. awaitUntil(Date deadline):使当前线程等待直到某个时间,如果在指定时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。

  6. signal():唤醒一个等待在Condition上的线程,并使其从await()方法返回。

  7. signalAll():唤醒所有等待在Condition上的线程,并使它们从await()方法返回。使用示例:

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void doSomething() {
        lock.lock();
        try {
            // 等待条件
            condition.await();
            // 执行其他操作
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    public void notifyThread() {
        lock.lock();
        try {
            // 唤醒线程
            condition.signal();
        } finally {
            lock.unlock();
        }
    }
}

在上述示例中,使用lock对象创建了一个Condition实例condition。在doSomething()方法中,线程将调用condition.await()进入等待状态,直到其他线程调用condition.signal()方法唤醒它。notifyThread()方法用于唤醒等待在condition上的线程。可以实现线程之间的等待和通知机制,实现更灵活的线程同步与协调。

三、进程的优先级

使用Thread类的setPriority(int priority)方法来设置线程的优先级。Java中的线程优先级范围是1(最低优先级)到10(最高优先级),其中5为默认优先级。

package lzx6;

public class ThreadPriorityExample {
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            for (int i = 0;i<100; i++) {
                System.out.println("Thread 1: " + i);
            }
        });

        Thread thread2 = new Thread(() -> {
            for (int i = 0;i<100;i++ ) {
                System.out.println("Thread 2: " + i);
            }
        });

        // 设置线程的优先级
        thread1.setPriority(Thread.MIN_PRIORITY);  // 最低优先级
        thread2.setPriority(Thread.MAX_PRIORITY);  // 最高优先级

        // 启动线程
        thread1.start();
        thread2.start();
    }
}

四、 Java线程同步Lock同步锁代码示例

Lock有ReadLock、WriteLock、ReentrantLock(可重入锁)
常用的就是ReentrantLock。代码如下:
代码逻辑:模拟一个账户取钱,同步操作
Account账户类,实现取钱的同步方法、DrawThread取钱的线程
Account.java

package lock.reentrantlock2;
import java.util.concurrent.locks.*;
/**
 *账户类,需保持同步
 */
public class Account
{
  //定义锁对象
  private final ReentrantLock lock = new ReentrantLock();
  private String accountNo;
  private double balance;
  public Account(){}
  public Account(String accountNo , double balance)
  {
    this.accountNo = accountNo;
    this.balance = balance;
  }
  public void setAccountNo(String accountNo)
  {
    this.accountNo = accountNo;
  }
  public String getAccountNo()
  {
     return this.accountNo;
  }
  public double getBalance()
  {
     return this.balance;
  }
  public void draw(double drawAmount)
  {
    lock.lock();
    try
    {
      //账户余额大于取钱数目
      if (balance >= drawAmount)
      {
        //吐出钞票
        System.out.println(Thread.currentThread().getName() +
          "取钱成功!吐出钞票:" + drawAmount);
        try
        {
          Thread.sleep(1);      
        }
        catch (InterruptedException ex)
        {
          ex.printStackTrace();
        }
        //修改余额
        balance -= drawAmount;
        System.out.println("\t余额为: " + balance);
      }
      else
      {
        System.out.println(Thread.currentThread().getName() +
          "取钱失败!余额不足!");
      }     
    }
    finally
    {
      lock.unlock();
    }
  }
  public int hashCode()
  {
    return accountNo.hashCode();
  }
  public boolean equals(Object obj)
  {
    if (obj != null && obj.getClass() == Account.class)
    {
      Account target = (Account)obj;
      return target.getAccountNo().equals(accountNo);
    }
    return false;
  }
}

DrawThread.java

package lock.reentrantlock2;
/**
 * 调用account取钱
 *
 */
public class DrawThread extends Thread
{
  //模拟用户账户
  private Account account;
  //当前取钱线程所希望取的钱数
  private double drawAmount;
  public DrawThread(String name , Account account ,
    double drawAmount)
  {
    super(name);
    this.account = account;
    this.drawAmount = drawAmount;
  }
  //当多条线程修改同一个共享数据时,将涉及到数据安全问题。
  public void run()
  {
    account.draw(drawAmount);
  }
}

TestDraw.java

package lock.reentrantlock2;
/**
 */
public class TestDraw
{
  public static void main(String[] args)
  {
    //创建一个账户
    Account acct = new Account("987654321" , 2000);
    //模拟两个线程对同一个账户取钱
    new DrawThread("甲" , acct , 1800).start();
    new DrawThread("乙" , acct , 800).start();
  }
}

运行结果:
甲取钱成功!吐出钞票:800.0
余额为:200.0
乙取钱失败!余额不足!

  • 14
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值