一、什么是线程?什么是进程
进程:
官方定义:
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础
简单理解:
进行(运行)中的程序,如打开任务管理器后中各种.exe程序
线程:
官方定义:
线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
简单理解:
线程是真正执行资源调度(使程序跑起来)的主体,一个进程往往可以包含多个线程,但至少包含一个线程。
如:开一个idea进程,其中至少有—> 线程1:输入代码,线程2:自动保存
Java真的可以开启线程吗?
答案是 : 不能。查看Thread.start()源码可得知:
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
// 本地方法,底层的C++ ,Java 无法直接操作硬件
private native void start0();
二、并发编程
2.1 并发编程:并发、并行
- 并发(多线程操作同一个资源) CPU 一核 ,模拟出来多条线程,天下武功,唯快不破,快速交替
- 并行(多个人一起行走) CPU 多核 ,多个线程可以同时执行; 线程池
通过java获取cpu的核数:
public class Test1 {
public static void main(String[] args) {
// 获取cpu的核数
// CPU 密集型,IO密集型
System.out.println(Runtime.getRuntime().availableProcessors());
//输出为8
//说明笔者电脑为八核处理器
}
}
2.2 线程的几个状态
public enum State {
// 新生
NEW,
// 运行
RUNNABLE,
// 阻塞
BLOCKED,
// 等待,死死地等
WAITING,
// 超时等待
TIMED_WAITING,
// 终止
TERMINATED;
}
2.3 wait与sleep的区别
//Object.wait()
public final void wait() throws InterruptedException {
wait(0);
}
//Thread.sleep()
public static native void sleep(long millis) throws InterruptedException;
来自不同的类
- wait() 来自 Object类
- sleep() 来自 Thread类
关于锁的释放
- wait() 会释放锁:wait是进入线程等待池等待,出让系统资源,其他线程可以占用CPU。
- sleep() 不出让系统资源;(简单来说,就是抱着锁睡觉)
是否需要捕获异常?
- 需要。源码都在上面写的死死的,throws InterruptedException 都不知网上随手一搜的博客说wait()
使用范围:
-
wait() 需要在同步代码块中使用
-
sleep()可以在任何地方睡
作用对象:
- wait()定义在Object类中,作用于对象本身
- sleep()定义在Thread 类中,作用当前线程。
方法属性:
- wait()是实例方法
- sleep()是静态方法 有static
三、卖票例子
3.1 synchronized 实现卖票例子
public class SaleTicket_WithSynchronized {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "C").start();
}
}
//资源类 OOP:
class Ticket {
//属性、方法
private int number = 30;
//卖票方法
//用synchronized 上锁
public synchronized void saleTicket() {
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" + (number--) + "张票,剩余" + number + "张");
}
}
}
3.2 Lock实现卖票例子
Lock源码定义:
常用上锁语句:
Lock l = ...;
l.lock(); //加锁
try { // access the resource protected by this lock
}
finally
{ l.unlock(); //解锁}
ReentrantLock 可重入锁
公平锁与非公平锁(简单认识,后面详解)
- 公平锁: 非常公平, 不能够插队,必须先来后到!
- 非公平锁:非常不公平,可以插队 (默认都是非公平)
案例
// Lock 三部曲
// 1、 new ReentrantLock();
// 2、 lock.lock(); // 加锁
// 3、 finally=> lock.unlock(); // 解锁
public class SaleTicket_WithLock {
public static void main(String[] args) {
// 并发:多线程操作同一个资源类, 把资源类丢入线程
Ticket ticket = new Ticket();
// @FunctionalInterface 函数式接口,jdk1.8 lambda表达式 (参数)->{ 代码 }
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "A").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "B").start();
new Thread(() -> {
for (int i = 1; i < 40; i++) {
ticket.saleTicket();
}
}, "C").start();
}
}
class Ticket2 {
// 属性、方法
private int number = 30;
Lock lock = new ReentrantLock();
public void sale() {
lock.lock(); // 加锁
try {
// 业务代码
if (number > 0) {
System.out.println(Thread.currentThread().getName() + "卖出了第" +
(number--) + "张票,剩余:" + number + "张");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock(); // 解锁
}
}
}
四、Synchronized 和 Lock 区别
Java中的锁——Lock和synchronized
相比于synchronized,Lock接口所具备的其他特性
①尝试非阻塞的获取锁tryLock():当前线程尝试获取锁,如果该时刻锁没有被其他线程获取到,就能成功获取并持有锁
②能被中断的获取锁lockInterruptibly():获取到锁的线程能够响应中断,当获取到锁的线程被中断的时候,会抛出中断异常同时释放持有的锁
③超时的获取锁tryLock(long time, TimeUnit unit):在指定的截止时间获取锁,如果没有获取到锁返回false
synchronized | Lock | |
---|---|---|
性质 | Java的内置关键字,在JVM层面上 | Java类(接口) |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 必须要手动释放锁,如果不释放锁,会造成死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。 如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式:大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可,可手动设置) |