美文网首页
第66条:同步访问共享的可变数据《高效java》

第66条:同步访问共享的可变数据《高效java》

作者: 戴码 | 来源:发表于2017-08-23 17:59 被阅读0次
同步.jpg

关键字:synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。

同步

概念:

单线程

理解:

1、当一个对象被一个线程修改的时候,可以组织另一个线程观察到对象内部不一致的状态。
2、如果没有同步,一个线程的变化就不能被其他线程看到。同步不仅可以阻止一个线程看到对象处于不一致的状态,还可以保证进入同步代码块的线程看到同一个锁之前所有的修改效果。

原子性

概念

原子是
读或写一个非long或double的基本类型变量是原子的

理解

如果把一个事务可看作是一个程序,它要么完整的被执行,要么完全不执行。这种特性就叫原子性

应用场景:一个线程干扰另一个线程任务

如果需要停止一个线程,可以使用Thread.stop方法,但是这个方法很久以前就不提倡使用了,因为不安全——使用它会使数据被破坏。
因此,建议的做法是,让一个线程轮询一个boolean域,另一个线程设置这个boolean域即可

错误示例

public class Temp {  
  
    private static boolean stopRequested;  
  
    public static void main(String[] args) throws InterruptedException {  
        Thread backgroundThread = new Thread(new Runnable() {  
            @Override  
            public void run() {  
                int i = 0;  
                while (!stopRequested) {  
                    i++;  
                }  
            }  
        });  
        backgroundThread.start();  
        TimeUnit.SECONDS.sleep(1);  
        stopRequested = true;  
    }  
  
}  

实际上以上这段程序会永远的运行下去,因为没有使用同步,无法保证后台进程可以看到stopRequested值的改变。虚拟机将代码:

while (!stopRequested) {  
    i++;  
}  

编译成:

if(!stopRequested)  
    while(true)  
        {  
            i++;  
        }  

这样一来,永远都不会看到stopRequested的改变。

修正示例-使用synchronized

private static boolean stopRequested;  
  
private static synchronized void requestStop() {  
    stopRequested = true;  
}  
  
private static synchronized boolean stopRequested() {  
    return stopRequested;  
}  
  
public static void main(String[] args) throws InterruptedException {  
    Thread backgroundThread = new Thread(new Runnable() {  
        @Override  
        public void run() {  
            int i = 0;  
            while (!stopRequested()) {  
                i++;  
            }  
        }  
    });  
    backgroundThread.start();  
    TimeUnit.SECONDS.sleep(1);  
    requestStop();  
}  

修正示例-使用volatile

private static volatile boolean stopRequested;  
  
public static void main(String[] args) throws InterruptedException {  
    Thread backgroundThread = new Thread(new Runnable() {  
        @Override  
        public void run() {  
            int i = 0;  
            while (!stopRequested) {  
                i++;  
            }  
        }  
    });  
    backgroundThread.start();  
    TimeUnit.SECONDS.sleep(1);  
    stopRequested = true;  
}  

==注意==:使用volatile容易出现错误

private static volatile int nextSerialNumber = 0;  
  
public static int generateSerialNumber() {  
    return nextSerialNumber++;  
}

尽管使用了volatile,但是由于++运算符不是原子的,因此在多线程的时候会出错。++运算符执行两项操作:1、读取值;2、写回新值(相当于原值+1)。如果第二个线程在第一个线程读取旧值和写会新值的时候读取了这个域,就会产生错误,他们会得到相同的SerialNumber。
解决方法可以是,加入synchronized并去掉volatile。进一步的,可以用Long来代替int,或者在快要溢出的时候,抛出异常。更好的是使用AtomicLong类。

private static final AtomicLong nextSerialNumber = new AtomicLong(0);  
  
public static Long generateSerialNumber() {  
    return nextSerialNumber.incrementAndGet();  
}

相关文章

  • Java中的多线程进阶篇

    这一篇主要是学习Effecttive Java并发章节的笔记。 1、同步访问共享的可变数据 如果没有同步,一个线程...

  • 第十一章 并发

    目录 同步访问共享的可变数据 避免过度同步 EXECUTORS, TASKS, STREAMS 优于线程 优先使用...

  • 第66条:同步访问共享的可变数据《高效java》

    关键字:synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块。 同步 概念...

  • Effective Java-并发

    同步访问共享的可变数据 避免过度同步 executor和task优先于线程 并发工具优先于wait和notify ...

  • 1_基础知识_chapter03_对象的共享_3_线程封闭

    (1) 访问共享的可变数据时, 通常就需要同步(2) 为了避免同步, 可以不共享数据, 在单线程内访问拘束的话就不...

  • 线程封闭与不变性 Java并发编程实战总结

    当访问共享的可变数据时, 通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据, 就不...

  • 【JDK1.8源码学习】ThreadLcoal

    前言 线程封闭:当访问共享的可变数据时,通常需要同步。一种避免同步的方式就是不共享数据。如果仅在单线程内访问数据,...

  • Java ThreadLocal分析

    参考:ThreadLocal与WeakReference 线程封闭 当访问共享的可变数据时,通常需要使用同步;一种...

  • 线程封闭

    线程封闭 当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据...

  • 《Effective Java》读书笔记 —— 并发

    1.同步访问共享的可变数据 同步:同步不仅可以阻止一个线程看到对象处于不一致的状态之中,还可以保证进入同步方法或者...

网友评论

      本文标题:第66条:同步访问共享的可变数据《高效java》

      本文链接:https://www.haomeiwen.com/subject/waoddxtx.html