java 实现线程安全的3种方式

java实现线程安全的3种方式

什么时候使用同步

运用在书里面看到的一种说法:如果你正在写一个变量,它可能接下来将被另一个线程读取,或者正在读取一个上一次已经被另一个线程写过的变量,那么你必须使用同步,并且,读写线程都必须用相同的监视器锁同步。

在我们的代码中常常会出现临界资源,如果在类中有超过一个方法在处理临界数据,那么你必须同步所有相关的方法。如果只同步一个方法,那么这个数据就变得不安全了,这值得我们重视:每个访问临界共享资源的方法都必须被同步,否则他们就不会正确地工作。

1.同步代码块

同步代码块使用的是
synchronized(锁对象){
}
这样一种结构,这种方式通过更改输入的锁对象的某个值,来确定当前哪一个线程执行,需要注意的是任何对象都可以成为锁对象。使用同一个锁对象的线程,会根据锁对象的值执行。
下面是一段示例代码:

public static void main(String[] args) {
        //线程不安全
        //解决办法1 同步代码块
        //格式:synchronized(锁对象){}
        //任何对象都可以是锁对象
        Runnable run = new Ticket2();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
        }

        static class Ticket2 implements Runnable{
            //总票数
            private int count = 10;
            private Object o = new Object();
            @Override
            public void run() {

                    while(true){
                        synchronized(o){
                            if (count > 0){
                             //卖票
                                System.out.println("正在卖票");
                                try {
                                   Thread.sleep(1000);
                                 } catch (InterruptedException e) {
                                 e.printStackTrace();
                                }
                                  count--;
                                 System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                        }else{
                                break;
                            }

                    }
                }


            }
    }

2.同步方法

通过为需要排队执行的方法加入synchronized修饰符实现线程安全。这种方式的锁对象为synchronized修饰的对象,调用时也就是this,或者在静态修饰的变量中就是 xxxx.class。
如果我们通过同一个对象多次调用synchronized修饰的方法,那么他是线程安全的。
如果通过同一个类的多个对象多次调用synchronized修饰的方法,他就不安全了。

下面是一段示例代码:

public static void main(String[] args) {
        //线程不安全
        //解决方案2
        Runnable run = new Ticket3();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket3 implements Runnable{
        //总票数
        private int count = 10;
        @Override
        public void run() {
            while(true){
                boolean flag = sale();
                if(!flag){
                    break;
                }
            }
        }
        //给方法添加synchronized修饰符 即可排队执行这个方法
        //这种情况锁对象是 run对象 也就是方法的this 或者 Ticket3.class
        //如果再创建一个synchronized代码块 锁对象为this 那么和锁方法共用一把锁
        public synchronized boolean sale(){
            if (count > 0){
                //卖票
                System.out.println("正在卖票");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                count--;
                System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                return true;
            }
            return false;
        }
    }

3.显示锁Lock

通过创建ReentrantLock对象,再手动调用该对象的lock()与unlock()方法来实现线程的安全。

下面是一段示例

public static void main(String[] args) {
        //线程不安全
        //解决办法3 显示锁Lock 子类 ReentrantLock
        //自己创建锁对象
        Runnable run = new Ticket4();
        new Thread(run).start();
        new Thread(run).start();
        new Thread(run).start();
    }

    static class Ticket4 implements Runnable{
        //总票数
        private int count = 10;
        //显示锁
        private Lock l = new ReentrantLock();

        @Override
        public void run() {

            while(true){
                l.lock();
                    if (count > 0){
                        //卖票
                        System.out.println("正在卖票");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        count--;
                        System.out.println(Thread.currentThread().getName() + "出票成功,余票:" + count);
                    }else{
                        break;
                    }
                    l.unlock();


            }


        }
    }

synchronized与显示Lock的对比

显示Lock相比较于synchronized显得更加灵活,更易于控制代码,当因为同步出现异常时,Lock会更加容易确保整个程序的继续执行,但是代码的数量会增加。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值