文章目录
- 前言
- 一、线程同步
- 二、代码示例
-
- 1.同步方法解决不安全案例
-
- 案例一(买票问题)
- 案例二(银行取钱问题)
- 案例三(数组问题)
- 总结
前言 上一篇我们共同学习了线程优先级和守护线程,本篇我们将一起学习线程同步的问题同时解决我们之前在学习多线程的过程中的并发问题。
一、线程同步
- 线程同步机制:
在处理多线程问题时,多个线程访问同一个对象,并且有可能修改这个对象,这时候就需要线程的同步。线程同步其实就是一种等待机制,多个需要同时访问这个对象的线程,进入这个对象的等待池形成队列,等待前面的一个线程使用完毕,下一个线程在继续使用。
- 锁机制:
由于同一个进程的多个线程访问同一个存储空间,带来方便的同时也带来了冲突,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制(synchronized),当一个线程获得对象的排它锁,独占资源,其他的线程必须等待,使用后释放锁机可。
问题:
- 一个线程持有锁会导致其他需要此锁的其他线程挂起。
- 在多线程的竞争下,加锁和释放锁会导致较多的上下文切换和调度延时,引起性能问题。
- 如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
- 同步方法:
- synchronized方法:
public synchronized void method(int args){}; - synchronized块:
synchroized (ojb){};
- synchronized方法:
案例一(买票问题)
1、不安全的情况
//不安全的买票
//线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"小明").start();
new Thread(buyTicket,"黄牛").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums = 10;
//票
private boolean flag = true;
//外部停止方式
//买票
@Override
public void run() {
while (flag){
try {
buy();
}catch (InterruptedException e){
e.printStackTrace();
}}
}
public void buy() throws InterruptedException{
//判断是否有票
if(ticketNums<=0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
}
}
运行结果:

文章图片
2、synchronize修饰后
//不安全的买票
//线程不安全,有负数
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"小红").start();
new Thread(buyTicket,"小明").start();
new Thread(buyTicket,"黄牛").start();
}
}
class BuyTicket implements Runnable{
private int ticketNums = 10;
//票
private boolean flag = true;
//外部停止方式
//买票
@Override
public void run() {
while (flag){
try {
buy();
}catch (InterruptedException e){
e.printStackTrace();
}}
}
public synchronized void buy() throws InterruptedException{
//判断是否有票
if(ticketNums<=0){
flag = false;
return;
}
//模拟延时
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票");
}
}
运行结果:

文章图片
案例二(银行取钱问题)
1、不安全的情况
//不安全的取钱
//两个人去银行取钱
public class UnsfeBank {
public static void main(String[] args) {
Account account = new Account("结婚基金",100);
Drawing you = new Drawing(account,50,"小明");
Drawing girlFriend = new Drawing(account,100,"小红");
you.start();
girlFriend.start();
}
}
//账户
class Account {
int money;
String name;
public Account(String name,int money) {
this.name = name;
this.money = money;
}
}
//银行:模拟取钱
class Drawing extends Thread{
Account account;
//账户
//取了多少钱
int drawingMoney;
//手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}@Override
public void run() {
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
if(account.money-drawingMoney<0){
System.out.println("对不起,"+Thread.currentThread().getName()+",您的账户余额不足");
return;
}
//账户余额
account.money = account.money - drawingMoney;
//手里的钱
nowMoney = nowMoney + drawingMoney;
//Thread.currentThread().getName() = this.getName()
System.out.println(account.name + "余额:" + account.money);
System.out.println(this.getName() + "手里的钱:" + nowMoney);
}
}
运行结果:

文章图片
2、synchronize修饰后
//不安全的取钱
//两个人去银行取钱
public class UnsfeBank {
public static void main(String[] args) {
Account account = new Account("结婚基金",100);
Drawing you = new Drawing(account,50,"小明");
Drawing girlFriend = new Drawing(account,100,"小红");
girlFriend.start();
you.start();
}
}
//账户
class Account {
int money;
String name;
public Account(String name,int money) {
this.name = name;
this.money = money;
}
}
//银行:模拟取钱
class Drawing extends Thread{
Account account;
//账户
//取了多少钱
int drawingMoney;
//手里有多少钱
int nowMoney;
public Drawing(Account account, int drawingMoney, String name) {
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}@Override
public void run() {synchronized (account){
if(account.money-drawingMoney<0){
System.out.println("对不起,"+Thread.currentThread().getName()+",您的账户余额不足");
return;
}
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
//账户余额
account.money = account.money - drawingMoney;
//手里的钱
nowMoney = nowMoney + drawingMoney;
//Thread.currentThread().getName() = this.getName()
System.out.println(account.name + "余额:" + account.money);
System.out.println(this.getName() + "手里的钱:" + nowMoney);
}
}
}
运行结果:

文章图片
案例三(数组问题)
1、不安全的情况
//线程不安全的集合
public class Unsafelist {
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();
}
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
运行结果:

文章图片
2、synchronize修饰后
//线程不安全的集合
public class Unsafelist {
public static void main(String[] args) {
List list = new ArrayList();
for(int i = 0;
i<10000;
i++){
new Thread(() -> {
synchronized (list) {
list.add(Thread.currentThread().getName());
}
}).start();
}
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(list.size());
}
}
【多线程详解|第九篇、线程同步(解决并发问题)】运行结果:

文章图片
总结
推荐阅读
- 面试题每日一练|spring事务的隔离级别有哪些()
- nginx+ffmpeg|视频直播(使用ffmpeg推送到nginx-rtmp实现rtsp转换rtmp)
- #|JavaCV-FFmpeg软封装多线程实现录制或推送rtsp流
- eureka|02-若依权限管理子系统简介
- 若依权限管理系统(用户管理)
- 面试|面经自己汇总(三维视觉算法&机器学习&深度学习)——持续更新
- 线程池是怎么回收空闲线程的(如果你认为有定时任务,那你就错了!)
- Java重载和重写的区别
- 服务器|登录会话模型实战