线程同步

synchronized:同步的

例:取钱

不做线程同步的场合,假设骗子和户主同时取钱,可能出现这种情况:

  • 【骗子】取款2000:账户余额1000
  • 【户主】取款2000:账户余额1000
  • 结果是社会财富增加1000,银行不开心。

代码如下所示:

// 账户类
class Account {
private int accountBalance = 2000;
public void withdraw(String userName, int amount) {
System.out.println(userName + "===in===");
if (accountBalance >= amount) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
accountBalance -= amount;// 取钱
System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
} else {
System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
}
System.out.println(userName + "===out===");
}
}
class MyThread extends Thread {
@Override
public void run() {
取钱Demo.act.withdraw(getName(), 1000);
}
}
public class 取钱Demo {
static Account act = new Account();// 账户就一份
public static void main(String[] args) {
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("户主");
t2.setName("骗子");
t2.start();
t1.start();
}
}

想要银行开心,就需要对线程进行同步处理,避免出现重复取款的情况。


线程同步

方法一:锁对象

	public void withdraw(String userName, int amount) {
synchronized (this) {
……
}
}

如果是静态方法,没有this,则是锁住【类名.class】

	public static void withdraw(String userName, int amount) {
synchronized (Account.class) {
……
}
}

方法二:锁方法

当方法被调用时,调用线程必须获得当前对象的锁,否则将等待下去。

方法结束后,锁会被释放。

public synchronized void withdraw(String userName, int amount) {...}

方法三:ReentrantLock重入锁

ReentrantLock是java.util.concurrent.locks.Lock接口的一个实现类。(reentrant:[rɪ'entrənt]再进去)

一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大(可以中断、定时)。

API文档上建议的用法:

建议总是 立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下: 

 class X {
private final ReentrantLock lock = new ReentrantLock();
// ... public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}

应用示例:

import java.util.concurrent.locks.ReentrantLock;
// 账户类
class Account {
private int accountBalance = 2000;
private ReentrantLock lock = new ReentrantLock();
public void withdraw(String userName, int amount) {
synchronized (Account.class) {
lock.lock();
try {
System.out.println(userName + "===in===");
if (accountBalance >= amount) {
Thread.sleep(500);
accountBalance -= amount;// 取钱
System.out.println(userName + "取款" + amount + ",余额:" + accountBalance);
} else {
System.out.println(userName + "企图取款" + amount + ",但余额只有:" + accountBalance);
}
System.out.println(userName + "===out===");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}

练习:买票

(未做线程同步,请实现线程同步)

import java.util.Random;
public class 卖票 {
public static void main(String[] args) {
// 一个Runnable实例对象
SellTicket st = new SellTicket();
// 创建三个线程对象
Thread t1 = new Thread(st, "窗口1");
Thread t2 = new Thread(st, "窗口2");
Thread t3 = new Thread(st, "窗口3");
// 启动线程
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable {
// 定义票数
private int tickets = 100;
private void sell() {
if (tickets > 0) {
// 模拟售票过程
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "正在出售第" + (tickets--) + "张票");
}
}
@Override
public void run() {
while (tickets > 0) {
sell();
// 模拟空闲过程
try {
Thread.sleep(new Random().nextInt(11) * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

Java基础教程——线程同步的更多相关文章

  1. Java基础-多线程-③线程同步之synchronized

    使用线程同步解决多线程安全问题 上一篇 Java基础-多线程-②多线程的安全问题 中我们说到多线程可能引发的安全问题,原因在于多个线程共享了数据,且一个线程在操作(多为写操作)数据的过程中,另一个线程 ...

  2. Java基础8-多线程;同步代码块

    作业解析 利用白富美接口案例,土豪征婚使用匿名内部类对象实现. interface White{ public void white(); } interface Rich{ public void ...

  3. Java基础教程——线程局部变量

    线程局部变量 ThreadLocal,线程局部变量,不提供锁,不做线程共享,而是为每个线程提供变量的独立副本. import java.util.concurrent.*; public class ...

  4. Java基础教程——线程池

    启动新线程,需要和操作系统进行交互,成本比较高. 使用线程池可以提高性能-- 线程池会提前创建大量的空闲线程,随时待命执行线程任务.在执行完了一个任务之后,线程会回到空闲状态,等待执行下一个任务.(这 ...

  5. Java基础教程——线程状态

    线程状态 JAVA定义了6种线程状态: Thread.State public enum State { NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, ...

  6. Java基础教程——线程通信

    线程通信:等待.唤醒 Object方法 这些方法在拥有资源时才能调用 notify 唤醒某个线程.唤醒后不是立马执行,而是等CPU分配 wait 等待,释放锁,不占用CPU资源 notifyAll 唤 ...

  7. Java基础教程:多线程基础(2)——线程间的通信

    Java基础教程:多线程基础(2)——线程间的通信 使线程间进行通信后,系统之间的交互性会更强大,在大大提高CPU利用率的同时还会使程序员对各线程任务在处理的过程中进行有效的把控与监督. 线程间的通信 ...

  8. Java基础教程:多线程基础——线程池

    Java基础教程:多线程基础——线程池 线程池 在正常负载的情况瞎,通过为每一个请求创建一个新的线程来提供服务,从而实现更高的响应性. new Thread(runnable).start() 在生产 ...

  9. Java基础教程:多线程基础(1)——基础操作

    Java:多线程基础(1) 实现多线程的两种方式 1.继承Thread类 public class myThread extends Thread { /** * 继承Thread类,重写RUN方法. ...

随机推荐

  1. uni-app h5端跨域问题解决

    例如我现在的项目运行在 http://localhost:8080,而我有个接口是 https://service.picasso.adesk.com/v1/wallpaper/album,发起请求就 ...

  2. ES & Filebeat 使用 Pipeline 处理日志中的 @timestamp

    使用 Pipeline 处理日志中的 @timestamp Filebeat 收集的日志发送到 ElasticSearch 后,会默认添加一个 @timestamp 字段作为时间戳用于检索,而日志中的 ...

  3. 【算法】二叉树、N叉树先序、中序、后序、BFS、DFS遍历的递归和迭代实现记录(Java版)

    本文总结了刷LeetCode过程中,有关树的遍历的相关代码实现,包括了二叉树.N叉树先序.中序.后序.BFS.DFS遍历的递归和迭代实现.这也是解决树的遍历问题的固定套路. 一.二叉树的先序.中序.后 ...

  4. 应用LORAWAN技术的好处是什么

    LoRaWAN现在一种非常流行的LPWA通信标准,在ISM(工业.科学.医疗)频段使用未经许可的无线电频谱,频率约为900MHz到430MHz(世界各地的标准各不相同). 物联网连接环境除了智能家庭联 ...

  5. java安全编码指南之:文件和共享目录的安全性

    目录 简介 linux下的文件基本权限 linux文件的特殊权限 Set UID 和 Set GID Sticky Bit SUID/SGID/SBIT权限设置 文件隐藏属性 特殊文件 java中在共 ...

  6. 容器探针(liveness and readiness probe)

    一.为什么需要容器探针 如何保持Pod健康   只要将pod调度到某个节点,Kubelet就会运行pod的容器,如果该pod的容器有一个或者所有的都终止运行(容器的主进程崩溃),Kubelet将重启容 ...

  7. leetcode116:search-for-a-range

    题目描述 给出一个有序数组,请在数组中找出目标值的起始位置和结束位置 你的算法的时间复杂度应该在O(log n)之内 如果数组中不存在目标,返回[-1, -1]. 例如: 给出的数组是[5, 7, 7 ...

  8. jQuery其他事件

    ready(fn)当DOM载入就绪可以查询及操作时,绑定一个要执行的函数. resize([[data],fn])当调整浏览器窗口的大小时,发生resize事件.它属于BOM中的window对象. f ...

  9. Open-Falcon 告警 如何实现自定义多功能告警

    Open-Falcon 是小米运维部开源的一款互联网企业级监控系统解决方案.其中有着如下的特点: ①强大灵活的数据采集:自动发现,支持falcon-agent.snmp.支持用户主动push.用户自定 ...

  10. Android 架构组件-Lifecycle、LiveData、ViewModel

    Lifecycle Lifecycle组件包括LifecycleOwner.LifecleObserver,能方便监听Activity或者Fragment的生命周期. 步骤: 1.实现Lifecycl ...