Java高并发知识点
1,基础概念
-
同步(Synchronous)与异步(Asynchronous)
-
同步方法调用一旦开始,调用者必须等待方法调用返回后才能继续执行后续动作。
-
异步方法调用类似消息传递,一旦开始,方法调用会立刻返回,调用者就可以继续执行后续动作。
-
-
并发(Concurrency)与并行(Parallilism)
-
并发说的是在同一个时间内,多件事情在这个时间段内交替执行。
-
并行说的是多件事情在同一时间内同时发生
-
-
临界区
临界区表示一种公共资源或者共享资源,可以被多个线程使用,但是每一次只能有一个线程使用它,一旦临界区资源被线程占用,其他线程要想使用这个资源必须等待。
-
阻塞与非阻塞
- 阻塞:描述的是多线程之间的相互影响,比如一个线程占用了共享资源,那么其他线程想使用这个共享资源,就必须等待,直到占用共享资源的线程释放,这样的现象就是阻塞
- 非阻塞:当共享资源没有线程占用的时候,所有的线程都可以尝试占用该共享资源
-
死锁,饥饿,活锁
- 死锁:两个线程相互等待获取对方持有的锁
- 饥饿:有的线程由于优先级等各种原因,导致一直无法获取锁的一种状态
- 活锁:两个线程在获取锁的过程种相互谦让,导致锁在两个线程之间出现跳锁状态
-
后续
2,JMM(java内存模型)相关的一些概念
-
原子性
-
原子性是指操作是不可分的,要么全部执行,要么不执行
-
demo
比如:a++,实际上操作有下面三个步骤: 1.读取变量a的值,假如a=1 2.a的值+1,为2 3.将2值赋值给变量a,此时a的值应该为2 所以必须保证这3个操作是原子性的,在操作a++的过程中,其他线程不会改变a的值,如果在上面的过程中出现其他线程修改了a的值,在满足原子性的原则下,上面的操作应该失败。 -
java实现原子操作的方法大致有两种:锁机制,无锁CAS机制
-
-
可见性
-
可见性:一个线程对共享变量的修改,对应另外一个线程来说是否可以看到。
-
java内存模型结构如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sxu78GYh-1674989552682)(C:\Users\zwx1144424\AppData\Roaming\Typora\typora-user-images\image-20230106155822265.png)]
-
我们定义的所有变量都是存储在主内存中
-
每个线程都有一个自己的工作内存,保存的是该线程使用到的变量副本(主内存中该变量的一份拷贝)
-
线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写(不能越级)
-
不同线程之间也无法直接访问其他线程的工作内存中的变量,线程之间变量的值需要通过主内存来进行传递(同级不能相互访问)
-
**共享变量可见性的实现原理:**线程A在自己工作内存中修改变量之后,需要将变量刷新到主内存中,线程B在使用变量之前需要先将主内存中的变量值更新到自己的工作内存中
-
线程可见性的控制常用volatile,synchronized等实现
-
-
有序性
-
有序性:指的是程序按照代码的顺序执行
-
但是编译器和处理器进行指令重排之后,执行的顺序就会发生改变。
-
同一个线程执行指令重排后的代码,结果不会收到影响,但是当多线程同时访问一段指令重排后的代码,代码执行后的结果就可能发送改变
-
单例模式下双重校验模式的方式,如下代码:
public class Singleton { static Singleton instance; static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
-
-
后续
3,线程与进程
- 进程:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。
- 进程的特质
- 动态性:进程是程序的一次执行过程,是临时的,有生命期的,是动态产生,动态消亡的
- 并发性:任何进程都可以同其他进行一起并发执行
- 独立性:进程是系统进行资源分配和调度的一个独立单位
- 结构性:进程由程序,数据和进程控制块三部分组成
- 线程:是程序执行的最小单元,使用多线程而不是多进程去进行并发程序的设计,是因为线程间的切换和调度的成本远远小于进程。
- 线程的几个状态介绍
- New:表示刚刚创建的线程,这种线程还没有开始执行
- RUNNABLE:运行状态,线程的start()方法调用后,线程会处于这种状态
- BLOCKED:阻塞状态。当线程在执行的过程中遇到了synchronized同步块,但这个同步块被其他线程已获取还未释放时,当前线程将进入阻塞状态,会暂停执行,直到获取到锁。当线程获取到锁之后,又会进入到运行状态(RUNNABLE)
- WAITING:等待状态。和TIMEWAITING都表示等待状态,区别是WAITING会进入一个无时间限制的等,而TIMEWAITING会进入一个有限的时间等待,那么等待的线程究竟在等什么呢?一般来说,WAITING的线程正式在等待一些特殊的事件,比如,通过wait()方法等待的线程在等待notify()方法,而通过join()方法等待的线程则会等待目标线程的终止。一旦等到期望的事件,线程就会再次进入RUNNABLE运行状态。
- TERMINATED:表示结束状态,线程执行完毕之后进入结束状态。
- 线程的所有状态在java.lang.Thread中的State枚举中定义。
- 后续
4,线程的基本操作
1,新建线程
1,创建线程方式一:继承Thread类
public static class T1 extends Thread {
@Override
public synchronized void run() {
System.out.println(Thread.currentThread().getName() + "线程准备执行!");
System.out.println("创建线程方式一:继承Thread类");
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程方式一:继承Thread类
T1 t1 = new T1();
t1.start();
Thread.sleep(100);
}
2,创建线程方式二:实现Runnable接口
public static class T2 implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程准备执行!");
System.out.println("创建线程方式二:实现Runnable接口");
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程方式二:实现Runnable接口
T2 runnable = new T2();
Thread t2 = new Thread(runnable);
t2.start();
}
3,创建线程方式三:实现Callable接口
public static class NewCallable implements Callable {
@Override
public Object call() throws Exception {
int sum = 0;
System.out.println(Thread.currentThread().getName() + "线程准备执行!");
System.out.println("创建线程方式三:实现Callable接口");
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
}
public static void main(String[] args) throws InterruptedException {
//创建线程方式三:实现Callable接口
NewCallable newCallable = new NewCallable();
FutureTask futureTask = new FutureTask(newCallable);
Thread thread = new Thread(futureTask);
thread.start();
Object sum = null;
try {
sum = futureTask.get();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("实现Callable接口输出返回值为:" + sum);
}
4,创建线程方式四:使用线程池方式实现
public static void main(String[] args) throws InterruptedException {
//创建线程方式四:使用线程池方式实现
T3 t3 = new T3();
ExecutorService threadPool = Executors.newFixedThreadPool(5);
threadPool.execute(t3);
}
public static class T3 implements Runnable {
int tickets = 10;
Object o = new Object();
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "线程准备执行!");
System.out.println("创建线程方式四:使用线程池方式实现");
while (true) {
synchronized (o) {
if (tickets > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "=" + tickets--);
}
if (tickets <= 0) {
break;
}
}
}
}
}
2,启动线程
start方法是启动一个线程,run方法只会在当前线程中串行的执行run方法中的代码。
3,终止线程
一般来说线程执行完毕就会结束,无需手动关闭。但是如果我们想关闭一个正在运行的线程,有什么方法呢?可以看一下Thread类中提供了一个stop()方法。
import java.util.concurrent.TimeUnit;
public class ThreadStop {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> {
System.out.println("thread start!");
boolean flag = true;
while (true) {
;
}
});
thread1.setName("test");
thread1.start();
TimeUnit.SECONDS.sleep(1);
thread1.stop();
System.out.println(thread1.getState());
TimeUnit.SECONDS.sleep(1);
System.out.println(thread1.getState());
}
}
运行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-smeflTUz-1674989552684)(C:\Users\zwx1144424\AppData\Roaming\Typora\typora-user-images\image-20230106171737316.png)]
4,中断线程
-
严格的说,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了!至于目标线程接收到通知之后如何处理,则完全由目标线程自己决定
-
Thread提供了3个与线程中断有关的方法
public void interrupt()//中断线程 public boolean isInterrupted()//判断线程是否被中断 public static boolean interrupted()//判断线程是否被中断,并清除当前中断状态 -
demo1:通过interrupt()和interrupted()方法结合终端线程
import java.util.concurrent.TimeUnit; public class ThreadInterupt { public static class T1 extends Thread { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "准备启动"); while (true) { if (interrupted()) { System.out.println("线程退出!!"); break; } } } } public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.setName("t1"); t1.start(); TimeUnit.SECONDS.sleep(1); t1.interrupt(); } } -
demo2:通过自定义标识 isStop来终止线程
import java.util.concurrent.TimeUnit; public class ThreadInterupt { static volatile boolean isStop = false; public static class T1 extends Thread { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "准备启动"); while (true) { if (isStop) { System.out.println("线程退出!!"); break; } } } } public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.setName("t1"); t1.start(); TimeUnit.SECONDS.sleep(1); isStop = true; } } -
demo3:通过自定义标识 isStop来终止线程(如果线程有sleep的情况)线程是无法中断
import java.util.concurrent.TimeUnit; public class ThreadInterupt { static volatile boolean isStop = false; public static class T1 extends Thread { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "准备启动"); while (true) { try { TimeUnit.SECONDS.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } if (isStop) { System.out.println("线程退出!!"); break; } } } } public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.setName("t1"); t1.start(); TimeUnit.SECONDS.sleep(1); isStop = true; } }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W07ofFHP-1674989552684)(C:\Users\zwx1144424\AppData\Roaming\Typora\typora-user-images\image-20230106174920120.png)]
-
demo4:当线程存在sleep的时候,只能使用interrupt()方法来中断线程
import java.util.concurrent.TimeUnit; public class ThreadInterupt { public static class T1 extends Thread { @Override public void run() { System.out.println("线程:" + Thread.currentThread().getName() + "准备启动"); while (true) { try { TimeUnit.SECONDS.sleep(1000); } catch (InterruptedException e) { this.interrupt();//sleep方法由于中断而抛出异常之后,线程的中断标志会被清除(置为false),所以在异常中需要执行this.interrupt()方法,将中断标志位置为true e.printStackTrace(); } if (interrupted()) { System.out.println("线程退出!!"); break; } } } } public static void main(String[] args) throws InterruptedException { T1 t1 = new T1(); t1.setName("t1"); t1.start(); TimeUnit.SECONDS.sleep(1); t1.interrupt(); } } -
注意:sleep方法由于中断而抛出异常之后,线程的中断标志会被清除(置为false),所以在异常中需要执行this.interrupt()方法,将中断标志位置为true
5,线程等待
1,wait()
当在一个对象实例上调用wait()方法后,当前线程就会在这个对象上等待。一直等到其他线程调用obj.notify()方法为止
2,notiy()
当object.notify()方法被调用时,它就会从这个队列中随机选择一个线程,并将其唤醒。
6,线程通知(线程唤醒)
1,suspend():线程的挂其
2,resume():线程继续执行
7,等待线程结束
1,join():等待线程执行完毕
8,出让cpu资源
2,yeild():让出cpu资源
5,线程组
-
我们可以把线程归属到某个线程组中,线程组可以包含多个线程以及线程组,线程和线程组组成了父子关系
-
创建线程的时候,可以给线程指定一个线程组,代码如下:
public static void main(String[] args) throws InterruptedException { ThreadGroup threadGroup = new ThreadGroup("thread-group-1"); Thread t1 = new Thread(threadGroup, new R1(), "t1"); Thread t2 = new Thread(threadGroup, new R1(), "t2"); t1.

本文详细介绍了Java高并发编程的知识点,包括基础概念(同步、异步、并发与并行)、Java内存模型(JMM)、线程与进程、线程的创建与管理、线程安全、synchronized关键字、ReentrantLock重入锁、Condition对象、CountDownLatch用法、LockSupport工具类、CyclicBarrier循环栅栏以及Java线程池的实现原理和相关API。文章深入浅出地探讨了并发编程中的重要概念和实用技巧。

2万+

被折叠的 条评论
为什么被折叠?



