一、多个线程操作一个对象实例
 当两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全问题”。
 1 package concurrent;
 2 /**
 3  * 测试不同线程操作同一个实例变量线程安全问题
 4  * @author foolishbird_lmy
 5  *
 6  */
 7 class ThePrivateNumber{
 8      private int num = 0;
 9
10      public synchronized void addI(String username){
11         try {
12             if (username.equals("a" )){
13                 num = 100;
14                System. out .println("a set over!");
15                Thread. sleep(2000);
16            } else {
17                 num = 200;
18                System. out .println("b set over!");
19            }
20            System. out .println(Thread.currentThread().getName()+ " num is " +num );
21        } catch (InterruptedException e){
22            e.printStackTrace();
23        }
24     }
25 }
26
27 class ThePrivateNumA extends Thread{
28      private ThePrivateNumber tNumber;
29      public ThePrivateNumA(ThePrivateNumber tNumber){
30         this .tNumber = tNumber;
31     }
32      public void run(){
33         tNumber.addI( "a" );
34     }
35 }
36 class ThePrivateNumB extends Thread{
37      private ThePrivateNumber tNumber;
38      public ThePrivateNumB(ThePrivateNumber tNumber){
39         this .tNumber = tNumber;
40     }
41      public void run(){
42         tNumber.addI( "b" );
43     }
44 }
45 public class ThePrivateNum {
46      public static void main(String[] args) {
47        ThePrivateNumber tNumber = new ThePrivateNumber();
48        ThePrivateNumA tNumA = new ThePrivateNumA(tNumber);
49        tNumA.setName( "A" );
50        tNumA.start();
51        ThePrivateNumB tNumB = new ThePrivateNumB(tNumber);
52        tNumB.setName( "B" );
53        tNumB.start();
54     }
55 }

  

上面演示的就是两个线程操作同一个对象实例,如果没有加synchronized关键字执行同步,则会出现线程安全问题,也就是产生脏读数据,即读取到的数据是已经被修改过的数据;
分析:当线程A进入获取到CPU执行权,匹配到“a”,则num=100,之后该线程被休眠2秒,此时B线程获得CPU执行权,开始执行到else代码,执行num=200,之后退出,A线程经过短暂休眠后自动苏醒继续执行,但是此时num已经被更改为了200,所以最后输出num都是200,这就是线程非安全的;加了同步锁之后,线程B必须等待线程A执行完之后才能进入,所以不会产生数据被修改问题。
二、一个对象实例中有同步方法与非同步方法
 1 package concurrent;
 2
 3 class MySynchroized{
 4      public synchronized void methodA(){
 5         try {
 6            System. out .println("the methodA is start:"+Thread. currentThread().getName());
 7            Thread. sleep(5000);
 8            System. out .println("A end time "+System.currentTimeMillis ());
 9        } catch (InterruptedException e){
10            e.printStackTrace();
11        }
12     }
13      //synchronized,分别测试加同步锁与不加执行顺序
14      public void methodB(){
15         try {
16            System. out .println("the methodB is start:"+Thread. currentThread().getName());
17            System. out .println("B begin time "+System.currentTimeMillis ());
18            Thread. sleep(5000);
19        } catch (InterruptedException e){
20            e.printStackTrace();
21        }
22     }
23 }
24 class TestA extends Thread{
25      private MySynchroized ms;
26      public TestA(MySynchroized ms){
27         this .ms = ms;
28     }
29      public void run(){
30         ms.methodA(); //调用同步方法
31     }
32 }
33 class TestB extends Thread{
34      private MySynchroized ms;
35      public TestB(MySynchroized ms){
36         this .ms = ms;
37     }
38      public void run(){
39         ms.methodB(); //调用非同步方法
40     }
41 }
42 public class TestSynchroized {
43      public static void main(String[] args) {
44        MySynchroized ms = new MySynchroized();
45        TestA a = new TestA(ms);
46        a.setName( "A" );
47        TestB b = new TestB(ms);
48        b.setName( "B" );
49        a.start();
50        b.start();
51     }
52 }

  

