import java.util.HashMap;
import java.util.concurrent.TimeUnit;

public class Test {
    public static void main(String[] args){
//        TickWindowRunnable.test();
//        Mutex.test();
//        TaskExample.test();
//        ThisMonitor.test();
        DeadLock.test();
    }
}

/*
    4.2 初识synchronized关键字

    synchronized关键字可以实现一个简单的策略来防止线程干扰和内存一致性错误,如果一个对象
    对多个线程是可见的,那么对该对想的所有读或者写都将通过同步的方式来进行,具体体现如下:
        1.synchronized关键字提供了一种锁的机制,能够确保共享变量的互斥访问,从而防止
            数据不一致的问题出现。
        2.synchronized关键字包括monitor enter和monitor exit两个jvm指令,它能够保证
            在任何时候任何线程执行到monitor enter成功之前都必须从主内存中获取数据,而不是
            从缓存中,在monitor exit运行成功之后,共享内存被更新后的值必须刷入主内存
        3.synchronized的指令严格遵守java happens-before规则,一个monitor exit指令之
            前必定要有一个monitor enter
 */

/*
    4.2.2 synchronized关键字的用法

        1.同步方法:
        2.同步代码块:
            private final Object MUTEX = new Object();

            public void sync(){
                synchronized(MUTEX){
                    //Do Something...
                }
            }
 */
class TickWindowRunnable implements Runnable{
    private int index = 1;
    private final static int MAX = 500;
    private final static Object MUTEX = new Object();

    @Override
    public void run() {
        synchronized (MUTEX){
            while (index<=MAX){
                System.out.println(Thread.currentThread()+"的号码是:"+(index++));
            }
        }
    }

    public static void test() {
        final TickWindowRunnable task = new TickWindowRunnable();

        Thread windowThread1 = new Thread(task,"fir");
        Thread windowThread2 = new Thread(task,"sec");
        Thread windowThread3 = new Thread(task,"thi");
        Thread windowThread4 = new Thread(task,"for");
        Thread windowThread5 = new Thread(task,"fif");

        windowThread1.start();
        windowThread2.start();
        windowThread3.start();
        windowThread4.start();
        windowThread5.start();
    }
}

/*
    4.3.1 线程堆栈分析

    线程获取了与mutex关联的monitor锁
        ——额,一个monitor锁与一个mutex关联着,我这个线程现在进入了你的方法
            所以我获取了关联着你这个mutex对象的monitor锁锁锁!!!

 */
class Mutex{
    //注意下这个问题,我发现这个对象都是静态的
    private final static Object MUTEX = new Object();

