推荐:关于锁:Java面试题超详细整理《多线程篇》_龙源lll的博客-优快云博客_高并发面试题
一.实现线程同步的几种方式?为何要使用同步?
java允许多线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),
将会导致数据不准确,相互之间产生冲突
因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
使用多线程时,保证数据的唯一性和准确性。
Java实现同步的几种方式:::
1.对方法通过加synchronized关键字实现多个线程同时访问共享资源,当有申请者申请该资源时,如果资源没有被占用,就给这个申请者使用,否则不能使用该资源。
public synchronized void save(){}
synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类
2.需要同步的变量加上volatile
使用特殊域变量(volatile)实现线程同步
a.volatile关键字为域变量的访问提供了一种免锁机制,
b.使用volatile修饰域相当于告诉虚拟机该域可能会被其他线程更新,
c.因此每次使用该域就要重新计算,而不是使用寄存器中的值
d.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量
private volatile int account = 1
3.使用重入锁实现线程同步;
4.使用局部变量ThreadLocal实现线程同步
二、线程安全问题
1.创建了三个窗口(线程)每个窗口都有自己的一百张票(ticketCount);所以要想三个窗口共享100张票,就需要把ticketCount成员变量改为静态的,所有Ticket对象共享;
多线程操作同一个数据时,出现了线程安全问题;
解决办法:线程同步;
2.对线程安全的理解
当多个线程访问一个对象时,如果不用进行额外的同步控制或其他的协调操作,调用这个对象的行为都可以获得正确的结果,我们就说这个对象是线程安全的 。
在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成线程安全的潜在原因。
目前主流操作系统都是多任务的。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的
如何保证线程安全?
方法一:使用安全类,比如 java.util.concurrent 下的类,使用原子类AtomicInteger
方法二:使用JVM提供的自动锁 synchronized。
方法三:使用JDK提供的手动锁 Lock。
3.synchronized关键字的作用
synchronized 它可以把任意一个非 NULL 的对象当作锁。他属于独占式的悲观锁,同时属于可重入锁。
synchronized 关键字解决的是多个线程之间访问资源的同步性,synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。
三种使用方式:
修饰实例方法:作用于当前对象实例加锁,进入同步代码前要获得当前对象实例的锁
修饰静态方法:也就是给当前类加锁,会作用于类的所有对象实例,因为静态成员不属于任何一个实例对象,是类成员。所以如果一个线程A调用一个实例对象的非静态 synchronized 方法,而线程B需要调用这个实例对象所属类的静态 synchronized 方法,是允许的,不会发生互斥现象,因为访问静态synchronized 方法占用的锁是当前类的锁,而访问非静态 synchronized 方法占用的锁是当前实例对象锁。
修饰代码块: 指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。
总结: synchronized 关键字加到 static 静态方法和 synchronized(class)代码块上都是是给 Class 类上锁。synchronized 关键字加到实例方法上是给对象实例上锁。尽量不要使用 synchronized(String a) 因为JVM中,字符串常量池具有缓存功能
3.volatile 关键字的作用
Java 提供了 volatile 关键字是线程同步的轻量级实现,用来保证可见性和有序性(禁止指令重排),volatile 常用于多线程环境下的单次操作(单次读或者单次写)。
对于加了 volatile关键字的成员变量,在对这个变量进行修改时,全直接将CPU高级缓存中的数据送回到主内存,对这个变量的读取也会直接从主内存中读取,从而保证了可见性
在对 volatile修饰的成员变量进行读写时,会插入内存屏障,而内存屏障可以达到禁止重排序的效果,从而可以保证有序性
4说说 synchronized 关键字和 volatile 关键字的区别
synchronized 关键字和 volatile 关键字是两个互补的存在,而不是对立的存在!
volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比synchronized关键字要好 。
volatile 关键字只能用于变量,而synchronized 关键字可以修饰方法以及代码块 。
volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。
volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资源的同步性。