分析:在共享的对象实例类中有两个方法,我们分别设置为同步与非同步,
(1)左边是B方法非同步测试效果,我们可以看到,当执行A线程时,A拿到该对象的实例锁,但是并没有影响线程B执行非同步的方法,说明A、B线程几乎是同时进行,B线程并没有因为A拿到锁而发生等待现象,属于异步执行;
(2)右边是将B方法也执行同步,可以看到A、B线程同步执行即顺序执行,当A先进入拿到对象锁,B此时就会是同步等待状态,只有当A执行休眠完成,释放锁之后B才有机会执行,属于同步执行操作。
结论:对于执行相同对象的不同线程,执行对象类中的同步方法时,不管有多少个同步的方法,都是同一个对象锁,必须等一个线程执行完毕释放锁之后另一个线程才能获取执行;但是非同步方法可以任意时刻调用,不收锁限制。
三、关于脏读问题
 1 package concurrent;
 2 class DirtyRead {
 3      private String name = "a" ;
 4      private String id = "aa" ;
 5      public synchronized void set(String name, String id) {
 6         try {
 7             this .name = name;
 8            Thread. sleep(2000);
 9             this .id = id;
10            System. out .println("set method " + Thread.currentThread().getName()
11                   + " name:" + name + " id:" + id);
12        } catch (InterruptedException e) {
13            e.printStackTrace();
14        }
15     }
16      public void get() {
17        System. out .println("get method " + Thread.currentThread().getName()
18                + " name:" + name + " id:" + id );
19     }
20 }
21 class DirtyReadRun extends Thread {
22      private DirtyRead dr;
23      public DirtyReadRun(DirtyRead dr) {
24         this .dr = dr;
25     }
26      public void run() {
27         dr.set( "b" , "bb" );
28     }
29 }
30 public class TestDirtyRead {
31      public static void main(String[] args) {
32         try {
33            DirtyRead dr = new DirtyRead();
34            DirtyReadRun drr = new DirtyReadRun(dr);
35            drr.start();
36 //         Thread.sleep(1000);
37            Thread. sleep(3000);
38            dr.get();
39        } catch (InterruptedException e) {
40            e.printStackTrace();
41        }
42     }
43 }

  

分析:
(1)左边结果是在主线程中休眠1秒,可以发现读取数据发生了错误,原因是我们将set方法设置为同步,所以线程在执行时拿到锁后会安全执行,数据的设置没有问题,但是当我们调用非同步方法get获取值时,注意主线程只休眠了1秒,但是我们在set线程中休眠了2秒,所以此时线程并没有来得及给id赋值操作,就直接输出了aa而不是bb;
(2)右边是主线程休眠3秒的情况,即调用get方法的线程是在线程赋值set休眠2秒之后才调用,此时已经安全给数据赋值了,所以输出结果正确;
当然我们也可以直接给get方法执行同步操作,这有另一个线程就必须等待第一个线程执行完set里面的全部操作释放锁之后才能执行,会发生等待。
四、锁的可重入
  关键字synchronized拥有可重入的功能,即当一个线程得到一个对象锁之后,再次请求此对象锁时是可以再次得到该对象的锁的,自己可以再次获取自己的内部锁。
 1 package concurrent;
 2 /**
 3  * 可重入锁测试
 4  * @author foolishbird_lmy
 5  *
 6  */
 7 class Synch{
 8      public synchronized void sA(){
 9        System. out .println("sA()" );
10        sB();
11     }
12      public synchronized void sB(){
13        System. out .println("sB()" );
14        sC();
15     }
16      public synchronized void sC(){
17        System. out .println("sC()" );
18     }
19 }
20 class SynchARun extends Thread{
21      private Synch sa;
22      public SynchARun(Synch sa){
23         this .sa = sa;
24     }
25      public void run(){
26         sa.sA();
27     }
28 }
29 public class ReSynchronized {
30      public static void main(String[] args) {
31        SynchARun sa = new SynchARun( new Synch());
32        sa.start();
33     }
34 }

  从输出结果可以看出,线程只要获取到了该对象锁,其他的同步锁也一样能获取。

