JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制


JAVA之旅,一路有你,加油!

一.静态同步函数的锁是class对象

我们在上节验证了同步函数的锁是this,但是对于静态同步函数,你又知道多少呢?

我们做一个这样的小实验,我们给show方法加上static关键字去修饰

private static synchronized void show() {
        if (tick > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread() + "show:" + tick--);
        }
    }

然后我们来打印一下

发现他打印出0票了,说明他还是存在this隐患,同时也说明了一点就是他使用的锁不是this,那既然不是this,那是什么呢?

  • 因为静态方法中,不可以定义this,我们可以分析,静态进内存中,内存中没有本类对象,但是一定有该类的字节码文件对象类名.class,我们可以这样同步
synchronized (MyThread.class)

你就会发现,线程是安全的了

静态同步的方法,使用的锁是该字节码的对象 类名.class

二.多线成中的单例设计模式

还记得我们讲的单例设计模式吗?我们今年温习一下这两个实现单例模式的方法

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {

        /**
         * 单例设计模式
         */

    }
}

/**
 * 饿汉式
 *
 * @author LGL
 *
 */
class Single1 {
    private static final Single1 s = new Single1();

    private Single1() {

    }

    public static Single1 getInstance() {
        return s;
    }
}

/**
 * 懒汉式
 * @author LGL
 *
 */
class Single {
    private static Single s = null;

    private Single() {

    }

    public static Single getInstance() {
        if (s == null) {
            s = new Single();
        }
        return s;
    }

}

我们着重点来看懒汉式,你会发现这个s是共享数据,所以我们所以延迟访问的话,一定会出现安全隐患的,但是我们使用synchronized来修饰的话,多线程启动每次都要判断有没有锁,势必会麻烦的,所以我们可以这样写

public static Single getInstance() {
        if (s == null) {
            synchronized (Single.class) {
                if (s == null) {
                    s = new Single();
                }
            }
        }
        return s;
    }

这样其实是比较麻烦的,我们用饿汉式比较多,懒汉式作用是延时加载,多线成访问就会有安全问题

三.多线程的死锁

我们同步当中会产生一个问题,那就是死锁

  • 同步中嵌套同步

是怎么个意思?我们来实现一下这段代码

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {

        /**
         * 需求:简单的卖票程序,多个线程同时卖票
         */
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread);
        Thread t2 = new Thread(myThread);

        t1.start();
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        myThread.flag = false;
        t2.start();
    }

}

/**
 * 卖票程序
 *
 * @author LGL
 *
 */
class MyThread implements Runnable {

    // 票数
    private int tick = 100;

    Object j = new Object();

    boolean flag = true;

    @Override
    public void run() {

        if (flag) {
            while (true) {
                synchronized (j) {
                    show();
                }
            }
        } else {
            while (true) {
                show();
            }
        }

    }

    private synchronized void show() {
        synchronized (j) {
            if (tick > 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "show:" + tick--);
            }
        }
    }
}

这段代码里面,this锁中又object锁,object中又this锁,就会导致死锁,不信?我们运行一下

你会看到他会停止不动了,这就是死锁,而在我们开发中,我们应该尽量避免死锁的发生。

四.线程中的通讯

线程中的通讯,是比较重要的,我们看一下这张例图

存什么,取什么

线程中通讯,其实就是多个线程在操作同一个资源,但是操作的动作不同。我们来具体看看例子

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {
        /**
         * 线程间通讯
         */
    }

}

// 资源
class Res {
    String name;
    String sex;
}

// 输入
class Input implements Runnable {

    @Override
    public void run() {

    }

}

// 输出
class Output implements Runnable {

    @Override
    public void run() {

    }

}

我们定义这些个类,对吧,一个资源,两个操作,紧接着,我们应该怎么去操作他?

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {
        /**
         * 线程间通讯
         */

        Res s = new Res();
        Input in = new Input(s);
        Output out = new Output(s);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();

    }

}

// 资源
class Res {
    String name;
    String sex;
}

// 输入
class Input implements Runnable {

    private Res s;

    public Input(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        int x = 0;

        while (true) {

            if (x == 0) {
                s.name = "lgl";
                s.sex = "男";
            } else if (x == 1) {
                s.name = "zhangsan";
                s.sex = "女";
            }
            // 交替
            x = (x + 1) % 2;
        }
    }

}

// 输出
class Output implements Runnable {

    private Res s;

    public Output(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        while (true) {
            System.out.println(s.name + "..." + s.sex);
        }
    }

}

这样去操作,你看下输出,这里出现了一个有意思的现象

你回发现他输出的竟然有女,这就是存在了安全隐患,但是也进一步的证实了,线程间的通讯

五.线程通讯带来的安全隐患

我们线程通讯,会有安全隐患,那已经怎么去解决呢?我们是不是一来就想到了同步synchronized?其实这样做没用的, 因为你传的锁是不一样的,你要想让锁唯一,就类名.class

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {
        /**
         * 线程间通讯
         */

        Res s = new Res();
        Input in = new Input(s);
        Output out = new Output(s);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();

    }

}

// 资源
class Res {
    String name;
    String sex;
}

// 输入
class Input implements Runnable {

    private Res s;

    Object o = new Object();

    public Input(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        int x = 0;

        while (true) {
            synchronized (Input.class) {
                if (x == 0) {
                    s.name = "lgl";
                    s.sex = "男";
                } else if (x == 1) {
                    s.name = "zhangsan";
                    s.sex = "女";
                }
                // 交替
                x = (x + 1) % 2;
            }
        }
    }

}

// 输出
class Output implements Runnable {

    private Res s;

    public Output(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        while (true) {
            synchronized (Input.class) {
                System.out.println(s.name + "..." + s.sex);
            }
        }
    }

}

这样,就解决了问题了

六.多线程等待唤醒机制

我们不需要多线成高速消耗CPU,而是在适当的时候唤醒他,所以我们需要定义一个布尔值

package com.lgl.hellojava;

//公共的   类   类名
public class HelloJJAVA {

    public static void main(String[] args) {
        /**
         * 线程间通讯
         */

        Res s = new Res();
        Input in = new Input(s);
        Output out = new Output(s);

        Thread t1 = new Thread(in);
        Thread t2 = new Thread(out);

        t1.start();
        t2.start();

    }

}

// 资源
class Res {
    String name;
    String sex;
    boolean flag = false;
}

// 输入
class Input implements Runnable {

    private Res s;

    Object o = new Object();

    public Input(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        int x = 0;

        while (true) {
            synchronized (Input.class) {

                if (s.flag) {
                    try {
                        // 等待线程都存放在线程池
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }

                if (x == 0) {
                    s.name = "lgl";
                    s.sex = "男";
                } else if (x == 1) {
                    s.name = "zhangsan";
                    s.sex = "女";
                }
                // 交替
                x = (x + 1) % 2;
                s.flag = true;
                // 通知
                notify();
            }
        }
    }

}

// 输出
class Output implements Runnable {

    private Res s;

    public Output(Res s) {
        this.s = s;
    }

    @Override
    public void run() {

        while (true) {
            synchronized (Input.class) {
                if (!s.flag) {
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                } else {
                    System.out.println(s.name + "..." + s.sex);
                    s.flag = false;
                    notify();
                }

            }
        }
    }

}

都使用在同步中,因为要对待件监视器(锁)的线程操作,所以要使用在线程中,因为只有同步才具有锁

为什么这些操作线程的方法要定义在Object类中呢?因为这些方法在操作同步线程中,都必须要标识它们所操作线程持有的锁,只有同一个锁上的被等待线程可以被同一个锁上notify,不可以对不同锁中的线程进行唤醒,也就是说,等待和唤醒必须是同一把锁!而锁可以是任意对象,所以可以被任意对象调用的方法定义在Object类中。

我们今天介绍就先到这里,线程的概念比较多,我们要写好几篇!!!

如果有兴趣,可以加群:555974449

JAVA之旅(十四)——静态同步函数的锁是class对象,多线程的单例设计模式,死锁,线程中的通讯以及通讯所带来的安全隐患,等待唤醒机制的更多相关文章

  1. 多线程---静态同步函数的锁是class(转载)

