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

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.第一种情况:饿汉式单例模式保证多线程操控的是同一对象

  1. //饿汉式单例模式pojo类
    public class Student {
  2. private String age = "";
  3. private String name = "Tome";
  4. private static Student student = new Student();//类加载时候创建对象
  5.  
  6. public String getNameAndAge() {
  7. return name+":"+age;
  8. }
  9. public void setNameAndAge(String name,String age) {
  10. this.name = name;
  11. this.age = age;
  12. }
  13.  
  14. private Student() //构造函数私有化
  15. {
  16. }
  17. public static Student GetInstace() { //方法区函数,静态函数
  18. return student;
  19. }
  20. }

线程2类:

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

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

  1. public class AppMain implements Runnable{
  2.  
  3. public static void main(String[] args) {
  4. AppMain appMain = new AppMain();
  5. for(int i =;i<;i++)
  6. {
  7. Thread thread1 = new Thread(appMain);//线程1
  8. MyThread thread2 = new MyThread();//线程2
  9. thread1.start();
  10. thread2.start();
  11. }
  12. }
  13. @Override
  14. public void run() {
  15. // TODO Auto-generated method stub
  16. System.out.println(Student.GetInstace().hashCode());
  17. }
  18. }

结果:

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

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

俩线程操控类:

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

线程2

  1. public class AppMain implements Runnable{
    public static void main(String[] args) {
  2. AppMain appMain = new AppMain();
  3. for(int i =;i<;i++)
  4. {
  5. Thread thread1 = new Thread(appMain);
  6. MyThread thread2 = new MyThread();
  7. thread1.start();
  8. thread2.start();
  9. }
  10. }
  11. @Override
  12. public void run() {
  13. // TODO Auto-generated method stub
  14. Student.GetInstace().setNameAndAge("", "");//设置name和age为2
    System.out.println(Student.GetInstace().getNameAndAge(););
  15. }
  16. }

执行结果:

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

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

测试类添加打印:

  1. public static void main(String[] args) {
  2. AppMain appMain = new AppMain();
  3. for(int i =;i<;i++)
  4. {
  5. Thread thread1 = new Thread(appMain);
  6. MyThread thread2 = new MyThread();
  7. thread1.start();
  8. thread2.start();
  9. System.out.println(Student.GetInstace().getNameAndAge());//添加打印,显示name和age值
  10. }
  11. }

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

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

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

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

操作结果:

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

  1. synchronized public String getNameAndAge() {
  2. return name+":"+age;
  3. }
  4. synchronized public void setNameAndAge(String name,String age) {
  5. this.name = name;
  6. this.age = age;
  7. }

测试结果:

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

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

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

java多线程知识点汇总(二)多线程实例解析的更多相关文章

  1. Java面试知识点汇总

    Java面试知识点汇总 置顶 2019年05月07日 15:36:18 温柔的谢世杰 阅读数 21623 文章标签: 面经java 更多 分类专栏: java 面试 Java面试知识汇总   版权声明 ...

  2. java基础知识点补充---二维数组

    #java基础知识点补充---二维数组 首先定义一个二维数组 int[][] ns={ {1,2,3,4}, {5,6,7,8}, {9,10,11,12}, {13,14,15,16} }; 实现遍 ...

  3. 2020最常见的200+Java面试题汇总(含答案解析)

    前言 2020年快要结束了,很多朋友问题,有没有整理今年的一些面试题,最近抽时间整理了一份Java面试题.或许这份面试题还不足以囊括所有 Java 问题,但有了它,我相信足以应对目前市面上绝大部分的 ...

  4. Java基础知识点(二)

    前言:Java的基础知识点不能间断. 1.Array和ArrayList的区别 关于Array的用法,参看:http://blog.csdn.net/b_11111/article/details/5 ...

  5. java多线程知识点汇总(一)多线程基础

    1.什么叫多线程程序? 答:一个进程至少有一个线程在运行,当一个进程中出现多个线程时,就称这个应用程序是多线程应用程序. java编写的程序都是多线程的,因为最少有俩线程,main主线程和gc线程. ...

  6. java多线程知识点汇总(四)多线程知识点脉络图

    1.多线程安全问题 1)synchronized关键字:如何加锁的问题,选择synchronized方法还是synchnized代码块. 选择哪个锁问题,this对象,还是class对象(针对stat ...

  7. Windows 多线程知识点汇总

    一.什么叫原子性? 答:一个操作不会被分成两个时间片来执行,不会刚执行到一半,由于时间片到了,CPU就跑去执行其他线程了.在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的操作是 ...

  8. Java 面试知识点汇总

    OOP:(Object Oriented Programming )面向对象编程 重用性.灵活性和扩展性 高内聚.低耦合 面向过程编程与面向对象编程的区别:举例,自己做饭吃与去饭馆吃,去饭馆只需要知道 ...

  9. java小知识点汇总

    1.ConcurrentHashMap使用segment来分段和管理锁,segment继承自ReentrantLock,因此ConcurrentHashMap使用ReentrantLock来保证线程安 ...

随机推荐

  1. swiper (Table切换和动态加载时候出现的问题)

    本文为让心灵-去旅行原创,转载请说明.. 我们在写一个简单的swiper图片轮播的时候很简单,是写死的也就那么几张图片轮播.如果这时候图片和一些东西是后台的,你从js里动态添加到DOM时,这时候你就会 ...

  2. csu 1749: Soldiers ' Training(贪心)

    1749: Soldiers ' Training Time Limit: 1 Sec  Memory Limit: 512 MBSubmit: 37  Solved: 18[Submit][Stat ...

  3. csu 1598(KMP)

    1598: 最长公共前缀 Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 109  Solved: 92[Submit][Status][Web Boar ...

  4. 初涉yield

    function* a(i) { console.log('here we go'); yield i; // 必须有*,不然b会作为返回值,而不是执行 yield* b(i); yield i+10 ...

  5. Hadoop(七)YARN的资源调度

    一.YARN 概述 YARN 是一个资源调度平台,负责为运算程序提供服务器运算资源,相当于一个分布式的操 作系统平台,而 MapReduce 等运算程序则相当于运行于操作系统之上的应用程序 YARN ...

  6. 133个Java面试问题列表

    转载: 133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...

  7. Socket编程(一):建立与客户端的连接并接受数据

    我们这里利用Socket在模拟一个客户端与服务器通信,其实客户端与服务端通信就像人与人打电话一样,想要给一个人打电话,我们首先必须要有手机,必须知道对方的手机号码,这里Socket就好比一部手机,而短 ...

  8. DDL DML DCL DQL的区别

    原文章出处:http://blog.csdn.net/tomatofly/article/details/5949070 SQL(Structure Query Language)语言是数据库的核心语 ...

  9. DP 题集 2

    关于 DP 的一些题目 String painter 先区间 DP,\(dp[l][r]\) 表示把一个空串涂成 \(t[l,r]\) 这个子串的最小花费.再考虑 \(s\) 字符串,\(f[i]\) ...

  10. 矩阵乘法<简单总结>

    原理:矩阵相乘最重要的方法是一般矩阵乘积.它只有在第一个矩阵的 行数 和第二个矩阵的 列数 相同时才可进行.若A为m×n矩阵,B为n×p矩阵,则他们的乘积AB会是一个m×p矩阵. 若A=    a   ...