对象及变量的并发访问一

一、多个线程操作一个对象实例

当两个线程同时访问一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能会出现“非线程安全问题”。

 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还是同步执行。

时间: 2024-12-15 01:43:53

对象及变量的并发访问一的相关文章

java多线程(对象和变量的并发访问)

在现实开发中,我们写的线程肯定会有不同的实例在执行,此时就可能会出现"非线程安全问题",非线程安全就是:多个线程对同一个对象中的实例变量进行并发访问时候,有可能A和B线程同时读取到数据,先后进行更改,此时,该变量就不是我们期望的数据,也就是通常所说的"脏数据" 实例变量非线程安全 需要注意的是,方法中的变量是不存在非线程安全问题的,这是因为方法内部的变量都是私有的. 如果多个线程共同访问了一个对象中的实例变量,则可能会出现线程安全问题.看下面代码: public c

Java多线程编程核心 - 对象及变量的并发访问

1.什么是“线程安全”与“非线程安全”? “非线程安全”会在多个线程对同一对象总的实例变量进行并发访问时发生,产生的后果是“脏读”,也就是取到的数据其实是被更改过的. “线程安全”是以获得的实例变量的值是经过同步处理的,不会出现脏读的现象. 2.非线程安全例子?怎么解决? 非线程安全 package com.jvm.thread; public class HasSelfPrivateNum { private int num =  0; public void add(String usern

(二)对象以及变量的并发访问--synchronized的使用细节,用法

具体的记录synchronized关键的各种使用方式,注意事项.感觉一步一步跟我来都可以看懂滴 大致是按照以下思路进行书写的.黑体字可以理解为结论, 1.synchronized锁的是什么? 2.synchronized能够锁住所有方法吗? 3.synchronized能够用来锁住一个方法之中的部分代码吗? 4.synchronized能够锁住除了this以外的其他对象吗?有什么用?有什么需要注意的? -----------------------------------------------

第二章:对象及变量的并发访问

为什么要使用多线程编程?什么时候会出现线程安全问题? 在单线程中不会出现线程安全问题,而在多线程编程中,有可能会出现同时访问同一个资源的情况,这种资源可以是各种类型的的资源:一个变量.一个对象.一个文件.一个数据库表等,而当多个线程同时访问同一个资源的时候,就会存在一个问题: 由于每个线程执行的过程是不可控的,所以很可能导致最终的结果与实际上的愿望相违背或者直接导致程序出错. 举个简单的例子: 现在有两个线程分别从网络上读取数据,然后插入一张数据库表中,要求不能插入重复的数据. 那么必然在插入数

第二章 对象以及变量的并发访问

synchronied 对象监视器为Object时的使用,或者监视器为Class时的使用. 方法中的变量不存在非线程安全问题,永远都是线程安全的,这是方法内部的变量是私有的特性造成的. 1 synchronized的使用 在方法前加关键字synchronized即可. 1)A线程先持有object对象的Lock锁,B线程可以异步的方式调用object对象中的非synchrionized类型的方法. 2)A线程先持有object对象的Lock锁,B线程如果在这时调用了object对象中的synch

多线程--对象及变量的并发访问

1 . 多个线程访问多个对象JVM会创建多个锁.2 . 静态方法是以类为单位进行同步的--对于同一个类中的所有静态方法,在同一时间内,只允许有一个线程执行其中的一个静态方法,其余想要进入这些方法的线程都必须挂起等待.非静态方法是以对象为单位进行同步的.3 .假设现有两个线程A和B,一个object对象,当线程A调用object对象的一个同步方法M1时,线程A就获得了M1方法所在对象的锁,所以其他线程必须等待线程A执行完毕之后才能调用方法M1,如果线程B调用object的同步方法M2,必须等待线程

Java多线程编程(二)对象及变量的并发访问

一.synchronized同步方法 1.方法内的变量为线程安全 2.实例变量非线程安全 3.多个对象多个锁 4.synchronized方法与锁对象 5.脏读 6.synchronized锁冲入 7.出现异常,锁自动释放 8.同步不具有继承性 二.synchronized同步语句块 1.synchronized方法的弊端 2.synchronized同步代码块的使用 3.用同步代码块解决同步的弊端 4.一半异步,一半同步 5.synchronized代码块间的同步性 6.验证同步synchro

第二章:对象及变量的并发序言

本章主要介绍了java多线程中的同步,也就是如何在java语言中写出线程安全的程序. 如何在java语言中解决非线程安全的相关问题. 设计知识点: synchronized对象监视器为Object时的使用. synchronized对象监视器为Class时的使用. 非线程安全是如何出现的. 关键字volatile的主要作用. 关键字volatile与synchronized的区别及使用情况.

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

避免Redis/Memcached缓存失效引发Dogpile效应 Redis/Memcached高并发访问下的缓存失效时可能产生Dogpile效应(Cache Stampede效应). 推荐阅读:高并发下的 Nginx 优化方案 http://www.linuxidc.com/Linux/2013-01/78791.htm 避免Memcached缓存的Dogpile效应 Memcached的read-through cache流程:客户端读取缓存,没有的话就由客户端生成缓存.Memcached缓