java面试

线程安全的实现方法(1)---互斥同步

2018-06-21  本文已影响20人  Bre_eze

互斥同步

同步是指在多个此案成并发访问共享数据时,保证共享数据在同一个时刻只被一个线程使用。互斥是实现同步的一种手段。除此之外的手段还有:临界区,互斥量和信号量。

Java中,最基本的互斥同步手段就是synchronized关键字,如果Java程序中明确指定了对象参数,那么锁的对象就是这个对象的引用。如果没有指定,则根据synchronized修饰的是实例方法还是类方法,去取对应的对象实例或Class对象来作为锁对象。

因为synchronized锁是阻塞式的,且要阻塞或唤醒一个线程,都需要操作系统来帮忙完成,这就需要从用户态切换到核心态,这会耗费很多时间。所以说synchronized是一个重型锁。

java.util.concurrent包中的重入锁(ReentrantLock)也可以实现同步,基本用法上与synchronized相似,但相比synchronized,ReentrantLock增加了一些新的高级功能:等待可中断,可实现公平锁,可以绑定多个条件。

等待可中断即当持有锁的线程长时间不释放锁的时候,正在等待的线程可以选择放弃等待,改为处理其他事情。

公平锁即当多个线程在等待一个锁时,必须按照申请的时间顺序来依次获得锁;synchronized是非公平的,ReentrantLock默认也是非公平的,但可以通过构造函数来使用公平锁。需要强调的是公平锁在高并发的情境下是极其损耗性能的。

//创建一个非公平锁,默认是非公平锁
Lock lock = new ReentrantLock();
Lock lock = new ReentrantLock(false);

//创建一个公平锁,构造传参true
Lock lock = new ReentrantLock(true);

锁绑定多个条件是指一个ReentrantLock对象可以同时绑定多个Condition对象,而synchronized在实现多个判断条件时,需要新建多个锁。

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class TestDemo7 {
    static class NumberWrapper{
        public int value = 1;
    }
    public static void main(String[] args) {
        //初始化可重入锁
        final Lock lock = new ReentrantLock();
        
        //第一个条件当屏幕上输出到3
        final Condition reachThreeCondition = lock.newCondition();
        //第二个条件当屏幕上输出到6
        final Condition reachSixCondition = lock.newCondition();
        
        //NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final
        //注意这里不要用Integer, Integer 是不可变对象
        final NumberWrapper num = new NumberWrapper();
        //初始化A线程
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                //需要先获得锁
                lock.lock();
                try {
                    System.out.println("threadA start write");
                    //A线程先输出前3个数
                    while (num.value <= 3) {
                        System.out.println(num.value);
                        num.value++;
                    }
                    //输出到3时要signal,告诉B线程可以开始了
                    reachThreeCondition.signal();
                } finally {
                    lock.unlock();
                }
                lock.lock();
                try {
                    //等待输出6的条件
                    reachSixCondition.await();
                    System.out.println("threadA start write");
                    //输出剩余数字
                    while (num.value <= 9) {
                        System.out.println(num.value);
                        num.value++;
                    }

                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
            }

        });


        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    lock.lock();
                    
                    while (num.value <= 3) {
                        //等待3输出完毕的信号
                        reachThreeCondition.await();
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.unlock();
                }
                try {
                    lock.lock();
                    //已经收到信号,开始输出4,5,6
                    System.out.println("threadB start write");
                    while (num.value <= 6) {
                        System.out.println(num.value);
                        num.value++;
                    }
                    //4,5,6输出完毕,告诉A线程6输出完了
                    reachSixCondition.signal();
                } finally {
                    lock.unlock();
                }
            }

        });


        //启动两个线程
        threadB.start();
        threadA.start();
    }
}
上一篇下一篇

猜你喜欢

热点阅读