    public void accessResource(){
        synchronized (MUTEX){
            try{
                TimeUnit.MINUTES.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void test(){
        final Mutex mutex = new Mutex();
        for (int i = 0; i < 5; i++) {
            new Thread(mutex::accessResource).start();
        }
    }
}

/*
==============================================================================================================================
            对 jstack 打印的日志进行分析:
==============================================================================================================================
"Thread-2" #13 prio=5 os_prio=0 tid=0x000000005696a000 nid=0xe54 waiting for monitor entry [0x000000005812e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at Mutex.accessResource(Test.java:82)
    - waiting to lock <0x00000000ec195368> (a java.lang.Object)     //这个地方说明这个线程在等待锁的释放
    at Mutex$$Lambda$1/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread-1" #12 prio=5 os_prio=0 tid=0x0000000056969000 nid=0xc38 waiting on condition [0x000000005825e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at Mutex.accessResource(Test.java:82)
    - locked <0x00000000ec195368> (a java.lang.Object)              //这个地方说明这个线程已经获得锁了
    at Mutex$$Lambda$1/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
==============================================================================================================================

 */

/*
    4.3.2 JVM指令分析
==============================================================================================================================
            JVM指令分析
==============================================================================================================================

    public void accessResource();
    Code:
       0: getstatic     #2       //获取 MUTEX对象
       3: dup
       4: astore_1
       5: monitorenter           //执行monitor enter JVM指令
       6: getstatic     #3
       9: ldc2_w        #4
      12: invokevirtual #6
      15: goto          23       //跳转到23行
      18: astore_2
      19: aload_2
      20: invokevirtual #8
      23: aload_1                //
      24: monitorexit            //执行monitor exit JVM指令
      25: goto          33
      28: astore_3
      29: aload_1
      30: monitorexit
      31: aload_3
      32: athrow
      33: return
==============================================================================================================================

 */

/*
    Monitorenter JVM指令

        每个对象都与一个monitor相关联,一个monitor的lock锁只能被一个线程在同一时间获得,
        在一个线程尝试获得与对象关联monitor的所有权会发生如下几件事情:
            1.如果monitor的计数器为0,则意味这该monitor的lock还没有被获得,某个线程获得
                之后将立即对计数器加一,从此该线程就是这个monitor的所有者了。
            2.如果一个已经拥有该monitor所有权的线程重入,会导致monitor计数器再次累加。
            3.如果monitor已经被其他线程所拥有,则其他线程尝试获取该monitor的所有权时,
                或被陷入阻塞状态,知道monitor计数器变为0,才能再次尝试获取对monitor
                的所有权。

    Monitorexit JVM指令
        当monitor计数器变为0时,被该monitor阻塞的线程将再次尝试获得对该monitor的所有权。
 */

/*
    4.3.3 使用synchronized需要注意的问题

        1.与monitor关联的对象不能为空
        2.synchronized作用域太大了
        3.不同的monitor企图锁相同的方法
 */
class Task implements Runnable{

    private final Object MUTEX = new Object();

    @Override
    public void run() {
        synchronized (MUTEX){

        }
    }

}

class TaskExample{
    public static void test(){
        /*
            案例解析:
                这个案例中构造了5个Runnable实例,Runnable作为线程逻辑执行单元传递给
                Thread~~~线程之间进行monitor lock的争夺只能发生在与monitor关联的
                同一个引用上!!!所以这个地方,需要new 出一个Task对象,或者将MUTEX
                对象编程静态的,但是那没有意义咯,因为你保护的资源大家都有自己的一份,
                哈哈哈哈哈哈哈
         */
        for (int i = 0; i < 5; i++) {
            new Thread(Task::new).start();
        }
    }
}
/*
        4.多个锁的交叉导致死锁

            案例分析:
                如果write方法和read方法,同时在两个线程中调用,而调用了read方法的线程获
                得了MUTEX_READ对象上的monitor锁,调用write方法的线程获得了MUTEX_WRITE
                对象上的monitor锁,就会发生死锁。

 */
class Mutex2{
    private final Object MUTEX_READ = new Object();
    private final Object MUTEX_WRITE = new Object();

    public void read(){
        synchronized (MUTEX_READ){
            synchronized (MUTEX_WRITE){

            }
        }
    }

    public void write() {
        synchronized (MUTEX_WRITE){
            synchronized (MUTEX_READ){

            }
        }
    }
}

/*
    4.4.1 this monitor
 */
class ThisMonitor{
    public synchronized void Method1(){
        System.out.println(Thread.currentThread().getName()+" enter to method1");
        try{
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public synchronized void Method2(){
        System.out.println(Thread.currentThread().getName()+" enter to method2");
        try{
            TimeUnit.MINUTES.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    public static void test(){
        ThisMonitor thisMonitor = new ThisMonitor();
        new Thread(thisMonitor::Method1,"T1").start();
        new Thread(thisMonitor::Method2,"T2").start();
    }
}

/*
==============================================================================================================
            jstack 打印的日志分析
==============================================================================================================
"T2" #12 prio=5 os_prio=0 tid=0x0000000057971800 nid=0xb50 waiting for monitor entry [0x000000005837f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at ThisMonitor.Method2(Test.java:251)
    - waiting to lock <0x00000000eb3c02b8> (a ThisMonitor)
    at ThisMonitor$$Lambda$2/1831932724.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"T1" #11 prio=5 os_prio=0 tid=0x000000005796e800 nid=0x16d8 waiting on condition [0x000000005824e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at java.lang.Thread.sleep(Thread.java:340)
    at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    at ThisMonitor.Method1(Test.java:245)
    - locked <0x00000000eb3c02b8> (a ThisMonitor)
    at ThisMonitor$$Lambda$1/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)
==============================================================================================================

    自己的分析:
        1.我看到了T1进行了加锁,T2在等待锁
        2.额,这个类似与什么情况类似?这个类似与我准备了一个MUTEX对象,用它来保护两个语句块
            按照我之前看到的,一点需要进入到这个对象保护的区域了,它都需要需询问一下monitor
            能否给个锁。但是发现不能狗获得这个锁。所以就只有一个方法运行了。
 */

/*
    4.5.1 程序死锁

        1.交叉锁可导致程序出现死锁
        2.内存不足导致的死锁
        3.一问一答的数据交换
        4.数据库锁
        5.文件锁
        6.死循环引起的死锁

    4.5.2 程序死锁举例
 */
class DeadLock{
    private final Object MUTEX_READ = new Object();
    private final Object MUTEX_WRITE = new Object();

    public void read(){
        synchronized (MUTEX_READ){
            synchronized (MUTEX_WRITE){
                System.out.println("read...");
            }
        }
    }

    public void write() {
        synchronized (MUTEX_WRITE){
            synchronized (MUTEX_READ){
                System.out.println("write...");
            }
        }
    }

    public static void test() {
        final DeadLock deadLock = new DeadLock();

        new Thread(()->{
            while(true){
                deadLock.read();
            }
        },"READ-THEAD").start();

        new Thread(()->{
            while(true){
                deadLock.write();
            }
        },"Write-THEAD").start();
    }
}

/*
    HashMap 不具备线程安全的能力,如果想要使用线程安全的map结构请使用
    ConcurrentHashMap或者使用Collection.synchronizeMap来代替
        ——这个还是了解一下吧。。。

    案例分析:
        书中是这么说的:如果多线程同时写操作的情况下,很容易出现死循环引起的死锁,
        程序运行一段时间后,CPU等资源居高不下。
            ——我想知道为什么会这样。。。
 */

class HashMapDeadLock{
    private final HashMap<String,String> map = new HashMap<>();

    public void add(String key, String value) {
        this.map.put(key,value);
    }

    public static void test(){
        final HashMapDeadLock hmdl = new HashMapDeadLock();
        for (int x = 0; x < 2; x++) {
            new Thread(()->{
                for (int i = 1; i < Integer.MAX_VALUE; i++) {
                    hmdl.add(String.valueOf(i),String.valueOf(i));
                }
            }).start();
        }
    }
}

/*
    4.5.3 死锁诊断
        ——这部分没看着,想找本专业的书,看看这部分
 */

——《Java高并发编程详解》笔记

Java线程安全与数据同步的更多相关文章

  1. java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字

    对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始. 数据同步引入: 这里用之前写过的银行叫号的功能做为 ...

  2. 线程安全、数据同步之 synchronized 与 Lock

    本文Demo下载传送门 写在前面 本篇文章讲的东西都是Android开源网络框架NoHttp的核心点,当然线程.多线程.数据安全这是Java中就有的,为了运行快我们用一个Java项目来讲解. 为什么要 ...

  3. java 线程​基本概念 可见性 同步

    开发高性能并发应用不是一件容易的事情.这类应用的例子包括高性能Web服务器.游戏服务器和搜索引擎爬虫等.这样的应用可能需要同时处理成千上万个请求.对于这样的应用,一般采用多线程或事件驱动的架构.对于J ...

  4. Java多线程学习笔记——从Java JVM对多线程数据同步的一些理解

       我们知道在多线程编程中,我们很大的一部分内容是为了解决线程间的资源同步问题和线程间共同协作解决问题.线程间的同步,通俗我们理解为僧多粥少,在粥有限情况下,我们怎么去防止大家有秩序的喝到粥,不至于 ...

  5. java实现高性能的数据同步

    最近在做一个银行的生产数据脱敏系统,今天写代码时遇到了一个“瓶颈”,脱敏系统需要将生产环境上Infoxmix里的数据原封不动的Copy到另一台 Oracle数据库服务器上,然后对Copy后的数据作些漂 ...

  6. 简单测试Java线程安全中阻塞同步与非阻塞同步性能

    摘抄自周志明老师的<深入理解Java虚拟机:JVM高级特性与最佳实践>13.2.2 线程安全的实现方法 1.名词解释 同步是指锁哥线程并发访问共享数据时,保证共享数据同一时刻只被一个线程访 ...

  7. Java线程如何返回数据

    前言 当开发者从单线程开发模式过渡到多线程环境,一个比较棘手的问题就是如何在一个线程中返回数据,众所周知,run()方法和start()方法不会返回任何值. 笔者在学习<Java Network ...

  8. java 线程之对象的同步和异步

    一.多线程环境下的同步与异步 同步:A线程要请求某个资源,但是此资源正在被B线程使用中,因为同步机制存在,A线程请求不到,怎么办,A线程只能等待下去. package com.jalja.org.th ...

  9. Java线程间怎么实现同步

    1.Object#wait(), Object#notify()让两个线程依次执行 /** * 类AlternatePrintDemo.java的实现描述:交替打印 */ class NumberPr ...

随机推荐

  1. Win10《芒果TV》商店版更新v3.2.6:修复后台任务故障,优化推送频次

    2017湖南卫视大型音乐竞技节目<歌手>,2017年1月21日晚首播第一期,7位歌手惊艳亮嗓,<芒果TV>UWP版迅速更新v3.2.6版,主要是修复后台任务故障,优化推送频次, ...

  2. 水晶报表异常“CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常,未能加载文件或程序集“log4net

    System.TypeInitializationException: “CrystalDecisions.ReportSource.ReportSourceFactory”的类型初始值设定项引发异常 ...

  3. git + gerrit push 代码问题

    关于refs/for 和 refs/heads: 1.     这个不是git的规则,而是gerrit的规则, 2.     Branches, remote-tracking branches, a ...

  4. 浅谈网络I/O多路复用模型 select & poll & epoll

    http://blog.csdn.net/nk_test/article/details/50662946

  5. 客服端JavaScript线程模型

    JavaScript语言核心并不包含任何线程机制,并且客服端JavaScript传统上没有定义任何线程机制.HTML5定义了一种作为后台线程的“WebWorker",但是客服端JavaScr ...

  6. Maven依赖范围<scope>

    1.Maven因为执行一系列编译.测试和部署运行等操作,在不同的操作下使用的classpath不同,依赖范围就是用来控制依赖与三种 classpath(编译classpath.测试classpath. ...

  7. java多线程之线程安全

    线程安全和非线程安全是多线程的经典问题,非线程安全会在多个线程对同一个对象并发访问时发生. 注意1: 非线程安全的问题存在于实例变量中,如果是方法内部的私有变量,则不存在非线程安全问题. 实例变量是对 ...

  8. Spark学习之路(十六)—— Spark Streaming 整合 Kafka

    一.版本说明 Spark针对Kafka的不同版本,提供了两套整合方案:spark-streaming-kafka-0-8和spark-streaming-kafka-0-10,其主要区别如下:   s ...

  9. 【设计模式】结构型05组合模式(Composite Pattern)

    组合模式(Composite Pattern) 意图:将对象组合成树形结构以表示"部分-整体"的层次结构.组合模式使得用户对单个对象和组合对象的使用具有一致性. 主要解决:它在我们 ...

  10. java写出PDF

    1\包 <dependency> <groupId>com.itextpdf</groupId> <artifactId>itextpdf</ar ...