当前位置:网站首页>并发之共享模型管程
并发之共享模型管程
2022-06-24 19:41:00 【七国的天下,我要九十九】
并发之共享模型管程
1 共享问题
两个线程对同一个数进行了加和减的操作,因为上下文切换,导致值没能更新,从而影响统计的结果.
static int counter = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
counter++;
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
counter--;
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("{}",counter);
/* 运行结果: -2470 */
问题说明
以上的结果可能是正数、负数、零.因为 Java 中对静态变量的自增,自减并不是原子操作,从字节码来分析
对于i++操作,对应JVM字节码
getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
iadd // 自增
putstatic i // 将修改后的值存入静态变量i
对于i–操作
getstatic i // 获取静态变量i的值
iconst_1 // 准备常量1
isub // 自减
putstatic i // 将修改后的值存入静态变量i
Java 的内存模型如下,完成静态变量的自增,自减需要在主存和工作内存中进行数据交换:

在单线程中执行,没有问题;在多线程中,可能出现正数,负数.
临界区
- 一个程序运行多个线程本身是没有问题的
- 问题出在多个线程访问共享资源
- 问题出在多个线程访问共享资源
- 在多个线程对共享资源读写操作时发生指令交错,就会出现问题
- 一段代码块内如果存在对共享资源的多线程读写操作,称这段代码块为临界区
以下为例:
static int counter = 0;
static void increment()
// 临界区
{
counter++;
}
static void decrement()
// 临界区
{
counter--;
}
竞态条件
多个线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,即发生了竞态条件
2 synchronized
为了避免临界区的竞态条件发生,有多种手段可以达到目的
- 阻塞式的解决方案:synchronized,Lock
- 非阻塞式的解决方案:原子变量
synchronized,即俗称的【对象锁】,它采用互斥的方式让同一 时刻至多只有一个线程能持有【对象锁】,其它线程再想获取这个【对象锁】时就会阻塞住。这样就能保证拥有锁 的线程可以安全的执行临界区内的代码,不用担心线程上下文切换.
java 中互斥和同步都可以采用 synchronized 关键字来完成,区别如下:
互斥是保证临界区的竞态条件发生,同一时刻只能有一个线程执行临界区代码
同步是由于线程执行的先后、顺序不同、需要一个线程等待其它线程运行到某个点
语法
synchronized(对象) // 线程1, 线程2(blocked)
{
临界区
}
// 当线程1 进入临界区 此时发生上下文切换, 线程2 因为没有对象锁 阻塞在外面
案例
static int counter = 0;
static final Object room = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter++;
}
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5000; i++) {
synchronized (room) {
counter--;
}
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("counter = {}",counter);
}
/* 运行结果: counter = 0 */
说明:
synchronized(对象) 中的对象,可以想象为一个房间(room),有唯一入口(门)房间只能一次进入一人 进行计算,线程 t1,t2 想象成两个人
当线程 t1 执行到 synchronized(room) 时就好比 t1 进入了这个房间,并锁住了门拿走了钥匙,在门内执行 count++ 代码
这时候如果 t2 也运行到了 synchronized(room) 时,它发现门被锁住了,只能在门外等待,发生了上下文切 换,阻塞住了
这中间即使 t1 的 cpu 时间片不幸用完,被踢出了门外(不要错误理解为锁住了对象就能一直执行下去哦), 这时门还是锁住的,t1 仍拿着钥匙,t2 线程还在阻塞状态进不来,只有下次轮到 t1 自己再次获得时间片时才 能开门进入
当 t1 执行完 synchronized{} 块内的代码,这时候才会从 obj 房间出来并解开门上的锁,唤醒 t2 线程把钥 匙给他。t2 线程这时才可以进入 obj 房间,锁住了门拿上钥匙,执行它的 count-- 代码
小结
synchronized 实际是用对象锁保证了临界区内代码的原子性,临界区内的代码对外是不可分割的,不会被线程切 换所打断.
如果把 synchronized(obj) 放在 for 循环的外面,如何理解?
- 那么整个for循环就是一个整体, 在循环结束前,其他线程不可运行,保证原子性.
如果 t1 synchronized(obj1) 而 t2 synchronized(obj2) 会怎样运作?
- sync是锁对象. 如上,使用两个不同对象,那么线程之间单独控制,不会因为锁互相影响阻塞.
如果 t1 synchronized(obj) 而 t2 没有加会怎么样?
- 那么线程t1可保证准确性,线程t2无法保证,最后结果也无法保证,sync锁是针对对象,需要关联所有线程都使用同一个对象.
案例改造之面向对象
// 用锁来控制 加 减 查询
class Room {
int value = 0;
public void increment() {
synchronized (this) {
value++;
}
}
public void decrement() {
synchronized (this) {
value--;
}
}
public int get() {
synchronized (this) {
return value;
}
}
}
测试类
@Slf4j
public class Test1 {
public static void main(String[] args) throws InterruptedException {
Room room = new Room();
Thread t1 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
room.increment();
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int j = 0; j < 5000; j++) {
room.decrement();
}
}, "t2");
t1.start();
t2.start();
t1.join();
t2.join();
log.debug("count: {}" , room.get());
}
}
/* 运行结果: count: 0 */
synchronized用在方法上
类对象
class Test{
public synchronized void test() {
}
}
// 等价于
class Test{
public void test() {
synchronized(this) {
}
}
}
类本身
class Test{
public synchronized static void test() {
}
}
// 等价于
class Test{
public static void test() {
synchronized(Test.class) {
}
}
}
线程八锁
- 结果12 或 21
@Slf4j(topic = "c.Number")
class Number{
public synchronized void a() {
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n1.b(); }).start();
}
- 1s后12,或 2 1s后 1
@Slf4j(topic = "c.Number")
class Number{
public synchronized void a() {
sleep(1);
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n1.b(); }).start();
}
- 3 1s 12 或 23 1s 1 或 32 1s 1
@Slf4j(topic = "c.Number")
class Number{
public synchronized void a() {
sleep(1);
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
public void c() {
log.debug("3");
}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n1.b(); }).start();
new Thread(()->{
n1.c(); }).start();
}
- 2 1s 后 1
@Slf4j(topic = "c.Number")
class Number{
public synchronized void a() {
sleep(1);
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
- 2 1s 后 1
@Slf4j(topic = "c.Number")
class Number{
public static synchronized void a() {
sleep(1);
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n1.b(); }).start();
}
- 1s 后12, 或 2 1s后 1
@Slf4j(topic = "c.Number")
class Number{
public static synchronized void a() {
sleep(1);
log.debug("1");
}
public static synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n1.b(); }).start();
}
- 2 1s 后 1
@Slf4j(topic = "c.Number")
class Number{
public static synchronized void a() {
sleep(1);
log.debug("1");
}
public synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
- 1s 后12, 或 2 1s后 1
@Slf4j(topic = "c.Number")
class Number{
public static synchronized void a() {
sleep(1);
log.debug("1");
}
public static synchronized void b() {
log.debug("2");
}
}
public static void main(String[] args) {
Number n1 = new Number();
Number n2 = new Number();
new Thread(()->{
n1.a(); }).start();
new Thread(()->{
n2.b(); }).start();
}
边栏推荐
- EPICS记录参考2--EPICS过程数据库概念
- 推送Markdown格式信息到釘釘機器人
- 双亲委派机制
- Web security XSS foundation 06
- Tech Talk 活动回顾|云原生 DevOps 的 Kubernetes 技巧
- [untitled]
- 2022 simulated 100 questions and simulated examination of high-altitude installation, maintenance and demolition
- Research Report on market supply and demand and strategy of China's solar charging controller industry
- cat写多行内容到文件
- JWT(Json Web Token)
猜你喜欢

Servlet

非单文件组件

对抗训练理论分析:自适应步长快速对抗训练

2022-06-10 work record --js- obtain the date n days after a certain date

See how sparksql supports enterprise data warehouse
![[laravel series 7.9] test](/img/49/4b470a8b309bab4a83eed930dcce65.png)
[laravel series 7.9] test

The extra points and sharp tools are worthy of the trust | know that Chuangyu won the letter of thanks from the defense side of the attack and defense drill!

Panorama of enterprise power in China SSD industry

23研考生注意啦!备考期间最容易中招的骗局,居然是它们?!

Introduction to machine learning compilation course learning notes lesson 1 overview of machine learning compilation
随机推荐
Servlet
Parental delegation mechanism
EPICS記錄參考3 -- 所有記錄都有的字段
[postgraduate entrance examination English] prepare for 2023, learn list9 words
2022-06-16 work record --js- judge the number of digits in string type digits + judge the number of digits in numeric type digits + limit the text length (display n words at most, exceeding...)
Research and investment strategy report on China's bridge anticorrosive coating industry (2022 Edition)
Leetcode algorithm The first common node of two linked lists
High level application of SQL statements in MySQL database (II)
laravel 宝塔安全配置
Solve the problem of non secure websites requesting localhost to report CORS after chrome94
Tech Talk 活动回顾|云原生 DevOps 的 Kubernetes 技巧
Sword finger offer 13 Range of motion of robot
Database transaction Transanction
Non single file component
shopee开店入驻流水如何提交?
结构体的内存对齐
Docker installation redis- simple without pit
【武汉大学】考研初试复试资料分享
LeetCode Algorithm 剑指 Offer II 027. 回文链表
Push markdown format information to the nailing robot