【Java】学习路径47-线程锁synchronized
线程安全问题:
简单来说,就是多个线程在操作同一个变量时引起的问题。
这里是用一个简单的例子说明一下:
以Runnable创建的线程为例:一个售票系统,count代表当前票数,卖出一张count--。
Runnable线程类:
public class Runnable_Exp implements Runnable{
private int count = 50;
@Override
public void run() {
while(true)
if(count >= 0){
System.out.println(Thread.currentThread().getName() +"现在的数:"+count);
count--;
}else break;
}
}
在main调用数个相同的Runnable线程:
Runnable_Exp rExp = new Runnable_Exp();
Thread trs1 = new Thread(rExp,"线程1");
Thread trs2 = new Thread(rExp,"线程2");
Thread trs3 = new Thread(rExp,"线程3");
Thread trs4 = new Thread(rExp,"线程4");
trs1.start();trs2.start();trs3.start();trs4.start();
运行:
我们会发现一个问题,票数会有相同的!
这就是线程安全的体现。此时我们就需要使用线程锁解决这个问题。
另外,我们之前学习的集合类中,有分线程安全与线程不安全的两类。
StringBuffer和Vector是线程安全的。
StringBuilder和ArrayList是线程不安全的。
在多线程中,我们不可以使用多个线程同时操作不安全的集合类
线程锁的使用:synchronized
Runnable中使用线程锁:
我们只需要修改一下Runnable线程类。
public class Runnable_Exp implements Runnable{
private int count = 500;
private Object lock = new Object();
@Override
public void run() {
while(true)
synchronized (lock) {
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}
}
解析:
添加一个线程锁对象,一般使用Object即可。
private Object lock = new Object();
这个Object对象lock是四个线程共享的(且这个lock一定是共用的!)可以自己尝试一下把锁放进run()中。
synchronized (lock)
当线程一运行到上面这一行的时候,线程一就会占用lock对象(相当于上锁了),直到线程一执行完synchronized语句块内的全部代码,lock对象就会取消占用。
若线程二运行到synchronized语句时,发现lock对象已经被占用了,则会等待,直到lock被取消占用。
有可能多个线程一起抢占。
效率会比较低。毕竟是你执行完再到我执行。无法同步执行。这就是所谓“抢占式”执行。
Thread中使用线程锁:
Thread线程类中:
public class Thread_Exp extends Thread {
public Thread_Exp(){}
public Thread_Exp(String name){
super(name);
}
private static Object lock = new Object();
private static int count = 100;
public void run(){
while(true)
synchronized(lock){
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}
}
需要注意的是,这里的线程锁对象和count都需要设置为静态的!
Thread_Exp t1 = new Thread_Exp("线程1");
Thread_Exp t2 = new Thread_Exp("线程2");
Thread_Exp t3 = new Thread_Exp("线程3");
Thread_Exp t4 = new Thread_Exp("线程4");
t1.start();t2.start();t3.start();t4.start();
不创建Object对象可以实现相同的操作吗?
答案是可以的,我们可以不用创建Object类型的lock对象。
我们直接用synchronized(this)就可以了。
相当于检测当前对象是非被线程占用了(加锁)。
实例:
@Override
public void run() {
synchronized (this){
while(true)
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
}
我们还可以直接使用synchronized关键字描述一个方法,具体请看下文。
使用synchronized方法简化上述代码:
同步方法,顾名思义,就是自带线程锁的方法。
只需要在方法的返回值前面添加synchronized关键字即可。
原先的代码我们可以写成:
使得run()方法更加简洁。
@Override
public void run() {
Count();
}
public synchronized void Count(){
while(true)
if (count >= 0) {
System.out.println(Thread.currentThread().getName() + "现在的数:" + count);
count--;
} else break;
}
使用同步方法,这个方法体就会自动被加锁。
关于线程安全的方法:
上面我们说了StringBuffer方法是线程安全的,那我们是怎么知道的呢?
我们可以直接查看StringBuffer中方法的源代码。
例如attend方法:
@Override
@IntrinsicCandidate
public synchronized StringBuffer append(char c) {
toStringCache = null;
super.append(c);
return this;
}
我们可以看到这个方法是synchronized方法。
大家可以自行去研究一下各种Java自带的方法,是否是线程安全的。
【Java】学习路径47-线程锁synchronized的更多相关文章
- 【Java】学习路径48-线程锁ReentrantLock
与上一章学习的线程锁synchronized类似,都是为了解决线程安全的问题. 使用方法: 新建一个ReentrantLock对象.(如果使用Thread多线程,则需要声明static静态) 然后在需 ...
- Java学习路径及练手项目合集
Java 在编程语言排行榜中一直位列前排,可知 Java 语言的受欢迎程度了. 实验楼上的[Java 学习路径]中将首先完成 Java基础.JDK.JDBC.正则表达式等基础实验,然后进阶到 J2SE ...
- java学习笔记15--多线程编程基础2
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...
- Java学习路径(抛光砖)
这就是我刚刚在五孔问答中找到的Java学习路线图抛光砖价格.我个人认为,这条Java学习路线是可以的.它是2018年相对较新的Java学习路线,更符合企业就业标准. Java学习路径的第一阶段:Jav ...
- java学习笔记14--多线程编程基础1
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...
- 深入理解java:2.2. 同步锁Synchronized及其实现原理
同步的基本思想 为了保证共享数据在同一时刻只被一个线程使用,我们有一种很简单的实现思想,就是 在共享数据里保存一个锁 ,当没有线程访问时,锁是空的. 当有第一个线程访问时,就 在锁里保存这个线程的标识 ...
- 【转】Java学习---深入理解线程池
[原文]https://www.toutiao.com/i6566022142666736131/ 我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很 ...
- Java学习路径:不走弯路,这是一条捷径
1.如何学习编程? JAVA是一种平台.也是一种程序设计语言,怎样学好程序设计不只适用于JAVA,对C++等其它程序设计语言也一样管用.有编程高手觉得,JAVA也好C也好没什么分别,拿来就用.为什么他 ...
- 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition
img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...
随机推荐
- 跟着 Guava、Spring 学习如何设计观察者模式
文章首发在公众号(龙台的技术笔记),之后同步到掘金和个人网站:xiaomage.info 今天讲解一篇行为型设计模式,什么是行为型?行为型主要负责设计 类或对象之间的交互.工作中常用的观察者模式就是一 ...
- Linux云服务器 磁盘分区失败
背景:阿里云服务器,df -h /dev/vda1有40g,然后想把这40g拆一下,拆成几个分区,挂载到不同的路径下. 行动:需要先卸载挂载的盘,但是umount失败,通过fuser删除盘上的服务失 ...
- [pwn基础]Pwntools学习
目录 [pwn基础]Pwntools学习 Pwntools介绍 Pwntools安装 Pwntools常用模块和函数 pwnlib.tubes模块学习 tubes.process pwnlib.con ...
- 为什么新的5G标准将为技术栈带来更低的 TCO
摘要 新5G标准和边缘计算对低延迟的要求,给那些试图将一堆不同组件组装成一个不会出现故障且仍具有低延迟的高成本效益应用程序公司带来了严峻的挑战.事实上,这个问题非常严重,以至于需要重新考虑架构. ...
- svn提交报错Unexpected HTTP status 413 'Request Entity Too Large' on
问题原因:nginx的client_max_body_size设置过小,默认 1M,如果请求的正文数据大于client_max_body_size,HTTP协议会报错 413 Request Enti ...
- NC16649 [NOIP2005]校门外的树
NC16649 [NOIP2005]校门外的树 题目 题目描述 某校大门外长度为 \(L\) 的马路上有一排树,每两棵相邻的树之间的间隔都是 \(1\) 米.我们可以把马路看成一个数轴,马路的一端在数 ...
- 自定义 systemd service
Red Hat Linux 自 7 版本后 采用systemd 形式取代原先 init ,用户可以参考 系统service 创建自己的service ,以便于日常统一管理,系统service 存储路径 ...
- 《SVDNet for Pedestrian Retrieval》理解
<SVDNet for Pedestrian Retrieval>理解 Abstract: 这篇文章提出了一个用于检索问题的SVDNet,聚焦于在行人再识别上的应用.我们查看卷积神经网络中 ...
- Django WEB开发 - Django 3.0 Tutorial入门
1. Django 官方网站 https://docs.djangoproject.com/zh-hans/3.0/intro/tutorial01/ 2. Model View Controller ...
- ArrayList集合存储基本数据类型
如何存储基本数据类型 ArrayList对象不能存储基本类型,只能存储引用类型的数据.类似 <int> 不能写,但是存储基本数据类型对应的 包装类型是可以的.所以,想要存储基本类型数据, ...