1.进程与线程/并行与并发
进程:就是一个正在运行的程序
线程:就是进程内的多条执行路径,一个进程内有多个线程。
并行:多核cpu下,每个核心都可以运行线程。同一时间动手做多件事情的能力。
并发:线程轮流使用cpu,同一时间应对多件事情的能力。
-
同步:需要等待结果返回才能继续向下运行
-
异步:不需要等待结果返回,就能继续向下运行。
2.创建线程
//继承Thread类,匿名内部类的写法
Thread t = new Thread("t1") {
@Override
public void run() {
log.info("666");
}
};
t.start();
//实现Runnable接口,lambda表达式的写法
new Thread(()->{
System.out.println("666");
},"t2").start();
//FutureTask异步任务,lambda表达式的写法,Callable函数式接口,有返回值和异常抛出。
FutureTask<Integer> task = new FutureTask<Integer>(()->{
return 100;
});
new Thread(task,"t3").start();
3、栈与栈帧
每个线程启动,虚拟机会为其分配线程工作栈内存。每个工作栈内存由多个栈帧组成,对应着每次方法调用时所占用的内存。每个线程只有一个活动栈帧,对应着当前正在执行的方法。
4、sleep
- 调用sleep方法会让当前线程从RUNNABLE运行状态变为TIMED_WAITING阻塞状态。
- 其他线程可以调用interrupt()方法,打断正在睡眠的线程,这时sleep方法会抛出InterruptedException。睡眠的线程将被唤醒。
- 但是睡眠结束后的线程未必会立即得到执行,而是进入RUNNABLE就绪状态,等待CPU的调度执行。
- TimeUnit替代sleep得到更好的可读性。
TimeUnit.MILLISECONDS.sleep(500);
- sleep防止cpu占用100%,让出cpu的执行权,不让死循环占用太多资源。
public class CpuSleepTest {
public static void main(String[] args) throws InterruptedException {
while (true){
Thread.sleep(1);
System.out.println(new Date());
}
}
}
5、yield
- 礼让,会让当前线程从运行状态变为就绪状态,等待CPU的调度执行,又有可能获得CPU的执行权。
- 具体的实现依赖于操作系统的任务调度器。
6、PRIORITY线程优先级
- 优先级会提示调度器优先执行该线程,但仅仅只是一个提示,调度器可能忽略这个提示。
- 如果CPU比较忙,优先级较高的线程会获得更多的时间片,CPU空闲时,线程的优先级几乎没有作用。
/**
* The minimum priority that a thread can have.
*/
public final static int MIN_PRIORITY = 1;
/**
* The default priority that is assigned to a thread.
*/
public final static int NORM_PRIORITY = 5;
/**
* The maximum priority that a thread can have.
*/
public final static int MAX_PRIORITY = 10;
public class YieldPriorotyTest {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
int count = 0;
for (; ; ) {
System.out.println("t1--"+count++);
}
});
Thread t2 = new Thread(() -> {
int count = 0;
for (; ; ) {
//Thread.yield();//礼让
System.out.println(" t2--"+count++);
}
});
t2.start();
t1.start();
//设置线程优先级
t2.setPriority(Thread.MAX_PRIORITY);
t1.setPriority(Thread.MIN_PRIORITY);
}
}
7、join插队
等待调用join()方法的线程结束
package new2023.juc;
import java.util.concurrent.TimeUnit;
/**
* @Author zhangxuhui
* @Date 2023/3/17
* @email zxh_1633@163.com
*/
public class JoinTest {
static int s = 0;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
s = 10;
System.out.println("t is over");
},"t");
t.start();
t.join();//插队,等待t线程结束。
System.out.println(s);
}
}
8、inturrupt打断线程
- 打断阻塞状态的线程(sleep,wait,join),会重置打断标记。
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/3/17
* @email zxh_1633@163.com
*/
public class InterruptTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
System.out.println("sleep....");
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread is over");
});
t.start();
Thread.sleep(1000);
System.out.println("interrupt...");
t.interrupt();
System.out.println("打断标记:"+t.isInterrupted());//false
}
}
- 打断正常运行的线程,判断打断标识可以优雅的停止线程。
package new2023.juc;
import java.util.concurrent.TimeUnit;
/**
* @Author zhangxuhui
* @Date 2023/3/17
* @email zxh_1633@163.com
*/
public class InterruptRunningThread {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
while(true){
boolean interrupted = Thread.currentThread().isInterrupted();
System.out.println(interrupted);
if(interrupted){
System.out.println("thread is inturrupted : stop");
break;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
t.interrupt();
System.out.println("main thread is stop");
}
}
- 两阶段终止模式(two phase termination):在一个线程t1中如何优雅的终止另一个线程t2,优雅是指给t2一个料理后事的机会。
package new2023.juc;
import java.util.concurrent.TimeUnit;
/**
* @Author zhangxuhui
* @Date 2023/3/17
* @email zxh_1633@163.com
*/
public class TwoPhaseTermination {
public static void main(String[] args) throws InterruptedException {
Phase p = new Phase();
p.start();
TimeUnit.SECONDS.sleep(3);
p.stop();
System.out.println("等待重启...");
TimeUnit.SECONDS.sleep(5);
p.restart();
}
}
class Phase{
private Thread monitor;
public void start(){
monitor = new Thread(()->{
while (true){
Thread current = Thread.currentThread();
//current.isInterrupted();不会清除打断标记
//Thread.interrupted();会清除打断标记
if(current.isInterrupted()){
System.out.println("线程被打断,料理后事");
break;
}
try {
Thread.sleep(1000);
System.out.println("系统监控");
} catch (InterruptedException e) {
e.printStackTrace();
current.interrupt();
}
}
});
monitor.start();
}
public void stop(){
monitor.interrupt();
}
public void restart(){
start();
}
}
- 打断park线程,使其继续执行,在打断标记为true时,park将失效。
package new2023.juc;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;
/**
* @Author zhangxuhui
* @Date 2023/3/18
* @email zxh_1633@163.com
*/
public class InterruptPark {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(()->{
System.out.println("park");
LockSupport.park();
System.out.println("unpark");
// System.out.println(Thread.currentThread().isInterrupted());
System.out.println(Thread.interrupted());//获取打断标记并清除
System.out.println("park two");
LockSupport.park();//打断标记为true时失效
},"t");
t.start();
TimeUnit.SECONDS.sleep(2);
t.interrupt();
}
}
9、守护线程
默认情况下,Java进程需要等待所有的线程结束才会结束。有一种特殊的线程叫守护线程,只要其他非守护线程运行结束了,即使守护线程代码没有运行结束也会强行结束。垃圾回收器线程就是守护线程。
package new2023.juc;
import java.util.concurrent.TimeUnit;
/**
* @Author zhangxuhui
* @Date 2023/3/18
* @email zxh_1633@163.com
*/
public class SetThreadDeamon {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (true){
System.out.println("running...");
}
});
thread.setDaemon(true);
thread.start();
TimeUnit.MILLISECONDS.sleep(10);
System.out.println("thread main over");
}
}
10、线程的状态
操作系统层面的五种状态:
- 初始状态(新建状态):即线程被创建new Thread()。
- 可运行状态(就绪状态):线程被启动,但是未得到CPU的时间片,没有执行。
- 运行状态:线程得到了CPU的时间片,线程正在执行。
- 阻塞状态:调用阻塞api例如:sleep,wait,线程进入阻塞状态。
- 终止状态:线程运行结束,生命周期结束。
java层面的六种状态:
11、线程应用统筹规划
- 解法一:join插队
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/3/18
* @email zxh_1633@163.com
*/
public class TeaThread {
public static void main(String[] args) {
Thread t1 = new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("小王 洗水壶");
Thread.sleep(5000);
System.out.println("小王 烧开水");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"小王");
Thread t2 = new Thread(()->{
try {
Thread.sleep(1000);
System.out.println("大王 洗茶壶");
Thread.sleep(1000);
System.out.println("大王 洗茶杯");
Thread.sleep(1000);
System.out.println("大王 拿茶叶");
t1.join();
System.out.println("大王 泡茶");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"大王");
t1.start();
t2.start();
}
}
- 解法二:谁泡茶都可以?怎么实现?
- 解法三:小王工作完将开水交给大王,怎么实现?
12 、多线程问题
临界区:代码块内存在对共享资源的多线程读写。
- 多线程读共享资源,其实没有问题。
- 多线程对共享资源的读写时发生指令交错,就会出现问题。
竞态条件:多线程在临界区内执行,由于代码的执行序列不同而导致结果无法预测,称之为发生了竞态条件。
13、 同步解决方案
- 阻塞式的解决方案:synchronized/lock
- 非阻塞式的解决方案:原子变量
synchronized
采用的互斥的方式让同一时刻,至多只有一个线程能持有对象锁,其他线程再想获取这个对象锁时就会阻塞住,这样就能保证拥有锁的线程可以安全的执行临界区内的代码,而不用担心线程的上下文切换导致线程同步问题的出现。
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/3/19
* @email zxh_1633@163.com
*/
public class synchronizedTest {
static int count=0;
public static void main(String[] args)throws Exception {
Object lock = new Object();
Thread t1 = new Thread(() -> {
for (int i = 0 ; i < 5000 ;i++){
synchronized(lock){
count++;
}
}
});
Thread t2 = new Thread(() -> {
for (int i = 0 ; i < 5000 ;i++){
synchronized (lock) {
count--;
}
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
- 面向对象改造
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/3/19
* @email zxh_1633@163.com
*/
public class OOMSynchronized {
public static void main(String[] args) throws Exception{
Room room = new Room();
Thread t1 = new Thread(() -> {
for (int i = 0 ; i < 5000 ;i++){
room.incr();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0 ; i < 5000 ;i++){
room.decr();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(room.getCount());
}
}
class Room{
private int count = 0;
public void incr(){
synchronized(this){
count++;
}
}
public void decr(){
synchronized (this){
count--;
}
}
public int getCount(){
synchronized (this){
return count;
}
}
}
- 对象锁
class test{
public void t1(){
synchronized (this){
}
}
//等价
public synchronized void t2(){
}
}
- 类锁
class test2{
public synchronized static void t(){
}
//等价
public void t2(){
synchronized (test2.class){
}
}
}
- 成员变量多线程读写存在安全问题,局部变量为引用类型时,也可能存在线程安全问题。
- 父类为了不让子类重写线程安全的方法,而造成线程安全问题,将方法声明为priavet或final类型,不让子类重写。
- 常见线程安全的类的方法是线程安全的,但是这些方法组合起来并不一定是线程安全的。
- 没有成员变量的类是线程安全的
- 转账练习
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/3/21
* @email zxh_1633@163.com
*/
public class AcountTest {
public static void main(String[] args) throws InterruptedException {
Counter a = new Counter(1000);
Counter b = new Counter(1000);
Thread t1 = new Thread(()->{
for (int i = 0; i < 1000; i++) {
a.transfer(b,1);
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 1000; i++) {
b.transfer(a,1);
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(a.getCount()+ b.getCount());
}
}
class Counter{
private int count;
Counter(int count){
this.count=count;
}
public int getCount(){
return this.count;
}
public void setCount(int count){
this.count = count;
}
public void transfer(Counter target,int money){
//类锁,因为this和target为不同的对象,不能用对象锁。
//他们共同的对象是Counter.class类对象。
synchronized (Counter.class) {
if (this.count >= money) {
this.setCount(this.count - money);
target.setCount(target.count + money);
}
}
}
}
14 、Monitor监视器/管程
- 对象头
- 每个Java对象都可以关联一个Monitor对象(由操作系统提供),如果使用synchronized(重量级锁)给对象上锁后,该对象的对象头中的mark word就被设置为执行Monitor对象的指针。
- 刚开始monitor中的owner为null
- 当thread-2执行零界区代码时,将obj对象的对象头的mark word设置为执行Monitor对象的指针。monitor的owner设置指向thread-2。
- 在thread-2上锁期间,其他线程执行零界区代码,将进入entry list阻塞队列。
- thread-2执行完同步代码后,不再指向owner,并且唤醒阻塞的线程,重新竞争锁执行,这种锁竞争是非公平的。
- wait set中的线程是之前获得过锁,但条件不满足进入waitting状态的线程。
注意:只要在同步代码块内,都会正常的加锁及解锁,不会出现死锁的现象。当同步代码块中出现异常时将自动解锁。
15、synchronized原理
- 轻量级锁(lock record)
如果一个对象虽然有多线程访问,但多线程访问的时间的错开的(没有竞争),那么可以使用轻量级锁进行优化。轻量级锁对使用者是透明的,语法仍然是synchronized。 - 锁膨胀
如果在尝试加轻量级锁的过程中,CAS操作无法成功,这是一种情况就是有其他线程为此对象加上了轻量级锁(锁竞争),这时需要进行锁膨胀,将轻量级锁升级为重量级锁。 - 自旋优化
重量级锁竞争的时候,如果当前线程自旋成功(即持锁线程已经退出了同步块,释放了锁。),这时当前线程就可以避免阻塞。 - 偏向锁
轻量级锁在没有竞争时(就自己单个线程),每次重入都需执行CAS操作。1.6加入偏向锁进行优化,只有第一次加锁时进行CAS,后续重入锁无需CAS操作。将线程id存放在对象头中,加锁时先判断对象头中的线程是不是自己。
16、wait-notify
- wait 让线程等待
- notify 唤醒wait set中的其中一个
- notifyAll 唤醒wait set 中的全部
都是object对象中的方法,必须获得对象的锁才能调用这些方法。即必须在同步方法或代码块中才能调用。
线程内不能使用if判断一次,应该使用while进行循环判断。
17、保护性暂停
有一个结果需要从一个线程传递到另一个线程,让他们关联同一个guardedobject。jdk中join,future的实现就是采用此模式。
package new2023.保护性暂停;
/**
* @Author zhangxuhui
* @Date 2023/4/5
* @email zxh_1633@163.com
* 线程传递结果的中间对象
*/
public class GuardeObject {
private Object response;
//get response by timeOut
public Object get(long timeOut){
synchronized (this){
long start = System.currentTimeMillis();
long con = 0;
while (response == null){
long waitTime = timeOut - con;
if (waitTime <= 0){
break;
}
try {
this.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
con = System.currentTimeMillis() - start;
}
return response;
}
}
//get response
public Object get(){
synchronized (this){
while (response == null){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return response;
}
}
//set response
public void complete(Object o){
synchronized (this){
this.response = o;
this.notifyAll();
}
}
}
注意:使用第三个类futures进行消息的生产者和消息的消费者之间的解耦,futures类包含多个消息处理的类,使用id进行一一对应,使用后注意将消息处理的类进行销毁,防止内存溢出。
18、生产者消费者模式
- 消费队列可以平衡消费和生产的线程资源
- 生产者只产生消息,消费者只消费消息,二者解耦互不干扰。
- 消息队列的容量时有限的,满时不会再添加数据,空时不会再消费数据。
- JDK中的阻塞队列就是依据此模式实现
package new2023.生产者消费者模式;
import java.util.LinkedList;
/**
* @Author zhangxuhui
* @Date 2023/4/5
* @email zxh_1633@163.com
* 消息队列
*/
public class MessageQuene {
private LinkedList<Message> list = new LinkedList<>();
private int cap;
public MessageQuene(int cap) {
this.cap = cap;
}
public Message take(){
synchronized (list){
while (list.isEmpty()){
try {
System.out.println(Thread.currentThread().getName()+"队列为空,没有消息。");
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Message last = list.removeLast();
System.out.println(Thread.currentThread().getName()+"消费消息:"+last);
list.notifyAll();
return last;
}
}
public void put(Message msg){
synchronized (list){
while (list.size() == cap){
try {
System.out.println(Thread.currentThread().getName()+"队列已满");
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
list.addFirst(msg);
System.out.println(Thread.currentThread().getName()+"生产消息:"+msg);
list.notifyAll();
}
}
}
package new2023.生产者消费者模式;
/**
* @Author zhangxuhui
* @Date 2023/4/5
* @email zxh_1633@163.com
*/
public class TestMessage {
public static void main(String[] args) {
MessageQuene quene = new MessageQuene(2);
for (int i = 0; i < 3; i++) {
int id = i;
new Thread(()->{
quene.put(new Message(id,"msg"+id));
}).start();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(()->{
for (;;){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
quene.take();
}
}).start();
}
}
19、park/unpark
原理
- 每个线程都关联一个Parker对象,由三部分组成_counter,_cond,_mutex.
- park检查通行证,unpark发放一张通行证。
- 先park后unpark
- 先unpark后park
20、多把锁活跃性
- 死锁:一个线程需要同时获取多把锁,这时就容易发生死锁。使用jstack或jconsole工具进行死锁检测。
- 活锁:两个线程相互改变对方的结束条件,导致两个线程都无法结束。
- 饥饿:一个线程由于优先级太低,始终得不到cpu调度执行,也不能结束。
package new2023.juc;
/**
* @Author zhangxuhui
* @Date 2023/4/6
* @email zxh_1633@163.com
* 实际开发中应避免死锁的产生
*/
public class ThreadDeadLock {
public static void main(String[] args) {
Object locka = new Object();
Object lockb = new Object();
new Thread(()->{
synchronized (locka){
System.out.println("locka--" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockb){
System.out.println("lockb--"+Thread.currentThread().getName());
}
}
},"t1").start();
new Thread(()->{
synchronized (lockb){
System.out.println("lockb--" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (locka){
System.out.println("locka--"+Thread.currentThread().getName());
}
}
},"t2").start();
}
}
21、可重入锁ReentrantLock
相对于synchronized对比具有以下特点:
- 可中断 lockInterruptibly()
- 可是设置超时时间 tryLock()
- 可以设置为公平锁,默认为不公平锁。
- 支持多个条件变量
- 同synchronized一样支持可重入
可重入:同一个线程如果首次获得了这把锁,就有权利再次获得这把锁,如果不可重入则自己也会被锁住,导致程序无法继续执行。
package new2023.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author zhangxuhui
* @Date 2023/4/7
* @email zxh_1633@163.com
* await前先获得锁
* await执行后释放锁,进入condition对象等待。
* await被唤醒/打断/超时后会重新竞争锁
* 竞争锁成功后将继续执行
*/
public class ReentantLockCondationTest {
static ReentrantLock lock = new ReentrantLock();
static Condition room = lock.newCondition();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
lock.lock();
try {
System.out.println("go into room");
try {
room.await();
System.out.println("run..");
} catch (InterruptedException e) {
e.printStackTrace();
}
}finally {
lock.unlock();
}
}, "t1");
t1.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.lock();
try {
System.out.println("唤醒。。。");
room.signal();
}finally {
lock.unlock();
}
}
}
22、线程顺序控制
package new2023.juc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author zhangxuhui
* @Date 2023/4/8
* @email zxh_1633@163.com
*/
public class ControlPrintNumReentrantLock {
public static void main(String[] args) throws InterruptedException {
awaitSinglePrint asp = new awaitSinglePrint();
Condition a = asp.newCondition();
Condition b = asp.newCondition();
Condition c = asp.newCondition();
new Thread(()->{
asp.print("a",a,b);
}).start();
new Thread(()->{
asp.print("b",b,c);
}).start();
new Thread(()->{
asp.print("c",c,a);
}).start();
Thread.sleep(1000);
asp.lock();
try {
a.signal();
}finally {
asp.unlock();
}
}
}
class awaitSinglePrint extends ReentrantLock{
private int loopNum = 5;
public void print(String msg,Condition current,Condition next){
lock();
try {
for (int i = 0; i < loopNum; i++) {
try {
current.await();
System.out.print(msg);
next.signal();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}finally {
unlock();
}
}
}
22、Java内存模型
- volatile易变关键字:可以用来修饰成员变量,避免线程在自己的工作内存中获取变量的值,直接操作主内存中的变量。synchronized代码块即可以保证原子性又可以保证可见性,但是性能较低。
- balking犹豫模式,一个线程发现另一个线程或本线程已经做了某件事情,将不再继续直接结束线程返回。
- JVM会在不影响执行结果的前提下,优化代码的执行顺序。
package new2023.内存模型;
/**
* @Author zhangxuhui
* @Date 2023/4/8
* @email zxh_1633@163.com
*/
public class BlakingTest {
static volatile boolean flag = false;
public static void main(String[] args) {
Thread thread = new Thread(() -> {
synchronized (BlakingTest.class) {
if (flag) {
System.out.println("stop");
return;
}
flag = true;
System.out.println("start");
}
});
flag = true;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.start();
}
}
volatile原理:不能保证原子性,可见性及有序性只在单个线程内,多个线程中共享变量的读写由cpu的调度决定,不能保证读到的数据就是最新的数据。
23、DCL(双检锁)
package new2023.内存模型;
/**
* @Author zhangxuhui
* @Date 2023/4/8
* @email zxh_1633@163.com
*/
public class DoubleCheckingLock {
public static void main(String[] args) {
for (int x = 0 ; x < 1000; x++){
new Thread(()->{
System.out.println(DCL.getIns());
}).start();
}
}
}
class DCL{
private static volatile DCL ins = null;
private DCL(){}
public static DCL getIns(){
if(ins == null){
synchronized (DCL.class){
if(ins == null){
ins = new DCL();
}
}
}
return ins;
}
@Override
public String toString() {
return "DCL " + this.hashCode();
}
}
24、happens-before规则
25、CAS比较并交换
package juc;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
*/
public class AtomicIntergerTest {
public static void main(String[] args) {
AtomicInteger ai = new AtomicInteger(100);
for (int x = 0;x < 10;x++){
new Thread(()->{
while (true){
int i = ai.get();
int update = i - 10;
if(ai.compareAndSet(i,update)){
System.out.println(Thread.currentThread().getName()+"将:"+i+"更新为:"+update);
break;
}
}
}).start();
}
try {
TimeUnit.SECONDS.sleep(1);
System.out.println("最后结果为:"+ai.get());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 原子整型:AtomicInteger、AtomicLong、AtomicBoolean
- 原子引用:AtomicReference、AtomicMarkableReference、AtomicReferenceFieldUpdater、AtomicStampedReference(存在aba问题)
- 原子数组:AtomicIntegerArray\AtomicLongArray\AtomicReferenceArray
- 字段更新器:AtomicReferenceFieldUpdater\AtomicIntegerFieldUpdater\AtomicLongFieldUpdater
- 原子累加器:LongAdder\LongAccumulator\DoubleAdder\DoubleAccumulator,性能比原子整型好,因为会在竞争时设置多个累加单元,最后将结果汇总,减少CAS重试失败,从而提高性能。
package juc;
import java.math.BigDecimal;
import java.util.concurrent.atomic.AtomicReference;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
*/
public class AtomicReferenceTest {
public static void main(String[] args) {
BigDecimal bigDecimal = new BigDecimal(100);
AtomicReference<BigDecimal> arf = new AtomicReference(bigDecimal);
System.out.println(arf.get());
if(arf.compareAndSet(bigDecimal,BigDecimal.valueOf(90))){
System.out.println(arf.get());
}
}
}
package juc;
import java.util.concurrent.atomic.AtomicMarkableReference;
/**
* @Author zhangxuhui
* @Date 2022/8/1
* @email zxh_1633@163.com
*/
public class AtomicMarkableReferenceDemo {
public static void main(String[] args) {
//原子类+标记 默认为false,修改后设置为true。
AtomicMarkableReference<Integer> ar = new AtomicMarkableReference<>(100,false);
boolean marked = ar.isMarked();
System.out.println(marked);
ar.compareAndSet(100,200,marked,!marked);
System.out.println(ar.isMarked());
System.out.println(ar.getReference());
}
}
package new2023.juc;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
*/
public class AtomicArrayTest {
public static void main(String[] args) {
// AtomicReferenceArray
// AtomicLongArray
// AtomicIntegerArray
int [] arr = new int[10];
for (int x = 0 ; x < arr.length;x++){
new Thread(()->{
for (int y = 0 ; y < 10000;y++){
arr[y%arr.length] = arr[y%arr.length]+1;
}
}).start();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Arrays.toString(arr));
AtomicIntegerArray ata = new AtomicIntegerArray(10);
for (int x = 0 ; x < ata.length();x++){
new Thread(()->{
for (int y = 0 ; y < 10000;y++){
ata.getAndIncrement(y%ata.length());
}
}).start();
}
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(ata);
}
}
package new2023.juc;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
* 原子字段更新器
*/
public class AtomicFileUpdateTest {
public static void main(String[] args) {
// AtomicReferenceFieldUpdater
// AtomicIntegerFieldUpdater
// AtomicLongFieldUpdater
Student s = new Student();
AtomicReferenceFieldUpdater arfu = AtomicReferenceFieldUpdater.newUpdater(Student.class,String.class,"name");
arfu.compareAndSet(s,null,"zhangsan");
System.out.println(s);
}
}
class Student{
volatile String name;
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
package new2023.juc;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.LongAdder;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
*/
public class AtomicNumberAdder {
public static void main(String[] args) {
LongAdder la = new LongAdder();
CountDownLatch cdl = new CountDownLatch(10);
for (int x = 0 ; x < 10;x++){
new Thread(()->{
for(int y = 0 ; y < 20000000;y++){
la.increment();
}
cdl.countDown();
}).start();
}
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(la.intValue());
}
}
package new2023.juc;
import sun.misc.Unsafe;
import java.lang.reflect.Field;
/**
* @Author zhangxuhui
* @Date 2023/4/9
* @email zxh_1633@163.com
*/
public class UnsafeTest {
public static void main(String[] args)throws Exception {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
Unsafe unsafe = (Unsafe) theUnsafe.get(null);
Person p = new Person();
Field id = p.getClass().getDeclaredField("id");
Field name = p.getClass().getDeclaredField("name");
long idoffset = unsafe.objectFieldOffset(id);
long nameoffest = unsafe.objectFieldOffset(name);
unsafe.compareAndSwapInt(p,idoffset,0,1);
unsafe.compareAndSwapObject(p,nameoffest,null,"bob");
System.out.println(p);
}
}
class Person{
private volatile String name;
private volatile int id;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
26、不可变对象
- final的使用:修饰属性保证了该属性只能被读取,不能被修改;修饰类保证了该类不能被继承,方法不能被重写,防止子类无意间破坏不可变性。
- 保护性拷贝:创建副本对象来避免共享的手段。
- 享元模式:需要重用数量有限的同一类对像时。
- 无状态:因为成员变量保存的数据也可以称为状态信息,因此没有成员变量称为无状态,没有成员变量的类线程安全。
//读取缓存-128 - 127
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//String类,不可变类。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
}