一、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类提供了以下方法:
-
await():使当前线程等待,直到被其他线程调用signal()或signalAll()方法唤醒。
-
awaitUninterruptibly():类似于await()方法,但是在等待期间不会响应线程中断。
-
await(long time, TimeUnit unit):使当前线程等待一段时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。
-
awaitNanos(long nanosTimeout):使当前线程等待一段纳秒时间,在指定的时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。
-
awaitUntil(Date deadline):使当前线程等待直到某个时间,如果在指定时间内没有被其他线程调用signal()或signalAll()方法唤醒,将自动唤醒。
-
signal():唤醒一个等待在Condition上的线程,并使其从await()方法返回。
-
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
乙取钱失败!余额不足!