JAVA——线程安全(synchronized)

来源:互联网 发布:数据清洗怎么做? 编辑:程序博客网 时间:2024/06/16 02:36

卖票的程序

我们在卖票的中间暂停一段时间。观察程序的运行结果。

class Ticket implements Runnable{    private  int ticket = 20;    public void run()    {        while(true)        {            if(ticket>0)            {                try                {                    Thread.sleep(100);                }                catch(Exception e)                {                }                System.out.println(Thread.currentThread().getName()+"---sale :"+ticket--);            }        }    }}class TicketDemo1{    public static void main(String[] args)    {        Ticket t  = new Ticket();        Thread t1  = new Thread(t);//创建了一个线程        Thread t2  = new Thread(t);//创建了一个线程        Thread t3  = new Thread(t);//创建了一个线程        Thread t4  = new Thread(t);//创建了一个线程        t1.start();        t2.start();        t3.start();        t4.start();    }}

这里写图片描述
通过分析,发现,打印出,0,-1.-2等错票。这是什么原因呢?有人说是博主故意加了sleep,其实吧,正常情况下,cpu要在多个线程之间进行来回切换的,不可以一直运行到底的,当卖票的时候cpu突然就切出去了。所以这里只是模仿了一下。

分析原因

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

解决办法

对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。JAVA对于多线程的安全问题提供了专业的解决方法。就是同步代码块。
synchronized(对象){    //需要被同步的代码}
对象如同锁。持有锁的线程可以在同步中进行。没有持有锁的线程即使获取了cpu的执行权,也进不去,因为没有获得锁。

例子

高铁上的卫生间,一个人进去了,门外就显示红色灯表明已经有人了,等ok出来以后,灯又变绿了。

同步的前提


1、必须要有两个或者两个以上的线程。(多个人想去上厕所)
2、必须使多个线程使用同一个锁。(上的必须是同车厢的厕所)
必须保证同步中只能有一个线程在运行。

好处:解决了多线程的安全问题。
弊端:多个线程都需要判断锁,较为消耗资源。

class Ticket implements Runnable{    private  int ticket = 20;    Object o = new Object();    public void run()    {        while(true)        {            synchronized(o) //线程运行到这里判断锁,就相当于一个标志位,获得锁以后就把标志位置位,其他线程进不来。当该线程执行完出去以后,会把标志位置回去。            {                if(ticket>0)                {                    try                    {                        Thread.sleep(100);                    }                    catch(Exception e)                    {                    }                    System.out.println(Thread.currentThread().getName()+"---sale :"+ticket--);                }            }        }    }}class TicketDemo1{    public static void main(String[] args)    {        Ticket t  = new Ticket();        Thread t1  = new Thread(t);//创建了一个线程        Thread t2  = new Thread(t);//创建了一个线程        Thread t3  = new Thread(t);//创建了一个线程        Thread t4  = new Thread(t);//创建了一个线程        t1.start();        t2.start();        t3.start();        t4.start();    }}

这里写图片描述

例子:
银行有一个金库。
有两个储户分别存300元,每次存100,存3次;

class Bank{       Object obj = new Object();    private int sum;    public  void add(int n)    {        synchronized(obj)        {               sum = sum +n;            try            {                Thread.sleep(10);            }            catch(Exception e){}            System.out.println("sum = "+sum);        }    }}class Cus implements Runnable{    private Bank b = new Bank();    public void run()    {        for(int x= 0;x<3;x++)        {            b.add(100);        }    }}class BankDemo{    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 Bank{       Object obj = new Object();    private int sum;    public synchronized void add(int n)    {            sum = sum +n;            try            {                Thread.sleep(10);            }            catch(Exception e){}            System.out.println("sum = "+sum);    }}class Cus implements Runnable{    private Bank b = new Bank();    public void run()    {        for(int x= 0;x<3;x++)        {            b.add(100);        }    }}class BankDemo{    public static void main(String[] args)    {        Cus c = new Cus();        Thread t1 = new Thread(c);        Thread t2 = new Thread(c);        t1.start();        t2.start();    }}

这里写图片描述

如何找问题?

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

死锁

Java线程死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。
假如线程 “A”获得了刀,而线程“B”获得了叉。线程“A”就会进入阻塞状态来等待获得叉,而线程“B”则阻塞来等待“A”所拥有的刀。

class Test implements Runnable{    private boolean flag;    Test(boolean flag)    {        this.flag = flag;    }    public void run()    {        if(flag)    {        synchronized(MyLock.locka)        {            System.out.println("if locka!");            synchronized(MyLock.lockb)        {            System.out.println("if lockb!");        }        }    }    else    {        synchronized(MyLock.lockb)        {         System.out.println("if lockb!");            synchronized(MyLock.locka)        {                 System.out.println("if locka!");        }        }    }    }}class MyLock{    static Object locka = new Object();    static Object lockb = new Object();}class DeadLockTest{    public static void main(String[] args)    {        Thread t1 = new Thread(new Test(true));        Thread t2 = new Thread(new Test(false));        t1.start();        t2.start();    }}

这里写图片描述

0 0
原创粉丝点击