Java---14---多线程---synchronized 同步函数

来源:互联网 发布:java. se有哪些内容 编辑:程序博客网 时间:2024/05/17 17:39

设计这样一个程序:两个用户去银行存钱,每次存100,存3次,显示银行中总的钱数。


class Bank{    private int sum;//sum代表银行中钱的总量    public void add (int n)    {        sum += n;        System.out.println("sum = "+sum);    }}class Cus implements Runnable{    private Bank b = new Bank();    @Override    public void run()    {        // TODO Auto-generated method stub        for (int i = 0; i < 3; i++)        {            b.add(100);        }    }}public class TONGBUHANSHU{    public  static void  main(String[] args)    {        Cus c = new Cus();        Thread t1 = new Thread(c);        Thread t2 = new Thread(c);        t1.start();        t2.start();    }}



那么该程序有没有安全问题呢?多运行几次发现

输出的结果可能是:


 sum = 100

 sum = 300

 sum = 400

 sum = 200

 sum = 500

 sum = 600

说明该程序存在安全问题。



如何找到问题?

1.明确哪些代码是多线程运行代码

run方法和add方法

2.明确共享数据

b 和 sum

3.明确多线程运行代码中哪些语句是操作共享数据的


class Bank{    private int sum;    Object obj = new Object();    public  void add (int n)    {        synchronized(obj)        {            try            {                Thread.sleep(100);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            sum += n;            System.out.println(" sum = "+sum);        }    }}class Cus implements Runnable{    private Bank b = new Bank();    @Override    public void run()    {        // TODO Auto-generated method stub        for (int i = 0; i < 3; i++)        {            //System.out.print(Thread.currentThread().getName());            b.add(100);        }    }}public class TONGBUHANSHU{    public  static void  main(String[] args)    {        Cus c = new Cus();        Thread t1 = new Thread(c);        Thread t2 = new Thread(c);        t1.start();        t2.start();    }}

经过同步代码块之后再运行就不会在出现上面的情况了。


同步代码块的作用就是封装代码,而函数的作用也是封装代码,那么函数封装代码和同步代码块封装代码有什么不同?

唯一的区别就是:同步代码块封装的代码带有同步的特性。

那如果让函数具备了同步的特性?

将 synchronized 关键字作为修饰符放到函数上

这就是传说中的同步函数:

class Bank{    private int sum;    Object obj = new Object();    public synchronized void add (int n)    {        //synchronized(obj)        // {        try        {            Thread.sleep(100);        }        catch (Exception e)        {            // TODO: handle exception            System.out.println(e.toString());        }        sum += n;        System.out.println(" sum = "+sum);        // }    }}class Cus implements Runnable{    private Bank b = new Bank();    @Override    public void run()    {        // TODO Auto-generated method stub        for (int i = 0; i < 3; i++)        {            //System.out.print(Thread.currentThread().getName());            b.add(100);        }    }}public class TONGBUHANSHU{    public  static void  main(String[] args)    {        Cus c = new Cus();        Thread t1 = new Thread(c);        Thread t2 = new Thread(c);        t1.start();        t2.start();    }}


将之前卖票的例子也搞成同步函数的形式。


class Test implements Runnable{    private int num = 50;    Object obj = new Object();    public synchronized void run ()    {        while (true)        {            // synchronized (obj)            //{            if (num >= 0)            {                try                {                    Thread.sleep(20);                }                catch (Exception e)                {                    // TODO: handle exception                    System.out.println(e.toString());                }                System.out.println(Thread.currentThread().getName()+">>"+num--);            }            //}        }    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        Thread c = new Thread(t);        Thread d = new Thread(t);        a.start();        b.start();        c.start();        d.start();    }}


然而输出结果显示全部都是0线程运行的,其他线程没有运行。

为什么?没有弄清楚哪些代码是需要同步的。

需要同步的代码是同步代码块之中的代码,而现在直接将synchronized 关键字作为修饰符放到run方法上,显然与之前用同步代码块的方式时同步的代码是不同的。

 

解决:

将同步代码块之中的代码封装成函数。


class Test implements Runnable{    private int num = 50;    // Object obj = new Object();    public  void run ()    {        while (true)        {            show ();        }    }    public synchronized void show ()    {        // synchronized (obj)        //{        if (num >= 0)        {            try            {                Thread.sleep(20);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            System.out.println(Thread.currentThread().getName()+">>"+num--);        }        //}    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        Thread c = new Thread(t);        Thread d = new Thread(t);        a.start();        b.start();        c.start();        d.start();    }}


同步函数的锁是哪个?

函数需要被对象调用,函数都有一个所属对象的引用

所以同步函数使用的锁是this。

 

验证:

使用两个线程来卖票,一个线程在同步代码块中

一个线程在同步函数中,都在执行卖票动作

如果同步的话是不会出现错误


class Test implements Runnable{    private int num = 50;    Object obj = new Object();    boolean flag = true;    public  void run ()    {        if (flag)        {            while (true)            {                synchronized (obj)                {                    if (num >= 0)                    {                        try                        {                            Thread.sleep(20);                        }                        catch (Exception e)                        {                            // TODO: handle exception                            System.out.println(e.toString());                        }                        System.out.println(Thread.currentThread().getName()+">>--code-- "+num--);                    }                }            }        }        else        {            while (true)            {                show();            }        }    }    public synchronized void show ()//  锁是 ?    {        // synchronized (obj)        //{        if (num >= 0)        {            try            {                Thread.sleep(20);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            System.out.println(Thread.currentThread().getName()+">>  show  "+num--);        }        //}    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        a.start();        try        {            Thread.sleep(20);        }        catch (Exception e)        {            // TODO: handle exception        }        t.flag = false;        b.start();    }}


两个线程,a线程运行的是同步代码块中的内容,b线程运行的同步函数中的内容。

 

输出结果显示有-1号票

说明这个程序时不安全的。

这个程序中已经使用了同步,但是却还是有错误?

考虑使用同步的前提条件:

1.必须有两个或两个以上的线程--(满足)

2.必须使用同一个锁--(??)

 

同步代码块中的锁是obj,而同步函数中的锁肯定不是obj,我们假设同步函数的锁是this,将同步代码块中的锁也换成this试试。



class Test implements Runnable{    private int num = 50;    Object obj = new Object();    boolean flag = true;    public  void run ()    {        if (flag)        {            while (true)            {                synchronized (this)                {                    if (num >= 0)                    {                        try                        {                            Thread.sleep(20);                        }                        catch (Exception e)                        {                            // TODO: handle exception                            System.out.println(e.toString());                        }                        System.out.println(Thread.currentThread().getName()+">>--code-- "+num--);                    }                }            }        }        else        {            while (true)            {                show();            }        }    }    public synchronized void show ()// 锁是 this    {        // synchronized (obj)        //{        if (num >= 0)        {            try            {                Thread.sleep(20);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            System.out.println(Thread.currentThread().getName()+">>  show  "+num--);        }        //}    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        a.start();        try        {            Thread.sleep(20);        }        catch (Exception e)        {            // TODO: handle exception        }        t.flag = false;        b.start();    }}

发现输出结果中没有-1号票了,说明该程序安全,也就说明了同步函数的锁是this



静态同步函数的锁是什么?

将show方法声明为静态方法,为了编译通过同时将num声明为静态


class Test implements Runnable{    private static int num = 50;    Object obj = new Object();    boolean flag = true;    public  void run ()    {        if (flag)        {            while (true)            {                synchronized (this)                {                    if (num >= 0)                    {                        try                        {                            Thread.sleep(20);                        }                        catch (Exception e)                        {                            // TODO: handle exception                            System.out.println(e.toString());                        }                        System.out.println(Thread.currentThread().getName()+">>--code-- "+num--);                    }                }            }        }        else        {            while (true)            {                show();            }        }    }    public static synchronized void show ()//      {        // synchronized (obj)        //{        if (num >= 0)        {            try            {                Thread.sleep(20);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            System.out.println(Thread.currentThread().getName()+">>  show  "+num--);        }        //}    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        a.start();        try        {            Thread.sleep(20);        }        catch (Exception e)        {            // TODO: handle exception        }        t.flag = false;        b.start();    }}

显示结果有-1,程序不安全,说明了静态同步函数锁使用的锁不是this; 

静态方法中没有隐含的this。

静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象:类名 .class   该对象的类型是class


class Test implements Runnable{    private static int num = 50;    Object obj = new Object();    boolean flag = true;    public  void run ()    {        if (flag)        {            while (true)            {                synchronized (Test.class)                {                    if (num >= 0)                    {                        try                        {                            Thread.sleep(20);                        }                        catch (Exception e)                        {                            // TODO: handle exception                            System.out.println(e.toString());                        }                        System.out.println(Thread.currentThread().getName()+">>--code-- "+num--);                    }                }            }        }        else        {            while (true)            {                show();            }        }    }    public static synchronized void show ()//     {        // synchronized (obj)        //{        if (num >= 0)        {            try            {                Thread.sleep(20);            }            catch (Exception e)            {                // TODO: handle exception                System.out.println(e.toString());            }            System.out.println(Thread.currentThread().getName()+">>  show  "+num--);        }        //}    }}public class RUNNABLE{    public static void main (String[] args)    {        Test t = new Test();        Thread a = new Thread(t);        Thread b = new Thread(t);        a.start();        try        {            Thread.sleep(20);        }        catch (Exception e)        {            // TODO: handle exception        }        t.flag = false;        b.start();    }}

程序运行安全,说明静态同步的方法,使用的锁是该方法所在类的字节码文件对象:类名.class

点击打开链接




0 0
原创粉丝点击