在Java
中,线程是一个重点,JUC
是java.util.concurrent
工具包的简称,这是一个处理线程的工具包,JDK 1.5
开始出现。
-
进程(
Process
)是系统进行资源分配和调度的基本单位,是操作系统结构的基础,进程是线程的容器。 -
线程(
thread
) 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
小结:进程是系统资源分配的最小单位,线程是程序执行的最小单位。
1.2 wait()/sleep()的区别sleep()
是Thread
的静态方法,而wait()
是Object
的方法,任何对象实例都能调用sleep()
不会释放锁,wait()
会释放锁,但调用它的前提是当前线程占有锁,即代码要在synchronized
中- 它们都可以被
interrupted
方法中断
-
串行表示所有任务都一一按先后顺序进行,串行是一次只能取得一个任务,然后执行这个任务
-
并行意味着可以同时取得多个任务,并同时去执行所取得的这些任务。并行模式相当于将长长的一条队列,划分成了多条短队列,所以并行缩短了任务队列的长度。并行的效率从代码层次上强依赖于多进程/多线程代码,从硬件角度上则依赖于多核CPU。
并发(concurrent
)指的是多个程序可以同时运行的现象,并发描述的是多进程同时运行的现象。
要解决大并发问题,通常是将大任务分解成多个小任务,由于操作系统对进程的调度是随机的,所以切分成多个小任务后,可能会从任一小任务处执行,这可能会出现一些现象:
- 可能出现一个小任务执行了多次,还没开始下个任务的情况。这时一般会采用队列或类似的数据结构来存放各个小任务的成果
- 可能出现还没准备好第一步就执行第二步的可能。这时,一般采用多路复用或异步的方式,比如只有准备好产生了事件通知才执行某个任务
- 可以多进程/多线程的方式并行执行这些小任务。也可以单进程/单线程执行这些小任务,这时很可能要配合多路复用才能达到较高的效率
并发:同一时刻多个线程在访问同一个资源,多个线程对一个点 并行:多项工作一起执行,之后再汇总
并发产生的条件:
- 多线程
- 共享变量
- 原子操作
数据一致性:
- 原子性
- 一致性
- 有序性:指令重排
用户线程:平时用到的普通线程,自定义线程 守护线程:运行在后台,是一种特殊的线程,比如垃圾回收 当主线程结束后,如果用户线程还在运行,那么JVM
存活,如果没有用户线程,都是守护线程,则JVM
结束
public static void main(String[] args) {
Thread aa = new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().isDaemon());
while (true) {
}
}, "aa");
//设置守护线程
aa.setDaemon(true);
aa.start();
System.out.println(Thread.currentThread().getName()+" over");
}
注:守护线程随着用户线程的结束而结束。可以在有较长耗时操作时,设置为守护线程进行。
2、Lock接口 2.1 synchronizedsynchronized
是Java
中的关键字,是一种同步锁,它修饰的对象有以下几种:
- 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象
- 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象
- 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象
- 修改一个类,其作用的范围是
synchronized
后面括号括起来的部分,作用的对象是这个类的所有对象
虽然可以使用synchronized
来定义方法,但synchronized
并不属于方法定义的一部分,因此,synchronized
关键字不能被继承。
如果在父类中的某个方法使用了synchronized
关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步,而必须显式地在子类的这个方法中加上synchronized
关键字才可以。
当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。
如果一个代码块被synchronized
修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里取得锁的线程释放锁只会有两种情况:
- 获取锁的线程执行完了该代码块,然后线程释放对锁的占有
- 线程执行发生异常,此时
JVM
会让线程自动释放锁
那么如果这个取得锁的线程由于要等待IO或者其他原因(比如调用sleep()
方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过Lock
就可以办到
多线程编程步骤:
-
第一步,创建资源类,在资源类创建属性和操作方法(多个线程抢占资源)
-
第二步,创建多个线程,调用资源类的操作方法
场景:3个窗口卖30张票
//第一步 创建资源类,定义属性和和操作方法
class Ticket {
//票数
private int number = 30;
//操作方法:卖票
public synchronized void sale() {
//判断:是否有票
if(number > 0) {
System.out.println(Thread.currentThread().getName()+" : 卖出:"+(number--)+" 剩下:"+number);
}
}
}
public class SaleTicket {
//第二步 创建多个线程,调用资源类的操作方法
public static void main(String[] args) {
// 创建共享资源Ticket对象
Ticket ticket = new Ticket();
// 创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
//调用卖票方法
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?