现代操作系统是多进程的,会同时执行多个程序
许多应用程序是多线程运行的,比如:在使用微信是,发送消息、接收消息、打开文档等操作,会让我们觉得这些是并发运行的

这些程序看起来像是同时运行,但是对于CPU而言,操作系统在同一时间只能运行一个程序。它将CPU的时间片轮流分配给不同的程序,给用户一种并发处理的感觉,因为CPU轮转的速度很快,人感觉不出来。
当一个程序进入内存时,就变成了一个进程


class MyThread extends Thread {
@Override
public void run() {
System.out.println("线程执行");
}
}
// 使用
MyThread t = new MyThread();
t.start();Runnable接口只有一个run()方法:
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("线程执行");
}
}
// 使用
Thread t = new Thread(new MyRunnable());
t.start();线程由新建、就绪、运行、阻塞、死亡这些状态构成了它的生命周期,呈现了其工作的过程。
线程在运行状态时,遇到如下状况将会进入各种阻塞状态:
[新建NEW] → [可运行RUNNABLE] → [运行中(操作系统层面)]
↗ ↓ ↖
[阻塞BLOCKED] ← [等待WAITING/TIMED_WAITING] → [终止TERMINATED]Thread t = new Thread(); // 状态:NEW
t.start(); // 状态:RUNNABLEstart()方法
start()后进入就绪状态


“阻塞”状态是三种状态的组合体:睡眠/资源阻塞/等待。

注意!!!我们无法控制线程调度器,不能要求、指定某个线程去被运行。
当多个线程共享同一个数据时,如果处理不当,很容易出现线程的安全隐患,所以多线程编程时经常需要解决线程同步问题:
public static void main(String[] args) {
Runnable target = new SecondThread();
Thread t1 = new Thread(target);
Thread t2 = new Thread(target);
t1.start();
t2.start();
}实际问题举例:
夫妻共同进行取钱,
Java中每个对象都有一个内置锁,当对象具有同步代码时,内置锁启用。
Java使用关键字synchonized修饰同步代码块或同步方法,为对象加锁。 加锁后的同步代码块或同步方法,形成“原子”操作,即该操作是不可分割的。
// 同步方法
public synchronized void method() {}
// 同步代码块
synchronized(lockObject) {}同步锁形成的原子操作会极大破坏并发性,所以不要同步原子操作之外的其他代码
public class MyRunnable implements Runnable{
private int i=0; //i作为属性
public void run(){
while(i<5){
i++;
for (int j = 0; j < 20000000; j++);
System.out.print(Thread.currentThread().getName()+" ");
System.out.println("i="+i);
}
}
}
public class Test {
public static void main(String[] args) {
Runnable target = new MyRunnable();
Thread t1 = new Thread(target, "A");
Thread t2 = new Thread(target, "B");
t1.start();
t2.start();
}
}
要理解这的错误,要先明确循环程序的操作顺序:
while(i < 5) { // 检查条件
i++; // 递增操作
// ...其他代码
}
添加同步代码之后:
public class MyRunnable implements Runnable{
private int i=0; //i作为属性
public void run(){
while(i<5){
synchronized(this){
i++;
for (int j=0; j<20000000; j++);
System.out.print(Thread.currentThread().getName()+" ");
System.out.println("i="+i);
}
}
}
}
public class Test {
public static void main(String[] args) {
Runnable target = new MyRunnable();
Thread t1 = new Thread(target, "A");
Thread t2 = new Thread(target, "B");
t1.start();
t2.start();
}
}假设以下执行顺序:
要彻底解决这三个问题应该将代码修改成为下面这样子:
public class MyRunnable implements Runnable{
private int i=0; //i作为属性
public void run(){
while(i<5){
synchronized(this){
if(i==5) break; //这一步很关键,起到了再次判断的效果
i++;
for (int j = 0; j < 20000000; j++);
System.out.print(Thread.currentThread().getName()+" ");
System.out.println("i="+i);
}
}
}
}如果一个方法内的所有代码组成“原子”操作,那么可以将该方法定义为同步方法,使用synchronized关键字修饰。

实例讲解:
写两个线程,线程A“做”10个披萨,线程B“做”20份意大利面,要求线程A每做一个披萨,就通知线程B去做两份意大利面,线程B完成两份意大利面后通知线程A继续做披萨……。
public class SimplePizzaPastaProduction {
// 使用一个简单的标志对象来控制流程
private static class Kitchen {
boolean isPizzaTime = true;
}
public static void main(String[] args) {
Kitchen kitchen = new Kitchen();
// 线程A:制作披萨
Thread pizzaChef = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
synchronized (kitchen) {
// 等待轮到做披萨
while (!kitchen.isPizzaTime) {
try {
kitchen.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 制作一个披萨
System.out.println("🍕 制作第" + i + "个披萨");
// 通知做意大利面
kitchen.isPizzaTime = false;
kitchen.notifyAll();
}
}
});
// 线程B:制作意大利面
Thread pastaChef = new Thread(() -> {
for (int i = 1; i <= 20; i += 2) {
synchronized (kitchen) {
// 等待轮到做意大利面
while (kitchen.isPizzaTime) {
try {
kitchen.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
// 制作两份意大利面
System.out.println("🍝 制作第" + i + "和" + (i+1) + "份意大利面");
// 通知做披萨
kitchen.isPizzaTime = true;
kitchen.notifyAll();
}
}
});
pizzaChef.start();
pastaChef.start();
}
}