怎么建设网站规划网址和网站的区别
1. 数据竞争导致数据不一致
两个线程分别把一个变量增加10000次,理论上变量最后的值是20000,实际上小于20000
package org.example;public class synchronizedTest {private static int counter = 0;public static void main(String[] args) throws InterruptedException {// 线程 1:对 counter 进行 10000 次自增操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter++;}});// 线程 2:对 counter 进行 10000 次自增操作Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {counter++;}});// 启动两个线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 预期结果应该是 20000,但由于数据竞争,实际结果可能小于 20000System.out.println("Counter value: " + counter);}
}
counter++
的底层操作
counter++
这个操作在 Java 代码里看似是一个简单的自增操作,但在计算机底层,它实际上是由多个步骤组成的:
- 读取(Read):从内存中读取
counter
的当前值到 CPU 的寄存器中。 - 加 1(Increment):在寄存器中对读取的值进行加 1 操作。
- 写入(Write):将寄存器中加 1 后的值写回到内存中的
counter
变量。
执行过程
- 线程 1 读取
counter
的值:线程 1 从内存中读取counter
的值 0 到它的寄存器中。此时线程 1 的寄存器中的值为 0,内存中counter
的值仍为 0。 - 线程调度切换到线程 2:操作系统将 CPU 控制权交给线程 2。
- 线程 2 读取
counter
的值:线程 2 从内存中读取counter
的值 0 到它的寄存器中。此时线程 2 的寄存器中的值为 0,内存中counter
的值还是 0。 - 线程 2 完成自增并写入:线程 2 在寄存器中对值进行加 1 操作,寄存器中的值变为 1,然后将 1 写回到内存中。此时内存中
counter
的值变为 1。 - 线程调度切换回线程 1:操作系统将 CPU 控制权交回给线程 1。
- 线程 1 完成自增并写入:线程 1 在它的寄存器中对之前读取的 0 进行加 1 操作,寄存器中的值变为 1,然后将 1 写回到内存中。此时内存中
counter
的值还是 1。
在上述过程中,线程 1 和线程 2 都进行了一次自增操作,但由于数据竞争,内存中 counter
的值只增加了 1,而不是预期的 2,这就导致了数据不一致。
修改后,把修改数据的值操作封装为方法用synchronized修饰,或者用synchronized修饰代码块,确保同一时间只有一个线程能够访问方法
package org.example;public class synchronizedTest {private static int counter = 0;private static final Object staticLock = new Object();public static synchronized void increment() {counter++;}public static void increment() {// 对当前对象(this)加锁synchronized (staticLock) {counter++;}}public static void main(String[] args) throws InterruptedException {// 线程 1:对 counter 进行 10000 次自增操作Thread thread1 = new Thread(() -> {for (int i = 0; i < 10000; i++) {increment();}});// 线程 2:对 counter 进行 10000 次自增操作Thread thread2 = new Thread(() -> {for (int i = 0; i < 10000; i++) {increment();}});// 启动两个线程thread1.start();thread2.start();// 等待两个线程执行完毕thread1.join();thread2.join();// 预期结果应该是 20000,但由于数据竞争,实际结果可能小于 20000System.out.println("Counter value: " + counter);}
}
2,并发修改
线程1在迭代List的同时,线程2添加新元素
package org.example;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class synchronizedTest {public static void main(String[] args) {// 创建一个 ArrayListList<Integer> list = new ArrayList<>();for (int i = 0; i < 10; i++) {list.add(i);}// 线程 1:迭代 ListThread thread1 = new Thread(() -> {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());try {// 模拟耗时操作Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}});// 线程 2:修改 ListThread thread2 = new Thread(() -> {try {// 等待一段时间,确保线程 1 开始迭代Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}list.add(10);});// 启动两个线程thread1.start();thread2.start();}
}
解决问题,使用线程安全集合类或者synchronized修饰代码块
package org.example;import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;public class synchronizedTest {private static final List<Integer> list = new CopyOnWriteArrayList<>();public static void main(String[] args) {// 初始化列表for (int i = 0; i < 10; i++) {list.add(i);}// 线程 1:迭代列表Thread thread1 = new Thread(() -> {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}});// 线程 2:修改列表Thread thread2 = new Thread(() -> {try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}list.add(10);});thread1.start();thread2.start();}
}
3,死锁问题
两个线程争夺资源造成互相等待现象
package org.example;public class synchronizedTest {private static final Object resource1 = new Object();private static final Object resource2 = new Object();public static void main(String[] args) {// 线程 1:先获取 resource1,再获取 resource2Thread thread1 = new Thread(() -> {synchronized (resource1) {System.out.println("Thread 1: Holding resource 1...");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 1: Waiting for resource 2...");// resource2已经被tread2占有,无法执行下面代码synchronized (resource2) {System.out.println("Thread 1: Holding resource 1 and 2...");}}});// 线程 2:先获取 resource2,再获取 resource1Thread thread2 = new Thread(() -> {synchronized (resource2) {System.out.println("Thread 2: Holding resource 2...");try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("Thread 2: Waiting for resource 1...");// resource1已经被tread1占有,无法执行下面代码synchronized (resource1) {System.out.println("Thread 2: Holding resource 1 and 2...");}}});// 启动两个线程thread1.start();thread2.start();}
}
修改,增加信号量,线程1获取resource1和信号量,线程2获取不到信号量,不能获取resource2,线程1接着获取resource2
package org.example;import java.util.concurrent.Semaphore;public class synchronizedTest {// 定义两个资源private static final Object resource1 = new Object();private static final Object resource2 = new Object();// 定义一个信号量,初始许可证数量为 1private static final Semaphore semaphore = new Semaphore(1);public static void main(String[] args) {// 线程 1Thread thread1 = new Thread(() -> {try {// 获取信号量的许可证semaphore.acquire();// 访问资源 1synchronized (resource1) {System.out.println("Thread 1: Holding resource 1...");Thread.sleep(100);// 访问资源 2synchronized (resource2) {System.out.println("Thread 1: Holding resource 1 and 2...");}}} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放信号量的许可证semaphore.release();}});// 线程 2Thread thread2 = new Thread(() -> {try {// 获取信号量的许可证semaphore.acquire();// 访问资源 2synchronized (resource2) {System.out.println("Thread 2: Holding resource 2...");Thread.sleep(100);// 访问资源 1synchronized (resource1) {System.out.println("Thread 2: Holding resource 1 and 2...");}}} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放信号量的许可证semaphore.release();}});// 启动线程thread1.start();thread2.start();}
}
ReentrantLock使用
private static int counter = 0;private static final Lock lock = new ReentrantLock();public static void increment() {// 获取锁lock.lock();try {// 临界区,对共享资源进行操作counter++;System.out.println(Thread.currentThread().getName() + " 执行后 counter 的值为: " + counter);} finally {// 释放锁,确保在任何情况下锁都会被释放lock.unlock();}}public static void main(String[] args) {// 线程 1Thread thread1 = new Thread(() -> {for (int i = 0; i < 5; i++) {increment();}});// 线程 2Thread thread2 = new Thread(() -> {for (int i = 0; i < 5; i++) {increment();}});thread1.start();thread2.start();}
package org.example;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class TryLockExample {private static final Lock lock = new ReentrantLock();public static void performTask() {if (lock.tryLock()) {try {System.out.println(Thread.currentThread().getName() + " 获取到锁,开始执行任务");// 模拟任务执行Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println(Thread.currentThread().getName() + " 释放锁");}} else {System.out.println(Thread.currentThread().getName() + " 未能获取到锁,放弃执行任务");}}public static void main(String[] args) {// 线程 1Thread thread1 = new Thread(TryLockExample::performTask);// 线程 2Thread thread2 = new Thread(TryLockExample::performTask);thread1.start();thread2.start();}
}
非公平锁,此处JVM用到了偏向锁优化,线程1第一次获取到锁,此后锁更偏向于这个线程
优点:减少锁开销,简化机制
公平锁,先来先得,避免饥饿问题
package org.example;import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class FairLockInOneClass {// 创建公平锁private final Lock fairLock = new ReentrantLock(true);// 共享计数器private int counter = 0;// 内部类,代表工作线程private class Worker extends Thread {public Worker(String name) {super(name);}@Overridepublic void run() {for (int i = 0; i < 3; i++) {// 获取锁fairLock.lock();try {counter++;System.out.println(Thread.currentThread().getName() + " 执行操作,当前计数器值为: " + counter);// 模拟操作耗时Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁fairLock.unlock();}}}}// 启动多个工作线程的方法public void startThreads() {Worker worker1 = new Worker("线程 1");Worker worker2 = new Worker("线程 2");Worker worker3 = new Worker("线程 3");worker1.start();worker2.start();worker3.start();try {// 等待所有线程执行完毕worker1.join();worker2.join();worker3.join();} catch (InterruptedException e) {e.printStackTrace();}}public static void main(String[] args) {FairLockInOneClass example = new FairLockInOneClass();example.startThreads();}
}
volatile关键字
如果flag不用volatile修饰,thread2读到的flag为false,不会重新从内存里面读取,不能结束循环
package org.example;public class volatileTest {// 使用 volatile 关键字修饰变量private static volatile boolean flag = false;public static void main(String[] args) {// 线程 1:修改 flag 的值Thread thread1 = new Thread(() -> {try {// 模拟耗时操作Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}flag = true;System.out.println("线程 1 已将 flag 设置为 true");});// 线程 2:不断检查 flag 的值Thread thread2 = new Thread(() -> {while (!flag) {// 循环等待}System.out.println("线程 2 检测到 flag 变为 true,退出循环");});// 启动线程thread2.start();thread1.start();try {// 等待两个线程执行完毕thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
作用保证可见性,禁止指令重排序,但是不能保证线程安全!
volatile
不能保证线程安全的原因
虽然 volatile
能保证变量的可见性和禁止指令重排序,但它不能保证复合操作的原子性。原子操作是指不可分割的操作,要么全部执行,要么全部不执行。像常见的 i++
操作,它实际上包含了读取、加 1 和写入三个步骤,并非原子操作。
AQS使用
(写锁)独占锁也称为排他锁,是一种一次只能被一个线程持有的锁。当一个线程获取了独占锁后,其他线程若想获取该锁,就必须等待持有锁的线程释放锁,否则会被阻塞。
(读锁)共享锁是一种可以被多个线程同时持有的锁。当一个线程获取了共享锁后,其他线程也可以获取该共享锁,但如果有线程想要获取独占锁,则必须等待所有持有共享锁的线程释放锁。
自定义独占锁
package org.example;import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Lock;// 自定义独占锁类,实现 Lock 接口
class CustomExclusiveLock implements Lock {// 内部类 Sync 继承自 AQSprivate static class Sync extends AbstractQueuedSynchronizer {// 判断当前是否处于锁定状态,即 state 是否为 1@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}// 尝试获取锁,使用 CAS 操作将 state 从 0 变为 1@Overrideprotected boolean tryAcquire(int acquires) {assert acquires == 1;if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}// 尝试释放锁,将 state 置为 0@Overrideprotected boolean tryRelease(int releases) {assert releases == 1;if (getState() == 0) {throw new IllegalMonitorStateException();}setExclusiveOwnerThread(null);setState(0);return true;}}private final Sync sync = new Sync();// 加锁操作,调用 AQS 的 acquire 方法@Overridepublic void lock() {sync.acquire(1);}// 可中断的加锁操作,调用 AQS 的 acquireInterruptibly 方法@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}// 尝试加锁,调用 AQS 的 tryAcquire 方法@Overridepublic boolean tryLock() {return sync.tryAcquire(1);}// 在指定时间内尝试加锁,调用 AQS 的 tryAcquireNanos 方法@Overridepublic boolean tryLock(long time, java.util.concurrent.TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(time));}// 解锁操作,调用 AQS 的 release 方法@Overridepublic void unlock() {sync.release(1);}// 获取条件对象@Overridepublic java.util.concurrent.locks.Condition newCondition() {return null;}
}// 测试自定义独占锁
public class CustomExclusiveLockTest {public static void main(String[] args) {CustomExclusiveLock lock = new CustomExclusiveLock();// 创建一个线程Thread thread = new Thread(() -> {lock.lock();try {System.out.println("Thread acquired the lock.");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();System.out.println("Thread released the lock.");}});thread.start();}
}
自定义共享锁
import java.util.concurrent.locks.AbstractQueuedSynchronizer;// 自定义共享锁类
class CustomSharedLock {// 内部类 Sync 继承自 AQSprivate static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {setState(permits);}// 获取共享资源@Overrideprotected int tryAcquireShared(int acquires) {for (; ; ) {int current = getState();int newState = current - acquires;if (newState < 0 || compareAndSetState(current, newState)) {return newState;}}}// 释放共享资源@Overrideprotected boolean tryReleaseShared(int releases) {for (; ; ) {int current = getState();int newState = current + releases;if (compareAndSetState(current, newState)) {return true;}}}}private final Sync sync;public CustomSharedLock(int permits) {this.sync = new Sync(permits);}// 获取共享锁public void acquireShared() {sync.acquireShared(1);}// 释放共享锁public void releaseShared() {sync.releaseShared(1);}
}// 测试自定义共享锁
public class CustomSharedLockTest {public static void main(String[] args) {CustomSharedLock sharedLock = new CustomSharedLock(2);// 创建线程 1Thread thread1 = new Thread(() -> {sharedLock.acquireShared();try {System.out.println("Thread 1 acquired the shared lock.");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {sharedLock.releaseShared();System.out.println("Thread 1 released the shared lock.");}});// 创建线程 2Thread thread2 = new Thread(() -> {sharedLock.acquireShared();try {System.out.println("Thread 2 acquired the shared lock.");Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();} finally {sharedLock.releaseShared();System.out.println("Thread 2 released the shared lock.");}});thread1.start();thread2.start();}
}