zoukankan      html  css  js  c++  java
  • java多线程知识点汇总(二)多线程实例解析

    本实验主要考察多线程对单例模式的操作,和多线程对同一资源的读取,两个知识。实验涉及到三个类:

    1)一个pojo类Student,包括set/get方法。

    2)一个线程类,设置student的成员变量age和name的值为111和111

    3)另一个线程类,设置student的成员变量age和name的值为222和2222

    4)main类,for循环200次,分别创建200个线程1和线程2对同一资源访问。(共400个线程)

    1.第一种情况:饿汉式单例模式保证多线程操控的是同一对象

    //饿汉式单例模式pojo类
    public
    class Student { private String age = "12"; private String name = "Tome"; private static Student student = new Student();//类加载时候创建对象 public String getNameAndAge() { return name+":"+age; } public void setNameAndAge(String name,String age) { this.name = name; this.age = age; } private Student() //构造函数私有化 { } public static Student GetInstace() { //方法区函数,静态函数 return student; } }

    线程2类:

    public class MyThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println(Student.GetInstace().hashCode());
        }
    }

    测试类,创建并启动400个线程:

    public class AppMain implements Runnable{
    
        public static void main(String[] args) {
            AppMain appMain = new AppMain();
            for(int i =0;i<200;i++)
            {
            Thread thread1 = new Thread(appMain);//线程1
            MyThread thread2 = new MyThread();//线程2
            thread1.start();
            thread2.start();
            }
        }    
        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println(Student.GetInstace().hashCode());
        }
    }

    结果:

    2.第二种情况:共享资源的写方法不设置任何同步,多个线程可以交叉写数据

      public String getNameAndAge() {
            return name+":"+age;
        }
         public void setNameAndAge(String name,String age) { //没有设置任何写同步
            this.name = name;
            this.age = age;
        }

    俩线程操控类:

    public class MyThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            Student.GetInstace().setNameAndAge("111", "111");//设置name和age值为1
    System.out.println(Student.GetInstace().getNameAndAge();); } }

    线程2

    public class AppMain implements Runnable{   
    public static void main(String[] args) { AppMain appMain = new AppMain(); for(int i =0;i<200;i++) { Thread thread1 = new Thread(appMain); MyThread thread2 = new MyThread(); thread1.start(); thread2.start(); } } @Override public void run() { // TODO Auto-generated method stub Student.GetInstace().setNameAndAge("222", "2222");//设置name和age为2
    System.out.println(Student.GetInstace().getNameAndAge();); } }

    执行结果:

    3.第三种情况:共享资源的写方法设置同步synchronized,保证同一时刻只有一个线程才能执行写,执行完后才释放锁。

      public String getNameAndAge() {
            return name+":"+age;
        }
        synchronized public void setNameAndAge(String name,String age) { //写方法设置synchronized了
            this.name = name;
            this.age = age;
        }

    测试类添加打印:

    public static void main(String[] args) {
            AppMain appMain = new AppMain();
            for(int i =0;i<200;i++)
            {
            Thread thread1 = new Thread(appMain);
            MyThread thread2 = new MyThread();
            thread1.start();
            thread2.start();
            System.out.println(Student.GetInstace().getNameAndAge());//添加打印,显示name和age值
            }
        }    

    这样就能多个线程按序设置name和set值了。但为什么测试结果依然有脏数据呢?比如111:222这种脏数据呢?

    答案:因为没设置单例对象读get方法的锁,这样读方法可以随时获取值,即使set线程还没执行完,因为没有synchronized限制可以随时访问。

    4.第四种情况,共享资源的读方法不同步不synchronized,方便随时读取不受锁的限制。但就像之前说的,会读到写线程还没执行完时的数据,造成数据混乱。因为读线程可以随时读,没有锁的限制。

      public String getNameAndAge() { //读方法没有做同步synchronized处理,可以随时读取,就可以读出写线程未执行完的中间数据
            return name+":"+age;
        }
        synchronized public void setNameAndAge(String name,String age) {
            this.name = name;
            this.age = age;
        }

    操作结果:

    5.第五种情况,读方法也设置synchronized,锁的对象也是this。保证写的时候不能读,保证读的时候不能写。即读写用同一个锁。

        synchronized public String getNameAndAge() {
            return name+":"+age;
        }
        synchronized public void setNameAndAge(String name,String age) {
            this.name = name;
            this.age = age;
        }

    测试结果:

    这样数据就全部准确了,但是这样效率很低,因为读写共同设置一个锁。读的时候不能写,写的时候不能读。全部都是按序来访问。

    结论:当多线程共同访问同一资源时候,此共享对象的读写方法,要都设置同一个锁,保证写的时候不能读,读的时候不能写,且读写都是按序执行。才能保证数据的准确性。

    同时,也说明了,没有设置锁的方法可以随时执行,随时执行,随时可能被cpu调度以至打断线程的执行,以至读到线程执行一半产生的脏数据。

  • 相关阅读:
    freeswitch对媒体的处理的三种方式
    Windows如何在cmd命令行中查看、修改、删除与添加、设置环境变量
    freeswitch电话代接
    freeswitch三方通话配置
    认识拨号计划
    洛谷P4049 [JSOI2007]合金 题解
    2020-9杂题选做
    LOJ#6497. 「雅礼集训 2018 Day1」图 题解
    LOJ#6496. 「雅礼集训 2018 Day1」仙人掌 题解
    LOJ#6495. 「雅礼集训 2018 Day1」树 题解
  • 原文地址:https://www.cnblogs.com/panxuejun/p/5920450.html
Copyright © 2011-2022 走看看