    /** 如果同步函数被静态修饰,那么他的锁就是该方法所在类的字节码文件对象 类名.class 静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象. 该对象就是:类名.class   ...

  2. Java 多线程之单例设计模式

    转载:https://segmentfault.com/a/1190000007504892 概念: Java中单例模式是一种常见的设计模式,单例模式的写法有好几种,这里主要介绍两种:懒汉式单例.饿汉 ...

  3. 【设计模式】单例设计模式的N中Java实现方法

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17359719 特点 单例模式的特点: 1.只能有一个实例: 2.必须自己创建自己的一个实例 ...

  4. JAVA之旅(十三)——线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this

    JAVA之旅(十三)--线程的安全性,synchronized关键字,多线程同步代码块,同步函数,同步函数的锁是this 我们继续上个篇幅接着讲线程的知识点 一.线程的安全性 当我们开启四个窗口(线程 ...

  5. JAVA之旅(六)——单例设计模式,继承extends,聚集关系,子父类变量关系,super,覆盖

    JAVA之旅(六)--单例设计模式,继承extends,聚集关系,子父类变量关系,super,覆盖 java也越来越深入了,大家加油吧!咱们一步步来 一.单例设计模式 什么是设计模式? JAVA当中有 ...

  6. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  7. java基础(27):线程安全、线程同步、等待唤醒机制

    1. 多线程 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. 我们通过一个案例,演示线程 ...

  8. java ->多线程_线程同步、死锁、等待唤醒机制

    线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量的值也和预期的是一样的,就是线程安全的. l  我们通过一个案例,演示线 ...

  9. 单例设计模式-java

    在实际项目中单例模式常见应用场景列举如下: 1.servlet编程中,每个servlet就是单例 2.网站计数器,和Application(servlet中涉及) 3.Strucs1框架中,控制器对象 ...

随机推荐

  1. redis分布式锁-SETNX实现

    Redis有一系列的命令,特点是以NX结尾,NX是Not eXists的缩写,如SETNX命令就应该理解为:SET if Not eXists.这系列的命令非常有用,这里讲使用SETNX来实现分布式锁 ...

  2. 浅析JS异步执行机制

    前言 JS异步执行机制具有非常重要的地位,尤其体现在回调函数和事件等方面.本文将针对JS异步执行机制进行一个简单的分析. 从一份代码讲起 下面是两个经典的JS定时执行函数,这两个函数的区别相信对JS有 ...

  3. matlab sparse函数和full函数用法详解(转)

    sparse函数 功能:Create sparse matrix-创建稀疏矩阵 用法1:S=sparse(X)--将矩阵X转化为稀疏矩阵的形式,即矩阵X中任何零元素去除,非零元素及其下标(索引)组成矩 ...

  4. MySQL UPDATE 查询

    MySQL UPDATE 查询 如果我们需要修改或更新MySQL中的数据,我们可以使用 SQL UPDATE 命令来操作.. 语法 以下是 UPDATE 命令修改 MySQL 数据表数据的通用SQL语 ...

  5. ionic3-ng4学习见闻--(多语言方案)

    1.安装ng2-translate npm install ng2-translate --save 2.app.moudle.ts 引入模块,在下方新增方法 import { TranslateMo ...

  6. 如何扩展/删除swap分区

    背景:         由于安装Oracle 的时候,swap太小只划分了4G,后期发现交换分区太小,不满足使用,于是进行了swap分区的扩容过程: swap分区的扩展很简单,但是需要root用户权限 ...

  7. Android的5层平台架构

    Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建.下图所示为 Android 平台的主要组件. Android 软件栈 Linux 内核 Android 平台的基础 ...

  8. J2EE中MVC的各层的设计原则及其编写注意事项

    总结了下J2EE的MVC模式开发原则,很多细节处理好了是很有利于开发与维护的. 下面就从各层说起. 视图层 主要是客户端的显示,主要是JSP和HTML,随着Web的不断发展,许多基于Javascrip ...

  9. Android源码浅析(六)——SecureCRT远程连接Linux,配置端点和字节码

    Android源码浅析(六)--SecureCRT远程连接Linux,配置端点和字节码 需要编译源码的同学,一般都是win+虚拟机吧,但是再虚拟机里体验并不是很好,所有市面上有很多的软件能够做到在wi ...

  10. 用premake5创建lua532工程

    用premake5创建lua532工程 (金庆的专栏) lua-5.3.2只有Makefile,根据readme.html中"Building Lua on other systems&qu ...