解决线程安全问题的方式三:Lock锁
@[toc]
Lock锁
(jdk5.0后的方式)
- 从JDK 5.0开始,Java提供了更强大的线程同步机制——通过显式定义同 步锁对象来实现同步。同步锁使用Lock对象充当。
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的 工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象 加锁,线程开始访问共享资源之前应先获得Lock对象。
- ReentrantLock 类实现了 Lock ,它拥有与 synchronized 相同的并发性和 内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以 显式加锁、释放锁。
- 代码举例(实现Runnable接口的方式去创建线程):
package com.jsm.java3;
import java.util.concurrent.locks.ReentrantLock;
/*
解决线程安全问题的方式三:Lock锁,jdk5.0后的方式
例子:创建三个窗口卖票,总票数是100张
*/
public class LockTest2 {
public static void main(String[] args) {
Order w1 = new Order();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.start();
t2.start();
t3.start();
}
}
class Order implements Runnable {
private int num = 100;//未加static,因为此时三个线程共用同一个num,此时只造了一个对象
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//2.调用锁定方法lock();
lock.lock();
try {
while (true) {
if (num > 0) {
System.out.println(Thread.currentThread().getName() + "卖出的票号为:" + num);
num--;
} else {
break;
}
}
} finally{
//3.调用解锁方法unlock();
lock.unlock();
}
}
}
注意:如果这里用继承Thread的方式去创建线程,lock就得加个static,设置为静态,因为创建了多个对象,要保证lock保证是唯一的
synchronized 与 Lock 的对比
相同点:都是用来解决线程的安全问题
不同点:
- synchronized 机制在执行完相应的同步代码之后,自动的释放同步监视器
- Lock需要手动的启动同步( .lock() ),同时结束同步也需要手动的实现(unlock() )
优先使用顺序(建议)
Lock —>同步代码块(已经进入了方法体,分配了相应资源) —> 同步方法 (在方法体之外)
练习题
分析:
- 是否是多线程问题?是,两个储户线程
- 是否有共享数据吗?有,账户余额
- 涉及到线程安全问题吗?是,需要考虑如何解决线程安全问题(同步机制:有三种方式)
答案代码:
package com.jsm.java3;
import com.sun.corba.se.impl.ior.ObjectAdapterIdNumber;
/*
银行有一个账户。
有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打
印账户余额。
*/
public class LXT {
public static void main(String[] args) {
Account acct = new Account(0);
Customer c1 = new Customer(acct);
Customer c2 = new Customer(acct);
c1.setName("小明");
c2.setName("小黄");
c1.start();
c2.start();
}
}
class Account {//账户
private double balance;//余额
public Account(double balance) {
this.balance = balance;
}
//存钱
public void deposit(double amt) {
synchronized (Account.class) {//解决线程的安全问题
if (amt > 0) {
balance += amt;
System.out.println(Thread.currentThread().getName() + "存钱成功,余额为:" + balance);
}
}
}
}
class Customer extends Thread {//客户
private Account acct1;
public Customer(Account acct1) {
this.acct1 = acct1;
}
@Override
public void run() {
for (int i = 0; i < 3; i++) {
acct1.deposit(1000);
}
}
}
方式二:
package com.jsm.java3;
/*
银行有一个账户。
有两个储户分别向同一个账户存3000元,每次存1000,存3次。每次存完打
印账户余额。
*/
public class LXT2 {
public static void main(String[] args) {
Money m1 = new Money();
Money m2 = new Money();
m1.start();
m2.start();
}
}
class Money extends Thread {
private double UserMoney = 3000;
private static double money = 0;
@Override
public void run() {
while (true) {
synchronized (Money.class) {
if (UserMoney>0 ) {
UserMoney -= 1000;
money += 1000;
System.out.println(Thread.currentThread().getName() + "存入1000元,账户余额为:" + money);
} else {
break;
}
}
}
}
}
.println(Thread.currentThread().getName() + "存入1000元,账户余额为:" + money);
} else {
break;
}
}
}
}
}