五、静态同步synchronized方法与synchronized(class)代码块

   用static修饰的同步方法中的同步锁是给Class类上锁,而非static方法是给对象上锁;

 1 package concurrent;
 2
 3 class StaticSyn {
 4      public static synchronized void printA() {
 5         try {
 6            System. out .println(Thread.currentThread().getName() + " : "
 7                   + System.currentTimeMillis() + "进入printA()");
 8            Thread. sleep(3000);
 9            System. out .println(Thread.currentThread().getName() + " : "
10                   + System.currentTimeMillis() + "退出printA()");
11        } catch (InterruptedException e) {
12            e.printStackTrace();
13        }
14     }
15      public static synchronized void printB() {
16         try {
17            System. out .println(Thread.currentThread().getName() + " : "
18                   + System.currentTimeMillis() + "进入printB()");
19            Thread. sleep(3000);
20            System. out .println(Thread.currentThread().getName() + " : "
21                   + System.currentTimeMillis() + "退出printB()");
22        } catch (InterruptedException e) {
23            e.printStackTrace();
24        }
25     }
26      public synchronized void printC() {
27         try {
28             System. out .println(Thread.currentThread().getName() + " : "
29                   + System.currentTimeMillis() + "进入printC()");
30            Thread. sleep(3000);
31            System. out .println(Thread.currentThread().getName() + " : "
32                   + System.currentTimeMillis() + "退出printC()");
33        } catch (InterruptedException e) {
34            e.printStackTrace();
35        }
36     }
37 }
38 class StaticSynRunA extends Thread{
39      private StaticSyn ss;
40      public StaticSynRunA(StaticSyn ss){
41         this .ss = ss;
42     }
43      @SuppressWarnings( "static-access" )
44      public void run(){
45         ss. printA();
46     }
47 }
48 class StaticSynRunB extends Thread{
49      private StaticSyn ss;
50      public StaticSynRunB(StaticSyn ss){
51         this .ss = ss;
52     }
53      public void run(){
54         ss .printB ();
55     }
56 }
57 class StaticSynRunC extends Thread{
58      private StaticSyn ss;
59      public StaticSynRunC(StaticSyn ss){
60         this .ss = ss;
61     }
62      public void run(){
63         ss.printC();
64     }
65 }
66 public class TestStaticSyn {
67      public static void main(String[] args) {
68        StaticSyn ss = new StaticSyn();
69        StaticSynRunA ssa = new StaticSynRunA(ss);
70        ssa.setName( "A" );
71        ssa.start();
72        StaticSynRunB ssb = new StaticSynRunB(ss);
73        ssb.setName( "B" );
74        ssb.start();
75        StaticSynRunC ssc = new StaticSynRunC(ss);
76        ssc.setName( "C" );
77 //     ssc.start();
78     }
79 }

分析:
(1)左边结果是注释掉C,即A、B线程都是调用静态的同步方法,所以都是同步顺序执行,他们的锁都是Class锁,是同一种锁,所以B线程必须等待A线程释放锁;
(2)右边的结果是A、B、C线程同时运行,由于C线程调用的是非静态的同步方法,非静态的同步方法是对象锁,与其他两个线程的锁不一样,所以是异步的,但是A与B还是同步执行。

转载于:https://www.cnblogs.com/lmy-foolishbird/p/5471853.html

