1 什么是线程同步
线程同步:即当有一个线程在对一块内存进行操作时,其他线程都不可以对这个内存地址进行操作,直到该线程完成操作, 其他线程才能对该内存地址进行操作,而其他线程又处于等待状态,这种线程访问数据的模式就叫做线程同步。
2 同步的前提
多个线程执行的时候访问共享数据时需要同步,如果是单线程则不需要同步。
多个线程在执行的过程中是不是使用同一把锁。如果是,就是同步。否则不是同步。
3 Java实现线程同步的方法
线程同步的方法有很多,这里介绍常用的6种方法。
同步方法
即由synchronized关键字修饰的方法。 由于java的每个对象都有一个内置锁,当用此关键字修饰方法时, 内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则就处于阻塞状态。
注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类。
同步代码块
即有synchronized关键字修饰的语句块。 被该关键字修饰的语句块会自动被加上内置锁,从而实现同步。
使用特殊域变量(volatile)实现线程同步
volatile关键字为共享变量的访问提供了一种免锁机制,使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,因此每次使用该域就要重新计算,而不是使用寄存器中的值。volatile禁止重排序,但是不会提供任何原子操作,它也不能用来修饰final类型的变量。
因此volatile可以用在对原性操作的共享变量进行数据同步,但是对于非原子性操作无效。
使用JUC提供的各种锁实现线程同步
JUC中的锁相比如synchronized更加灵活。
ReentrantLock; 可重入锁.互斥锁
ReentrantReadWriteLock 可重入锁.读写锁
使用JUC中的阻塞队列实现线程同步
每次只能有一条线程改变队列的数据,保证了队列的安全,底层使用还是使用的lock锁。
使用JUC中的原子变量实现线程同步
java.util.concurrent.atomic包下的是类的小工具包,支持在单个变量上解除锁的线程安全编程。底层使用的是cas无锁机制操作变量+volatile修饰变量,保证操作的可见性和原子性并禁止指令重排序。
3.1 同步块
使用同步块解决数据安全问题:
synchronized(obj){ //obj叫做同步监视器,也叫同步锁 //操作的共享数据 }
上面代码的含义为:线程开始执行同步代码块之前,必须先获得对同步监视器的锁定。任何时候只能有一条线程可以获得同步监视器的锁定,并且sleep的线程不会释放同步锁。当同步代码块执行完之后,该线程会释放对该同步监视器的锁定。
注意多条线程并须使用同一把锁,否则“锁”便没有作用!
3.2 同步函数
使用同步函数(方法)解决数据安全问题:
Public (static) synchronized void xx(){ //语句一; // 语句二; }
3.3 同步块和同步函数的优缺点
共同的优缺点:
优点:线程同步,解决了数据的安全问题。
缺点:当线程较多时,每一条线程每次运行都会去判断获取同步锁,这是很消耗资源的,会降低运行效率。
分别的优缺点:
同步块优点:
锁范围自定义。
锁对象可以是任意对象,除了null,但必须是同一把锁对象。
如果在线程同步当中需要使用多个锁对象,只能使用同步块。
同步块缺点:
增加了代码的层级。
同步函数优点:
使用方便,减少了代码的层级。
同步函数缺点:
锁对象是默认的:普通方法锁对象是this锁;静态方法的是字节码文件对象Class。
锁的范围是整个方法,不灵活。
如果有什么不懂或者需要交流,可以留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!