需要提前了解的概念
1.1 并行和并发
并发(Concurrency)
核心:同一段时间内"有很多任务在推进",但不一定真的同时做。 通常表现为:大家轮流用同一份资源,谁拿到谁先用。
多线程/多任务"争用同一个资源"是并发里常见的一种情况,但并发不只等于"抢资源"。
例子: 春运抢票 电商秒杀
并发 = 看起来同时发生,其实可能是轮流推进。
并行(Parallelism)
核心:多个任务真的在同一时刻一起执行。 一般需要"多个 CPU 核心/多个工人"同时干活。
例子:
更硬核一点:两个人做饭,一个切菜一个炒菜(真正并行)
并行 = 真同时干活。
1.2 同步和异步
同步(Synchronous):
一件事做完,才能做下一件事。
像排队点餐:前一个人没点完,你就得等着。
异步(Asynchronous):
先把事"交代出去",自己继续干别的;等结果好了再来处理。
像你发消息:发完你就去刷别的,不用等对方回。
开新线程通常能让任务不阻塞当前流程,所以属于常见的异步/并发执行方式。
1.3线程的状态
- 新建(New):线程被创建但尚未启动执行。
- 就绪(Runnable):线程等待CPU时间片以便执行,也就是处于就绪状态。
- 阻塞(Blocked):线程暂停执行,通常是因为等待某些条件满足,例如等待I/O操作完成、等待锁释放等。
- 无限期等待(Waiting):线程无限期地等待某个条件的发生,通常需要其他线程来唤醒它。
- 有限期等待(Timed Waiting):线程等待一段时间,超过指定时间后会自动唤醒。
- 终止(Terminated):线程执行完成或者异常终止,进入终止状态。
Thead类中的源码里可以看到线程的以下状态:
java
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW, //新建
/**
* Thread state for a runnable thread. A thread in the runnable
* state is executing in the Java virtual machine but it may
* be waiting for other resources from the operating system
* such as processor.
*/
RUNNABLE, //就绪
/**
* Thread state for a thread blocked waiting for a monitor lock.
* A thread in the blocked state is waiting for a monitor lock
* to enter a synchronized block/method or
* reenter a synchronized block/method after calling
* {@link Object#wait() Object.wait}.
*/
BLOCKED, //阻塞
/**
* Thread state for a waiting thread.
* A thread is in the waiting state due to calling one of the
* following methods:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called {@code Object.wait()}
* on an object is waiting for another thread to call
* {@code Object.notify()} or {@code Object.notifyAll()} on
* that object. A thread that has called {@code Thread.join()}
* is waiting for a specified thread to terminate.
*/
WAITING, //不见不散
/**
* Thread state for a waiting thread with a specified waiting time.
* A thread is in the timed waiting state due to calling one of
* the following methods with a specified positive waiting time:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING, //过时不候
/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED; //终止
}
2 synchronized 入门
如何编写企业需要的工程化多线程代码?
多线程编程模板:
- 线程 操作 资源类
实现步骤:
- 创建资源类
- 资源类里创建同步方法、同步代码块
- 多线程调用
例子: 卖票的程序 , 多个窗口同时买票, 用多线程进行模拟.
java
//资源类
class Ticket{
int num =20;
public void sell(){
if(num<=0){
System.out.println("卖没了");
return;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
num--;
System.out.println(Thread.currentThread().getName()+"卖完之后还剩: "+num);
}
}
// 多线程模版公式 。线程操作资源类
public class SellTacket {
public static void main(String[] args) {
Ticket ticket=new Ticket();
new Thread(()->{
for (int i = 0; i <= 20; i++) {
ticket.sell();
}
},"窗口1").start();
new Thread(()->{
for (int i = 0; i <= 20; i++) {
ticket.sell();
}
},"窗口2").start();
}
}

- 修改方法

2.1 synchronized的8锁问题(区分默认用的是那把锁)
synchronized锁的是什么?
看下面这段儿代码,回答多线程的8个问题:
- 先访问短信,再访问邮件,先打印短信还是邮件
- 停4秒在短信方法内,先打印短信还是邮件
- 先访问短信,再访问hello方法,是先打短信还是hello
- 现在有两部手机,第一部发短信,第二部发邮件,先打印短信还是邮件
- 两个静态同步方法,1部手机,先打印短信还是邮件
- 两个静态同步方法,2部手机,先打印短信还是邮件
- 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
- 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
java
class Phone {
public synchronized void sendSMS() {
//TimeUnit.SECONDS.sleep(4);
System.out.println("sendSMS");
}
public synchronized void sendEmail() {
System.out.println("sendEmail");
}
public void hello() {
System.out.println("hello");
}
}
public class Lock8 {
public static void main(String[] args) {
// 线程操作资源类
Phone phone = new Phone();
// 调用短信
new Thread(()->{
phone.sendSMS();
}).start();
// 调用邮件
new Thread(()->{
phone.sendEmail();
}).start();
}
}
下面例子的答案都是基于: CPU的调度是随机的, 但是那个线程先启动,那个线程就有先天优势, 这里先不考虑随机的情况, 就看谁先启动.
- 成员方法依赖对象存在, 静态方法依赖于类存在 , 对应的锁也是一样
-
先访问短信,再访问邮件,先打印短信还是邮件?
一旦进入了sendSMS方法, sendEmail方法只能等待锁释放. 相反也是
两个方法都是属于同步方法:正常按照顺序执行. 同时这个两个方法使用的锁是同一把锁!

-
停4秒在短信方法内,先打印短信还是邮件?
虽然睡觉,但是锁不释放.
原因:短信与邮件使用的是同一把锁 ,那么这个锁是谁? this 也就是phone对象, 谁调用方法这个this就是谁

-
先访问短信,再访问hello方法,是先打短信还是hello
先hello ,再短信!
原因:一个有锁,一个没有锁!然后按照顺序执行, 短信中有睡眠,hello 中没有,先执行hello!

- 现在有两部手机,第一部发短信,第二部发邮件,先打印短信还是邮件?
先邮件,再短信!
原因:两个同步方法使用的不是同一把锁:因为对象变了 ,phone ,phone2

-
两个静态同步方法,1部手机,先打印短信还是邮件
先短信,再邮件(以代码先天顺序优势的情况下看)
因为同一把锁,那么这个锁是谁? Phone.class
静态方法随着类的加载而加载,此时没有对象!

- 两个静态同步方法,2部手机,先打印短信还是邮件
先短信,再邮件:
Phone phone = new Phone(); Phone phone2 = new Phone();
原因:同一把锁! phone and phone2 都是来自于同一个元模板: Phone.class, 静态的同步方法, 与实例对象有几个没有关系.

- 1个静态同步方法,1个普通同步方法,1部手机,先打印短信还是邮件
先邮件,再短信
原因:短信锁,Phone.class 邮件锁,this

- 1个静态同步方法,1个普通同步方法,2部手机,先打印短信还是邮件
先邮件,再短信
原因:短信锁Phone.class 邮件锁this

总结
synchronized = 给共享资源加互斥锁,让多线程排队进入临界区。
Java中的每一个对象都可以作为锁。具体表现为以下3种形式:
- 对于普通同步方法,锁是
当前实例对象。 - 对于
静态同步方法,锁是当前类的Class对象。 - 对于同步代码块,锁是Synchonized括号里配置的对象
- 而静态同步方法(Class对象锁)与非静态同步方法(实例对象锁)之间是不会有竞争的。
sleep() 和 wait()的 区别
最核心的区别在于:sleep 是"带着锁睡觉",而 wait 是"空手睡觉"。
| 特性 | Thread.sleep() | Object.wait() |
|---|---|---|
| 所属类 | 属于 Thread 类的静态方法 |
属于 Object 类的成员方法 |
| 锁的状态 (最重要) | 不释放锁。线程睡着了也占着资源。 | 释放锁。让出资源让其他线程进入。 |
| 使用范围 | 可以在任何地方使用 | 必须在 synchronized 代码块中调用 |
| 唤醒条件 | 时间到自动苏醒 | 需 notify() / notifyAll() 唤醒 |