对象及变量的并发访问一相关推荐

  1. 两个线程同时访问一个变量_百战程序员:Java多线程对象及变量的并发访问

    在开发多线程程序时,如果每个多线程处理的事情都不一样,每个线程都互不相关,这样开发的过程就非常轻松.但是很多时候,多线程程序是需要同时访问同一个对象,或者变量的.这样,一个对象同时被多个线程访问,会出 ...

  2. java 并发变量_二、Java多线程编程 (对象及变量的并发访问)

    非线程安全 多个线程对同一个对象中的实例变量进行并发操作时会出现值被更改.值不同步的情况,进而影响程序的执行流程. 线程安全 线程安全就是获得实例变量的值是经过同步处理的.不会出现被更改不同步的情况. ...

  3. 对象及变量的并发访问(案例加总结)--学习笔记

    文章目录 前言 一.synchronized的使用? 1.0.1 实际变量非线程安全及解决 1.0.2 当类中存在同步方法与非同步方法时(synchronized锁的是对象,而非方法) 1.0.3 s ...

  4. Java-对象及变量的并发访问小结

    1)多线程环境下,方法内的变量是线程安全的 2)多个线程同时处理一个实例,这个实例内的变量是不安全的 3)不同线程中注入同一个类的不同实例,实例中的变量是安全的 4)Synchronized获取到的锁 ...

  5. 【译文】 C#面向对象的基本概念 (Basic C# OOP Concept) 第一部分(类,对象,变量,方法,访问修饰符)...

    译文出处:http://www.codeproject.com/Articles/838365/Basic-Csharp-OOP-Concept 相关文档:http://files.cnblogs.c ...

  6. java中sofa并发访问,云上的日子:用块存储、文件存储还是对象存储?

    当今最流行的存储类型是哪种? 答案毫无疑问是:块存储.文件存储还有对象存储!作为伴随着云计算兴起的存储类型,这三种存储绝对是这个时代的存储明星,用C位出道来形容再合适不过. 那么,在云上的日子,究竟该 ...

  7. [转]高并发访问下避免对象缓存失效引发Dogpile效应

    避免Redis/Memcached缓存失效引发Dogpile效应 Redis/Memcached高并发访问下的缓存失效时可能产生Dogpile效应(Cache Stampede效应). 推荐阅读:高并 ...

  8. Spring并发访问的线程安全性问题

    下面的记录对spring中并发的总结.理论分析参考Spring中Singleton模式的线程安全,建议先看 spring中的并发访问题: 我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下 ...

  9. Spring 是如何解决并发访问的线程安全性问题的

    springmvc的controller是singleton的(非线程安全的),这也许就是他和struts2的区别吧!和Struts一样,Spring的Controller默认是Singleton的, ...

最新文章

  1. MySQL两主多从,且故障转移配置
  2. JSP GridView --使用自定义标签实现ASP.NET的控件
  3. 两年前觉得这很难那很难的人,现在觉得更难了
  4. TCP/IP数据包结构分解
  5. Python脚本实现图片加水印
  6. Eclipse调试Java程序技巧
  7. PostgreSQL Json字段作为查询条件的解决方案
  8. 深度linux系统老版本,Deepin Linux15.7下载
  9. 查看华为huawei状态码
  10. 系统运维数据存储知识-系统数据误删除恢复
  11. SAP中一次性客户及供应商的应用浅晰
  12. 内燃机 vs 外燃机
  13. springboot整合阿里云短信验证(无需签名和模板,通过阿里云api测试实现短信验证)
  14. 小米是最能赚外国人钱的国产手机品牌,其他手机品牌只能窝里横
  15. 如何知晓计算机的字长,计算机字长是什么意思,如何判断指令格式是单字长还是双字长...
  16. 【程序设计】Java基本语法练习题
  17. opencv在大图片上贴小图
  18. 【转】计算方法太牛了,留着以后教孩子
  19. swagger2配置
  20. C++新手入门学习教程

热门文章

  1. ResultSet用法集锦 (转)
  2. 在批处理中运行.sql文件
  3. 管理文库]我喜欢的10个经典管理学定律点评
  4. SpringBoot配置属性之DataSource
  5. Python_logging模块
  6. 一个有关ajax去获取天气预报然后用echarts展现出来的小demo
  7. mysql重复数据查询
  8. DHTMLX-Tabbar
  9. 20161023 NOIP 模拟赛 T1 解题报告
  10. 清空SQL Server数据库中所有表数据的方法(转)