Java synchronized关键字

来源:互联网 发布:淘宝分销是什么意思 编辑:程序博客网 时间:2024/06/06 00:33
文章目录  一、“脏数据”的产生     1、银行存钱的例子     2、原因      当多条语句在操作线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来。导致共享数据的错误。     3、解决方案     对多条操作共享数据的语句,只能让一个线程执行完。再执行过程中,其他线程不能够参与执行。 二、同步方案    1、同步代码块    2、同步方法    3、锁    在使用同步代码块方法时,    非静态方法可以使用 this 或者 Class 对象(字节码文件对象)    静态方法必须使用Class对象(字节码文件对象)来同步三、使用snchroized关键字注意的点   1、snchroized 关键字不能被继承   2、在接口定义方法时不能使用snchroized 关键字   3、构造方法不能使用 snchroized  关键字,但是可以使用同步代码块来进行同步   4、可以自由放置 snchroized      5、大量使用 snchroized  会造成 不必要的资源消耗和性能损失四、如何排查线程安全问题1、明确哪些代码是多线程运行的代码2、明确共享数据3、明确多线程运行代码哪些语句是操作共享数据五、利用同步 解决线程安全问题的 前提1、必须要>=2 个线程2、必须是多个线程使用同一个锁六、利弊1、优点: 解决了多线程安全问题2、当线程获取到执行权的时候,每次都判断锁,消耗了资源(这个资源是在允许范围内的)扩展 : 如何保证单例

一、脏数据的产生

1、银行存钱的例子

两个储户分别同时往银行存300元,每次存100,分三次存。

代码如下 :

//银行public class Bank {    private int sum = 0;    public void add(int n )    {        sum += n;        System.out.println(sum);    }}//用户 public class User implements Runnable{    private Bank bank = new Bank();    @Override    public void run()    {        for(int x= 0 ; x < 3;x++)        {            bank.add(100);        }    }}// 在main 函数中     public static void main(String[] args)    {         //线程对象a         User a =  new User();         Thread t1 = new Thread(a);         //线程对象b         User b = new User();         Thread t2 = new Thread(b);         t1.start();         t2.start();    }
// 可能的输出结果200200400400600600

2、原因

两个线程同时访问共享数据 sum 。当第一条线 t1 程执行到 sum,并对sum进行数据操作时候,t1线程还未执行输出语句时候,失去执行权。t2线程对象获取执行权去执行sum数据操操作。同样的,还未执行到输出语句时候,t2 失去执行权。此刻t1 获取到执行权,执行输出语句。执行完成后,t1失去执行权,t2 获取到执行权,也同样执行输出语句。以此类推…

总结为:当多条语句在操作线程共享数据时,一个线程对多条语句只执行了一部分,还没执行完,另一个线程参与进来。导致共享数据的错误。

如何模拟出现上述结果,其实也很简单,sleep()下即可。修改如下

public class Bank {    private int sum = 0;    public void add(int n )    {        sum += n;        try        {            Thread.sleep(10);        }catch (InterruptedException e)        {        }        System.out.println(sum);    }}

注意: 上述的 add方法是可以throws 异常,在run方法里面进行try-catch。 但是,run 方法不能throws 异常的。

3、解决方案

对多条操作共享数据的语句,只能让一个线程执行完。再执行过程中,其他线程不能够参与执行。

Java 对于多线程的安全问题提供了专业的解决方式。

二、同步方案

1、同步代码块

synchronized (对象) {    //需要被同步的代码}   //注意: 对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取到执行权,也进不去。

对add方法修改如下:

public class Bank {    private int sum = 0;    Object obj = new Object();    public void add(int n )    {        // 同步代码块        synchronized (obj)         {            sum += n;            try            {                Thread.sleep(10);            }catch (InterruptedException e)            {            }            System.out.println(sum);        }    }}

2、同步方法

在函数方法名前添加 synchronized 关键字 。

那么同步函数是哪一个锁呢 ? 函数需要被对象调用,那么函数都持有对象引用,就是this.所以同步函数使用的锁是 this。

如果同步函数被静态修饰后,使用的锁是什么 ? 该方法所在类的字节码文件对象,即 Class 对象。如何获取 ? 类名.class 或者 实例对象.getClass()

注意 : 非静态函数 同步的锁 可以是当前对象,也可以是非静态函数所在类的字节码文件对象。

修改如下:

public class Bank {    private int sum = 0;    //Object obj = new Object();    public synchronized void add(int n )    {        //synchronized (obj)         //{            sum += n;            try            {                Thread.sleep(10);            }catch (InterruptedException e)            {            }            System.out.println(sum);        }    //}}

三、使用snchroized关键字注意的点

1、snchroized 关键字不能被继承

如果在父类某个方法使用 snchroized 关键字,而自类覆盖了,子类默认情况是不同步的。如果子类方法也需要通保护,有两个方法,方法名前添加 snchroized 或者 在方法体内 调用父类方法。

2、在接口定义方法时不能使用snchroized 关键字

3、构造方法不能使用 snchroized 关键字,但是可以使用同步代码块来进行同步

4、可以自由放置 snchroized

snchroized 不能放在 方法返回值类型后面 !!!

5、大量使用 snchroized 会造成 不必要的资源消耗和性能损失

四、如何查找线程安全问题

1、明确哪些代码是多线程运行的代码  即 run方法2.、明确共享数据 sum3、明确多线程运行代码哪些语句是操作共享数据  sim += n 和 输出语句

五、利用同步解决线程安全问题的的前提

1、必须要>=2 个线程2、必须是多个线程使用同一个锁

六、同步的利弊如下

优点: 解决了多线程安全问题弊端:当线程获取到执行权的时候,每次都判断锁,消耗了资源(这个资源是在允许范围内的)

多线程在单例中的运用

原创粉丝点击