JAVA——线程安全(synchronized)

卖票的程序

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

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();

    }
}

JAVA——线程安全(synchronized)_第1张图片
通过分析,发现,打印出,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();

    }
}

这里写图片描述

你可能感兴趣的:(java,线程安全,Class)