多线程问题
先来看一段代码
/**
* 支持 import Java 标准库 (JDK 1.8)
*/
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 注意:目前 Java 代码的入口类名称必须为 Main(大小写敏感)
*/
public class Main {
private int visitor = 100;
private void update() {
if (visitor >= 30) {
System.out.println(String.format("- 时间戳:%d【线程%s】修改visitor值", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
visitor -= 30;
}
// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
// Date date = new Date(System.currentTimeMillis());
}
public void threadControll() throws InterruptedException{
Thread t1 = new Thread(() -> {
System.out.println("Thread 1");
update();
System.out.println(String.format("- 时间戳:%d【线程%s】当前游客数量: %d", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
}, "t1");
Thread t2 = new Thread(() -> {
System.out.println("Thread 2");
update();
System.out.println(String.format("- 时间戳:%d【线程%s】当前游客数量: %d", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
}, "t2");
Thread t3 = new Thread(() -> {
System.out.println("Thread 3");
update();
System.out.println(String.format("- 时间戳:%d【线程%s】当前游客数量: %d", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
}, "t3");
Thread t4 = new Thread(() -> {
System.out.println("Thread 4");
update();
System.out.println(String.format("- 时间戳:%d【线程%s】当前游客数量: %d", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
}, "t4");
Thread t5 = new Thread(() -> {
System.out.println("Thread 5");
update();
System.out.println(String.format("- 时间戳:%d【线程%s】当前游客数量: %d", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
}, "t5");
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
t1.join();
t2.join();
t3.join();
t4.join();
t5.join();
}
public static void main(String []args) {
try {
new Main().threadControll();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Hello world!");
}
}
这段代码会出现线程不同步问题。
程序中定义了5个线程对visitor
值进行更改,并且读取操作,可以看到:
从输出就可以看出,数据出现了-50
、-20
这样的脏数据。
我们能想到的解决方案就是加锁,加锁分为自动加锁和手动加锁。
自动加锁
使用sychronized关键字进行加锁
非常简单,只需要在方法前加synchronized
关键字
private synchronized void update() {
if (visitor >= 30) {
System.out.println(String.format("- 时间戳:%d【线程%s】修改visitor值", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
visitor -= 30;
}
再来看一下输出:
可以看到,数量在低于30的情况下,不支持修改了
手动加锁
使用JUC并发包的ReentrantLock
进行手动加锁
可以将程序改为
private Lock lock = new ReentrantLock();
private void update() {
lock.lock();
if (visitor >= 30) {
System.out.println(String.format("- 时间戳:%d【线程%s】修改visitor值", System.currentTimeMillis(), Thread.currentThread().getName(), visitor));
visitor -= 30;
}
lock.unlock();
}
可以看到输出结果也是正常的: