首页 > 代码库 > Java---14---多线程---synchronized 同步函数

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

设计这样一个程序:两个用户去银行存钱,每次存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 ()//  锁是 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; 

静态方法中没有隐含的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 ()//  锁是 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();

    }
}

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

点击打开链接




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