什么是Java锁?

  • Post category:Java

Java锁是一种多线程同步的机制,通常用于保护共享资源,避免多个线程同时访问和修改同一个资源导致的数据不一致性和安全问题。Java中有多种锁机制可供使用,如synchronized关键字、ReentrantLock、ReadWriteLock等。下面将详细讲解这些锁的使用攻略:

一、synchronized关键字

synchronized是Java中最基本的锁机制,可以用来修饰方法、代码块和静态方法。使用synchronized关键字时,线程会在执行代码块前尝试获取锁,如果锁已经被其他线程获得,则该线程会进入阻塞状态,直到其他线程释放锁。

1.1 synchronized方法

以下是synchronized方法的使用示例:

public class SyncTest {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}

这里定义了一个SyncTest类,其中的increment和getCount方法都被synchronized修饰。这意味着在任意时刻只能有一个线程访问这两个方法,从而保证了count变量的原子性和可见性。

1.2 synchronized代码块

以下是synchronized代码块的使用示例:

public class SyncTest {
    private int count = 0;
    private Object lock = new Object();

    public void increment() {
        synchronized (lock) {
            count++;
        }
    }

    public int getCount() {
        synchronized (lock) {
            return count;
        }
    }
}

这里定义了一个SyncTest类,其中的increment和getCount方法都使用了synchronized代码块来保护count变量。由于synchronized关键字只能保证同一对象的同步,因此需要定义一个锁对象lock来保证不同方法间的同步。

二、ReentrantLock

ReentrantLock是Java中的另一种锁机制,它提供了更灵活的锁控制方式,如可重入锁、公平锁等。与synchronized关键字不同,ReentrantLock必须手动获取和释放锁。

以下是ReentrantLock的使用示例:

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

public class LockTest {
    private int count = 0;
    private Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}

这里定义了一个LockTest类,其中的increment和getCount方法都使用了ReentrantLock来保护count变量。调用lock方法获取锁,调用unlock方法释放锁,这里使用了try-finally语句块保证锁的释放。

三、ReadWriteLock

ReadWriteLock是Java中的另一种锁机制,用于解决读写冲突的问题。在使用ReadWriteLock时,多个线程可以同时读取共享数据,但只能有一个线程进行写操作。

以下是ReadWriteLock的使用示例:

import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class RWLockTest {
    private int count = 0;
    private ReadWriteLock lock = new ReentrantReadWriteLock();

    public void increment() {
        lock.writeLock().lock();
        try {
            count++;
        } finally {
            lock.writeLock().unlock();
        }
    }

    public int getCount() {
        lock.readLock().lock();
        try {
            return count;
        } finally {
            lock.readLock().unlock();
        }
    }
}

这里定义了一个RWLockTest类,其中的increment方法使用了writeLock来保护count变量的写操作,getCount方法使用了readLock来保护count变量的读操作。由于读操作不会影响数据一致性,因此使用读锁时可以允许多个线程同时访问共享数据。

示例说明

示例1:使用synchronized关键字实现银行转账

public class Account {
    private int balance;

    public Account(int balance) {
        this.balance = balance;
    }

    public synchronized void withdraw(int amount) {
        if (balance >= amount) {
            System.out.println(Thread.currentThread().getName() +
                    " withdraw " + amount);
            balance -= amount;
        } else {
            System.out.println(Thread.currentThread().getName() +
                    " withdraw failed");
        }
    }

    public synchronized void deposit(int amount) {
        System.out.println(Thread.currentThread().getName() +
                " deposit " + amount);
        balance += amount;
    }

    public synchronized int getBalance() {
        return balance;
    }
}

public class Transfer {
    public static void main(String[] args) {
        Account account1 = new Account(1000);
        Account account2 = new Account(2000);
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(() -> {
            for (int i = 0; i < 10; i++) {
                account1.withdraw(100);
                account2.deposit(100);
            }
        });
        executor.execute(() -> {
            for (int i = 0; i < 10; i++) {
                account2.withdraw(100);
                account1.deposit(100);
            }
        });
        executor.shutdown();
    }
}

在上述示例中,使用synchronized关键字来保护共享资源的访问,从而实现银行转账过程的线程安全。

示例2:使用ReentrantLock实现生产者消费者模型

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

public class ProducerConsumer {
    private static final int CAPACITY = 3;

    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        Lock lock = new ReentrantLock();
        Condition notFull = lock.newCondition();
        Condition notEmpty = lock.newCondition();

        Thread producer = new Thread(() -> {
            lock.lock();
            try {
                while (queue.size() == CAPACITY) {
                    notFull.await();
                }
                int n = (int)(Math.random() * 100);
                queue.offer(n);
                System.out.println("Produced: " + n);
                notEmpty.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        Thread consumer = new Thread(() -> {
            lock.lock();
            try {
                while (queue.size() == 0) {
                    notEmpty.await();
                }
                int n = queue.poll();
                System.out.println("Consumed: " + n);
                notFull.signal();
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        producer.start();
        consumer.start();
    }
}

在上述示例中,使用ReentrantLock和Condition来实现生产者消费者模型的线程安全。其中生产者线程使用notFull条件变量等待队列未满,消费者线程使用notEmpty条件变量等待队列不为空。