实现线程同步的所有方法分析

一、介绍

当我们写并发编程时,多个线程可同时访问一个共享资源,比如变量或对象,如果多个线程同时读写该资源,会导致该资源状态混乱,数据不准确,相互之间产生冲突。

因此加入同步锁,使资源同一时间只能有一个线程访问,从而保证资源不发生冲突。

二、线程同步具体实现

1、Synchronized

使用Synchronized关键字,分为同步方法和同步代码块,具体关于Synchronized的介绍和使用请移步《Synchronized简单介绍(原理、使用、对比分析)

2、volatile

volatile的中文意思是不稳定的、反复无常的、异变的。

a、volatile是轻量级同步机制,在访问volatile修饰的变量时,不会执行加锁操作,所以也就不能使线程阻塞,是一种比synchronized关键字更轻量级的同步机制。

b、volatile只能保证可见性,不能保证原子性;加锁机制即可以保证可见性又可以保证原子性,所以votatile并不能替代synchronized。

volatile的中文意思是不稳定的,反复无常的,异变的。当使用volatile声明变量后,系统总是重新从它所在的内存中读取数据,而非从缓存中读取,从而保证了数据在内存中的可见性,这就保证了同步。(通过这里也可以看出,volatile修饰的变量都是可变的,不能修饰final类型的变量)

c、volatile会禁止指令重排,屏蔽代码优化,进而造成效率降低,所以只有在必要时才可用该关键字。

3、使用原子变量

原子变量保证了原子操作(所谓原子操作,就是数据的读取、修改、保存作为一个整体行为,这个整体行为要么都执行,要么都不执行,是不可被分割、不可中断的,不能仅仅执行其中的一部分动作)

需要线程同步的根本原因就是因为普通变量的操作不是原子的,而原子变量保证了原子性,所以能保证同步。

在Java的util.concurrent.atomic包中提供了创建原子类型变量的工具类,使用这些原子类可以简化线程同步。

常用的原子变量类型有:

基本类型对应:AtomicBoolean、AtomicInteger、AtomicLong

数组类型对应:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

引用类型对应:AtomicReference

4、使用ThreadLocal类

ThreadLocal通过字面直接翻译,就是线程本地 ,也就是线程本地变量、线程局部变量。

ThreadLocal的作用域仅仅是本线程内,即ThreadLocal为每个线程提供了一个特定的变量,以保存该线程所独享的数据,所以其提供了一种隔离线程,防止线程间数据共享的方法。

详细介绍请移步《ThreadLocal解析》。

5、使用阻塞队列

阻塞队列(BlockingQueue)和普通队列的区别就是阻塞,所谓阻塞,即当队列是空时,从队列中取数据(take())的操作会被阻塞,或当队列已经满时,向队列存数据(put(E, e))的操作将会被阻塞。当存取数据队列被阻塞时,即表示当前线程阻塞。

阻塞队列的常见实现类如ArrayBlockingQueue,LinkedBlockingQueeu,PriorityBlockingQueue等。

阻塞队列的使用场景典型的是生产者消费者模式,生产者线程向阻塞队列中存放数据,消费者线程从该阻塞队列中取出数据。

public class MainActivity extends AppCompatActivity {
    ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);
    Button mBtn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtn = (Button)  findViewById(R.id.mBtn);
        mBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Producer(queue).start();
                new Consumer(queue).start();
            }
        });
    }

    //生产者
    class Producer extends Thread {
        ArrayBlockingQueue<String> mQueue;
        public Producer(ArrayBlockingQueue<String> queue) {
            mQueue = queue;
        }

        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(50);
                    mQueue.put("Production" + i);
                    System.out.println("Producer:Production" + i );
                } catch (Exception e) {
                    e.getMessage();
                }
            }
        }
    }

    //消费者
    class Consumer extends Thread {
        ArrayBlockingQueue<String> mQueue;

        public Consumer(ArrayBlockingQueue<String> queue) {
            mQueue = queue;
        }

        @Override
        public void run() {
            super.run();
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(50);
                    String str = mQueue.take();
                    System.out.println("Consumer: " + str );
                } catch (Exception e) {
                    e.getMessage();
                }
            }
        }
    }
}

执行结果如下:

6、使用Lock

主要就是使用重入锁ReentrantLock类 ,其实现了Lock接口。

常用的方法:

创建ReentrantLock()实例:ReentrantLock lock = new ReentrantLock();

获得锁:lock.lock();

释放锁:lock.unlock();

代码 :

//创建实例
ReentrantLock lock = new  ReentrantLock();
lock.lock();
try{
    //doSomething
}finally{
    lock.unlock();
}

通过代码可以看到,使用ReentrantLock,必须自己实现获得/释放锁,从而带来了风险,显然不如直接使用synchronized.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值