线程同步

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. Mongodb命令 --- MongoDB基础用法(二)

    Mongodb命令 数据库操作 创建数据库 MongoDB 创建数据库的语法格式如下: use DATABASE_NAME 如果数据库不存在,则创建数据库,否则切换到指定数据库. 删除数据库 Mong ...

  2. SQL Server 列存储索引 第四篇:实时运营数据分析

    实时运营数据分析(real-time operational analytics )是指同时在同一张数据表上执行分析处理和业务处理.分析查询主要是对海量数据执行聚合查询,而事务主要是指对数据表进行少量 ...

  3. git提交后文件夹显示灰色无法打开的解决办法

    上传完本地文件到gitee线上后发现如下情况,文件夹显示为灰色并且无法访问. xxx@xxx 的灰色文件 问题原因 : 无法点击的灰色文件夹中含有 .git 文件 即在本地初始化的仓库(使用 git ...

  4. 云服务器部署scrapyd爬虫

    Scrapyd部署爬虫项目 GitHub:https://github.com/scrapy/scrapyd API 文档:http://scrapyd.readthedocs.io/en/stabl ...

  5. Charles使用part3——安装证书&手机抓取https请求

    一.配置 Charles 根证书 1.进入 Charles->Help->SSL Proxying->Install Charles Root Certificate ,会打开证书, ...

  6. P5691 [NOI2001]方程的解数

    题意描述 方程的解数 求方程 \(\sum_{i=1}^{n}k_ix_i^{p_i}=0(x_i\in [1,m])\) 的解的个数. 算法分析 远古 NOI 的题目就是水 类似于这道题. 做过这道 ...

  7. Java才是世界上最好的语言,Java在高频交易中替代C++

    高频交易 高频交易是指从那些人们无法利用的极为短暂的市场变化中寻求获利的计算机化交易,比如,某种证券买入价和卖出价差价的微小变化,或者某只股票在不同交易所之间的微小价差.在高频交易中,自动化应用程序每 ...

  8. 2020最常见的200+Java面试题汇总(含答案解析)

    前言 2020年快要结束了,很多朋友问题,有没有整理今年的一些面试题,最近抽时间整理了一份Java面试题.或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信足以应对目前市面上绝大部分的 ...

  9. 02_tcp_deadlock

    # 这个程序我们是测试客户端和服务端在进行通信的过程中,可能会产生死锁的情况. # 这是因为缓冲区,和TCP协议的可靠性连接导致的. # 在程序中我们可以看到,客户端先向服务端发送数据,然后服务端就收 ...

  10. read函数

    ssize_t read(int fildes, void *buf, size_t nbyte); 返回值: > 0: 实际读到的字节数 = 0: 读完数据(读文件, 管道, socket末尾 ...