什么情况需要用到?
当多个线程同时操作一个可共享的资源变量时,有可能产生冲突,为了避免这种冲突引入了线程同步的概念。
作用?
保证了对共享资源访问的唯一性。
举个例子:
我们去食堂吃饭,有一个阿姨负责打饭菜,她同时给几个人打菜,但有时候会给人打错菜,那去吃饭的人就有意见了,于她为了避免这样的问题,于是规定菜饭需要排队,每次只给一个人打菜,这样就比较公平了。
这个例子是告诉我们,同步就和排队一样,虽然能避免冲突,但也是会影响效率的,但是在某些场合, “公平”却显得更总要一些。
线程同步有几种方法:
1.synchronized关键字修饰
由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
同步方法
public synchronized void save(){}
同步代码块
synchronized(object){}
因为同步是一种高开销操作,因此应该尽量减少同步的内容,只同步关键代码就可以。
2.volatile关键字
用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此每次使用该域就要重新计算,而不是使用寄存器中的值,不能用来修饰final类型的变量。
例如:private volatile int account = 100; //此变量在做其他操作时即可实现线程同步
3.重入锁实现线程同步
JavaSE5.0中新增了一个java.util.concurrent包来支持同步使用的时候new ReentrantLock()对象,调lock()获得锁,关键代码执行完毕后调用unlock()释放锁。
4.局部变量实现线程同步
如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本, 副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。
ThreadLocal() : 创建一个线程本地变量
get() : 返回此线程局部变量的当前线程副本中的值
initialValue() : 返回此线程局部变量的当前线程的"初始值"
set(T value) : 将此线程局部变量的当前线程副本中的值设置为value
疑问?虽然是有副本,但源头同时被两个人调用,两个都修改了源值,一个先保存了,另一个还未保存的副本修改的内容就没有意义了?
5.队列实现线程同步
LinkedBlockingQueue:
实现了先进先出等特性,队列满的时候会阻塞直到有队列成员被消费,队列空的时候会阻塞,直到有队列成员被放进来。内部对数据的读写采用了锁的机制,和重入锁的概念一致,他的作用是让使用者交替访问共享的资源,避免数据错乱。
总结一下:
1.线程同步一定是多个线程访问同一个资源。
2.只有共享资源的读写访问才需要同步。
3.只有“变量”才需要同步访问。
4.使用synchronized关键字同步,应尽量缩小范围。
5.非线程安全不等于不安全,比如同时访问一个资源,但我每个线程都new一个这个资源对象做操作也不存在安全问题的。