多线程-02,线程安全问题解决

第一种方法:同步代码块

代码示例:

/**
 * 运行出现 打印出0,-1,-2错票
 * 线程出现了安全问题
 *
 * 问题原因:当多条语句操作‘同一个’线程共享数据时,一个线程对多条语句只执行了一部分,还没有
 *         执行完,另一个线程参与进来执行。导致共享数据的错误。
 *
 * 解决办法:
 *     对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行
 *
 * java对于多线程的安全问题提供了专业的解决方式
 * 就是同步代码块。
 *
 * synchronized(对象)
 * {
 *     需要同步的代码
 * }
 *
 * 对象如同锁。持有锁的线程可以在同步中执行
 * 没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁
 *
 * 火车的卫生间 -- 经典
 *
 * 同步的前提
 * 1,必须有两个或者两个以上的线程
 * 2,必须是多个线程使用同一个锁
 *
 * 必须保证同步中只能有一个线程在运行
 *
 * 好处:解决了多线程的安全问题
 *
 * 弊端:多个线程都需要判断锁,较为消耗资源。
 *
 */
public class TicketDemo  implements Runnable{

    private int tick = 100;
    Object obj = new Object();
    public void run() {
        while (true) {
            synchronized (obj){
                if (tick > 0) {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + "sale..." + tick--);
                }
            }
        }
    }

    public static void main(String[] args) {
        TicketDemo demo = new TicketDemo();

        Thread t1 = new Thread(demo);
        Thread t2 = new Thread(demo);
        Thread t3 = new Thread(demo);
        Thread t4 = new Thread(demo);

        t1.start();
        t2.start();
        t3.start();
        t4.start();

    }

}

第二种方式:同步函数

代码示例:

/**
 * 需求:
 * 银行有一个金库
 * 有两个储户分别存300元,每次存100
 *
 * 目的:该程序是否有安全问题,如果有,如何解决?
 *
 * 如何找问题?
 * 1,明确哪些代码是多线程运行代码
 * 2,明确共享数据
 * 3,明确多线程运行代码中哪些语句是操作共享数据的
 */
public class Bank {
    private int sum;
//    Object obj = new Object();
    public synchronized void add(int n) {
//        synchronized (obj){
        sum += n;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("sum = " + sum );
//        }
    }
}

class Cus implements Runnable{

    private Bank bank = new Bank();

    public void run() {
        for (int i = 0; i < 3; i++) {
            bank.add(100);
        }
    }

}
class BankDemo{
    public static void main(String[] args) {
        Cus cus = new Cus();
        Thread t1 = new Thread(cus);
        Thread t2 = new Thread(cus);

        t1.start();
        t2.start();
    }
}

同步代码块中的锁是this

/**
 * 同步函数用的是哪一个锁?
 * 函数需要被对象调用。那么函数都有一个所属对象的引用。就是this
 * 所以同步函数使用的锁是this
 *
 * 通过改程序进行验证:
 * 使用两个线程来买票
 * 一个在同步代码块中
 * 一个在同步函数中
 * 都在执行买票动作
 */
public class Ticket implements Runnable{

    private int tick = 100;
    Object obj = new Object();
    boolean flag = true;
    public void run() {
        if(flag){
            while (true){
                synchronized (this){
                    if (tick > 0) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "sale..." + tick--);
                    }
                }
            }
        }else {
            while (true)
            this.show();
        }
    }
    public synchronized void show() {
        if (tick > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "sale..." + tick--);
        }
    }
}
class TicketDemo{
    public static void main(String[] args) throws InterruptedException {
        Ticket demo = new Ticket();

        Thread t1 = new Thread(demo);
        Thread t2 = new Thread(demo);

        t1.start();
        Thread.sleep(10);
        demo.flag = false;
        t2.start();

    }
}
静态方法使用的锁是什么?

/**
 * 如果同步函数被静态修饰后,使用的锁是什么?
 *
 * 通过验证发现不是this。因为静态方法中不可以定义this
 *
 * 静态进内存时,内存中没有本类对象,但一定有该类对应的二进制码文件对象。
 * 类名.class,该对象类型是class
 *
 * 静态的同步方法使用的锁是该方法所在类的字节码文件对象 类名.class
 */
public class Ticket implements Runnable{

    private static int tick = 100;
    Object obj = new Object();
    boolean flag = true;
    public void run() {
        if(flag){
            while (true){
                synchronized (Ticket.class){
                    if (tick > 0) {
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "sale..." + tick--);
                    }
                }
            }
        }else {
            while (true)
            this.show();
        }
    }
    public static synchronized void show() {
        if (tick > 0) {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "sale..." + tick--);
        }
    }
}
class TicketDemo{
    public static void main(String[] args) throws InterruptedException {
        Ticket demo = new Ticket();

        Thread t1 = new Thread(demo);
        Thread t2 = new Thread(demo);

        t1.start();
        Thread.sleep(10);
        demo.flag = false;
        t2.start();

    }
}

/**
 * 解决懒汉模式的多线程安全问题
 * 双重判断可解决同步代码低效问题
 */
public class Single
{
    private static Single single;

    private Single()
    {
    }

    public static Single getInstance()
    {
        if (single == null)
        {
            synchronized (Single.class)
            {
                if (single == null)
                {
                    single = new Single();
                }
            }
        }
        return single;
    }

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值