线程同步: 即当有一个线程在对内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态
常用锁- 同步锁,使用关键字 synchronized
- 可重入锁 ReentrantLock
java 编程中,多个线程,同时对一个对象进行操作,会引发出数据不一致的问题,也就是 高并发 场景,要确保数据的安全
例子说明 多用户购票场景 不安全例子package com.wei.sync;
public class UnSafeBuyTicket {
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 {
int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
try {
while (flag) {
Thread.sleep(200);
buy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到票No:" + ticketNums);
ticketNums--;
}
}
安全的改进方案
方式1:在buy()方法上,增加关键字 synchronized,表示 锁 Ticket2 对象的 buy() 方法
package com.wei.sync;
public class SafeBuyTicket {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(ticket, "用户1").start();
new Thread(ticket, "用户2").start();
new Thread(ticket, "用户3").start();
}
}
class Ticket2 implements Runnable {
int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
try {
while (flag) {
Thread.sleep(1000);
buy();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 方式1:使用 synchronized 锁 Ticket2 对象的 buy 方法
public synchronized void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到票No:" + ticketNums);
ticketNums--;
}
}
方式2:使用关键字 synchronized,括住buy()方法块代码,表示 锁 Ticket2 对象的 buy() 方法
package com.wei.sync;
public class SafeBuyTicket {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(ticket, "用户1").start();
new Thread(ticket, "用户2").start();
new Thread(ticket, "用户3").start();
}
}
class Ticket2 implements Runnable {
int ticketNums = 10;
boolean flag = true;
@Override
public void run() {
try {
while (flag) {
Thread.sleep(200);
// this 表示当前对象,此处锁的是 Ticket2 这个对象的buy方法
synchronized (this) {
buy();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到票No:" + ticketNums);
ticketNums--;
}
}
方式3:使用可重入锁ReentrantLock,显式加锁与解锁代码块
package com.wei.sync;
import java.util.concurrent.locks.ReentrantLock;
public class SafeBuyTicket {
public static void main(String[] args) {
Ticket2 ticket = new Ticket2();
new Thread(ticket, "用户1").start();
new Thread(ticket, "用户2").start();
new Thread(ticket, "用户3").start();
}
}
class Ticket2 implements Runnable {
int ticketNums = 10;
boolean flag = true;
ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
while (flag) {
Thread.sleep(200);
// 方式3:使用可重入锁,显式加锁与解锁代码块
try {
lock.lock();
buy();
} finally {
lock.unlock();
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void buy() {
if (ticketNums <= 0) {
flag = false;
return;
}
System.out.println(Thread.currentThread().getName() + "买到票No:" + ticketNums);
ticketNums--;
}
}
两个人同时取一个账户的钱
不安全例子
package com.wei.sync.account;
import java.util.concurrent.TimeUnit;
public class UnsafeDraw {
public static void main(String[] args) {
Account account = new Account(100);
Bank bank = new Bank(account, 50, "小王");
Bank bank2 = new Bank(account, 80, "小明");
bank.start();
bank2.start();
}
}
class Bank extends Thread {
Account account;
int draw;
public Bank(Account account, int draw, String name) {
super(name);
this.account = account;
this.draw = draw;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",初始金额=" + account.initMoney);
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//synchronized (account) {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",再次确认初始金额=" + account.initMoney);
if (account.initMoney < this.draw) {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",余额不足");
return;
}
int nowMoney = account.initMoney - this.draw;
account.initMoney = nowMoney;
System.out.println(Thread.currentThread().getName() + "取完钱,剩余金额=" + nowMoney);
//}
}
}
class Account implements Runnable {
int initMoney;
public Account(int initMoney) {
this.initMoney = initMoney;
}
@Override
public void run() {
}
}
安全的改进方案
方式1:使用synchronized (account) 对账户进行加锁,不能写 synchronized (this) this 是对银行对象加锁,没用
package com.wei.sync.account;
import java.util.concurrent.TimeUnit;
public class UnsafeDraw {
public static void main(String[] args) {
Account account = new Account(100);
Bank bank = new Bank(account, 50, "小王");
Bank bank2 = new Bank(account, 80, "小明");
bank.start();
bank2.start();
}
}
class Bank extends Thread {
Account account;
int draw;
public Bank(Account account, int draw, String name) {
super(name);
this.account = account;
this.draw = draw;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",初始金额=" + account.initMoney);
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 对账户 account 进行加锁,不能写 synchronized (this) this 是对银行对象加锁,没用
synchronized (account) {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",再次确认初始金额=" + account.initMoney);
if (account.initMoney < this.draw) {
System.out.println(Thread.currentThread().getName() + "取钱 " + draw + ",余额不足");
return;
}
int nowMoney = account.initMoney - this.draw;
account.initMoney = nowMoney;
System.out.println(Thread.currentThread().getName() + "取完钱,剩余金额=" + nowMoney);
}
}
}
class Account implements Runnable {
int initMoney;
public Account(int initMoney) {
this.initMoney = initMoney;
}
@Override
public void run() {
}
}
多线程往集合存放数据
不安全例子
资料上说是不安全的,但是~~~,我电脑没重现出来
package com.wei.sync;
import java.util.ArrayList;
import java.util.List;
public class array {
public static void main(String[] args) {
List list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
安全的改进方案
方式1:使用 CopyOnWriteArrayList
package com.wei.sync;
import java.util.concurrent.CopyOnWriteArrayList;
public class array {
public static void main(String[] args) throws InterruptedException {
CopyOnWriteArrayList


