线程同步机制
概念:
- 当多个线程同时访问一种共享资源时,可能会造成数据的覆盖等不一致问题,此时需要对线程之间进行通信和协调,该机制叫做线程同步机制
- 多个线程并发读写同一个临界资源时会发生线程并发安全问题
异步操作:多线程并发的操作,各自独立运行
同步操作:多线程串行的操作,先后执行的顺序
实现方式:
- 在Java语言中使用synchronized关键字来实现同步/对象锁机制从而保证线程执行的原子性
使用同步代码块方式:
使用同步代码块的方式实现部分代码的锁定,格式如下:
synchronized(类类型的引用) {
编写所有需要锁定的代码;
}
案例代码:
package 线程;
/**
* 线程的同步机制
* 同步代码块的方式一
*/
public class AccountTest implements Runnable{
private int balance; // 用于描述账户余额
public AccountTest() {
}
public AccountTest(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "AccountTest{" +
"balance=" + balance +
'}';
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
synchronized (this) {
System.out.println(Thread.currentThread().getName() + "正在操作");
int temp = getBalance();
// 模拟取款
System.out.println("正在取款请稍后");
if (temp > 200) {
System.out.println("正在出钞,请稍后");
temp -= 200;
try {
Thread.sleep(5000);
System.out.println("取钞完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("余额不足");
}
// 模拟将最新的账户余额写入后台
setBalance(temp);
System.out.println(getBalance());
}
}
public static void main(String[] args) {
AccountTest at = new AccountTest(1000);
Thread t1 = new Thread(at);
Thread t2 = new Thread(at);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
使用同步方法的方式
直接使用synchronized关键字来修饰整个方法即可
该方式等价于:
synchronized(this) { 整个方法体的代码 }
案例代码:
- Runnable代码:
/**
* 线程的同步机制
* 直接使用synchronized来修饰整个方法
*/
public class AccountTest3 implements Runnable{
private int balance; // 用于描述账户余额
public AccountTest3() {
}
public AccountTest3(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "AccountTest{" +
"balance=" + balance +
'}';
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public synchronized void run() {
System.out.println(Thread.currentThread().getName() + "正在操作");
int temp = getBalance();
// 模拟取款
System.out.println("正在取款请稍后");
if (temp > 200) {
System.out.println("正在出钞,请稍后");
temp -= 200;
try {
Thread.sleep(5000);
System.out.println("取钞完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("余额不足");
}
// 模拟将最新的账户余额写入后台
setBalance(temp);
System.out.println(getBalance());
}
public static void main(String[] args) {
AccountTest at = new AccountTest(1000);
Thread t1 = new Thread(at);
Thread t2 = new Thread(at);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- Thread代码:
/**
* 线程的同步机制
* synchronized直接修饰
*/
public class AccountTest4 extends Thread{
private int balance; // 用于描述账户余额
public AccountTest4(){
}
public AccountTest4(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public synchronized void run() {
int temp = getBalance();
System.out.println("正在取款,请稍后");
if (temp >= 200) {
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setBalance(temp);
System.out.println("余额" + getBalance());
}
public static void main(String[] args) {
AccountTest2 at2 = new AccountTest2(1000);
at2.start();
AccountTest2 at22 = new AccountTest2(1000);
at22.start();
try {
at2.join();
at22.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
静态方法锁定的方式
- 当我们对一个静态方法加锁,如:
public synchronized static void xxx(){….}
- 那么该方法锁的对象是类对象。每个类都有唯一的一个类对象。获取类对象的方式:类名.class
- 静态方法与非静态方法同时使用了synchronized后它们之间是非互斥关系的
- 原因在于:静态方法锁的是类对象而非静态方法锁的是当前方法所属对象
- 使用synchronized保证线程同步应当注意
- 多个需要同步的线程在访问同步块时,看到的应该是同一个锁对象引用。
- 在使用同步块时应当尽量减少同步范围以提高并发的执行效率
案例代码:
/**
* 线程的同步机制
* 同步代码块方式二
*/
public class AccountTest2 extends Thread{
private int balance; // 用于描述账户余额
private static Demo dm = new Demo();
public AccountTest2(){
}
public AccountTest2(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
synchronized (dm) {
int temp = getBalance();
System.out.println("正在取款,请稍后");
if (temp >= 200) {
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setBalance(temp);
System.out.println("余额" + getBalance());
}
}
public static void main(String[] args) {
AccountTest2 at2 = new AccountTest2(1000);
at2.start();
AccountTest2 at22 = new AccountTest2(1000);
at22.start();
try {
at2.join();
at22.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Demo{}
使用Lock(锁)实现线程同步
- 从Java5开始提供了更强大的线程同步机制—使用显式定义的同步锁对象来实现
- java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具
- 该接口的主要实现类是ReentrantLock类,该类拥有与synchronized相同的并发性,在以后的线程安全控制中,经常使用ReentrantLock类显式加锁和释放锁
- 常用方法:
方法 | 功能 |
---|---|
ReentrantLock() | 使用无参方式构造对象 |
void lock() | 获取锁 |
void unlock() | 释放锁 |
案例代码:
- Runnable代码:
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程的同步机制
* 使用Lock实现线程的同步
*/
public class AccountTest5 implements Runnable {
private int balance; // 用于描述账户余额
private ReentrantLock lock = new ReentrantLock(); // 准备了一把锁
public AccountTest5() {
}
public AccountTest5(int balance) {
this.balance = balance;
}
@Override
public String toString() {
return "AccountTest{" +
"balance=" + balance +
'}';
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
lock.lock(); // 关锁
System.out.println(Thread.currentThread().getName() + "正在操作");
int temp = getBalance();
// 模拟取款
System.out.println("正在取款请稍后");
if (temp > 200) {
System.out.println("正在出钞,请稍后");
temp -= 200;
try {
Thread.sleep(5000);
System.out.println("取钞完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
System.out.println("余额不足");
}
// 模拟将最新的账户余额写入后台
setBalance(temp);
System.out.println(getBalance());
lock.unlock(); // 开锁
}
public static void main(String[] args) {
AccountTest at = new AccountTest(1000);
Thread t1 = new Thread(at);
Thread t2 = new Thread(at);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- Thread代码:
import java.util.concurrent.locks.ReentrantLock;
/**
* 线程的同步机制
* 使用Lock锁实现同步机制
*/
public class AccountTest6 extends Thread{
private int balance; // 用于描述账户余额
private ReentrantLock lock = new ReentrantLock(); // 准备一把锁
public AccountTest6(){
}
public AccountTest6(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
lock.lock();
int temp = getBalance();
System.out.println("正在取款,请稍后");
if (temp >= 200) {
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
setBalance(temp);
System.out.println("余额" + getBalance());
lock.unlock();
}
public static void main(String[] args) {
AccountTest2 at2 = new AccountTest2(1000);
at2.start();
AccountTest2 at22 = new AccountTest2(1000);
at22.start();
try {
at2.join();
at22.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}