背景
多个线程操作同一资源时,会产生线程安全问题。
实现
synchronized与lock锁均可解决线程安全问题。
问题复现
代码
package com.langjialing;
public class ThreadSynchronized {
public static void main(String[] args) {
BuyTicket buy = new BuyTicket();
new Thread(buy,"小明").start();
new Thread(buy,"小红").start();
}
}
class BuyTicket implements Runnable{
private int ticketsNum = 100;
boolean flag = true;
@Override
public void run() {
//买票
while(flag){
try{
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private synchronized void buy() throws InterruptedException {
if (ticketsNum <= 0){
flag = false;
return;
}
//模拟延迟
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到"+ ticketsNum--);
}
}
效果
synchronized解决
synchronized同步方法
synchronized同步方法仅需要在buy()方法上添加synchronized关键字,将buy()方法设置为同步方法。
package com.langjialing;
public class ThreadSynchronized {
public static void main(String[] args) {
BuyTicket buy = new BuyTicket();
new Thread(buy,"小明").start();
new Thread(buy,"小红").start();
}
}
class BuyTicket implements Runnable{
private int ticketsNum = 100;
boolean flag = true;
@Override
public void run() {
//买票
while(flag){
try{
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//synchronized关键字,将方法设置为同步方法,锁的是this
private synchronized void buy() throws InterruptedException {
if (ticketsNum <= 0){
flag = false;
return;
}
//模拟延迟
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到"+ ticketsNum--);
}
}
synchronized同步代码块
使用格式如下:
// Obj即为需要同步的对象
synchronized(Obj){
功能代码块;
}
例如对 List 添加同步锁:
public void test13() throws InterruptedException{
List<Integer> list = new ArrayList<>();
final ExecutorService executorService = Executors.newFixedThreadPool(32);
for (int i = 0; i < 32; i++) {
executorService.execute(() -> {
try {
Thread.sleep(RandomUtil.randomInt(100,1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int j = 0; j < 10; j++) {
synchronized (list){
list.add(j);
}
}
// 这里需要添加一个等待,否则会由于 List 引起并发问题
try {
Thread.sleep(RandomUtil.randomInt(300,500));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("size:" + list.size() + "; element:" + list);
});
}
executorService.shutdown();
}
Lock锁解决
package com.langjialing;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSynchronized {
public static void main(String[] args) {
BuyTicket buy = new BuyTicket();
new Thread(buy,"小明").start();
new Thread(buy,"小红").start();
new Thread(buy,"小兰").start();
}
}
class BuyTicket implements Runnable{
private int ticketsNum = 100;
boolean flag = true;
//定义lock锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
//买票
while(flag){
try{
lock.lock(); //加锁
buy();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
private void buy() throws InterruptedException {
if (ticketsNum <= 0){
flag = false;
return;
}
//模拟延迟
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "拿到"+ ticketsNum--);
}
}
synchronized与lock锁的区别
- synchronized可以作用在方法,也可以作用在指定的代码块,在括号中指定要锁的对象,同时不会出现死锁;而lock锁通过lock和unlock实现加锁、解锁,并需要把unlock写在finally代码块中防止死锁。
- synchronized是Java的关键字,是JVM内置的语言实现,因此性能比较低效;而lock锁是控制锁的代码块。
- synchronized只能等待锁的释放,不可以中断;而lock锁可以通过interrupt来中断。
- 在线程数量较大时,lock锁的效率要远高于synchronized。
- synchronized使用Object对象本身的wait、notify、notifyAll调度机制,而lock锁可以使用Condition进行线程之间的调度。
总结
线程安全问题可以通过线程同步来解决。