线程和进程
进程
程序运行需要有内存空间,可以把这块内存空间理解为进程;
每个应用至少有一个进程,进程之间相互独立,即使要通信,也需要双方同意;
线程
有了进程后,就可以运行程序的代码了。
运行代码称为线程。
一个进程至少有一个线程,所以在进程开启后会自动创建一个线程来运行代码,该线程称为主线程
并发和并行
并行:在同一时刻,有多个执行在多个CPU上(同时)执行 ;
比如一个教室里同一时刻学生有的看书,有的写字
并发:在同一时刻,有多个指令在单个CPU上(交替执行)
比如一个厨师在炒多个菜;
说明:
1. 单核CPU,执行多个程序的时候是在多个线程中做高速切换;
2. 多核多线程,2核4线程,CPU可以同时运行4个线程,超过4线程,CPU就会切换,CPU执行的程序的时候并发和并行都会存在;
CPU调度
1. 分时调度:指的是让所有的线程轮流获取CPU的使用权,并且平均分配每个线程占用的CPU时间片;
2. 抢占式调度:多个线程轮流抢占CPU的使用权,哪个线程先抢到,哪个执行,一般都是优先级高的先抢到CPU使用权的几率大;
3. java程序就是抢占式调度。
主线程
主线程:CPU和内存之间开辟专门为main方法服务的线程
创建线程的方法
继承Thread
- 定义一个类,继承
Thread
- 重写
run
方法,在run
方法中设置线程任务,(所谓的线程任务指的是此线程具体执行的代码)- 调用
Thread
中的start
方法,开启线程,jvm 自动调用run
方法
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:15
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadDemo threadDemo = new ThreadDemo();
// 开启线程
threadDemo.start();
for (int i = 0; i < 10; i++) {
System.out.println("main" + i);
}
// 每次执行结果不一样,因为是CPU抢占式
}
}
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:19
* @Description
*/
public class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
}
}
结果分析
同一线程对象不能连续调用多次
start
方法,如果需要开启新的线程需要new
一个
Thread中的方法
void start()
开启线程,Jvm会自动调用run
方法void run()
开启线程任务,Thread重写接口Runable中的run方法String getName()
获取线程的名称void setName()
设置线程的名字static Thread currentThread()
获取正在执行的线程对象,在哪个线程中使用,获取的就是哪个线程对象static void sleep()
线程睡眠,超时后会自动醒来
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:15
* @Description
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.setName("alex");
// 开启线程
threadDemo.start();
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
Thread.sleep(1000L);
System.out.println("main" + i);
}
// 每次执行结果不一样,因为是CPU抢占式
}
}
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:19
* @Description
*/
public class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(getName() + "-i=" + i);
}
}
}
在重写的run方法中,有异常只能try,不能throws
因为继承的Thread中的run方法没有抛异常,所以在子类中重写的run方法就不能抛出去,只能try_catch
Thread中的其他方法
void setPriority(int newPriority)
设置线程优先级,优先级越高的线程,抢到CPU使用权的几率就越大,但是不是每次都能抢到;int getPriority()
获取线程的优先级void setDaemon(boolean on)
设置为守护线程,当非守护线程执行完毕,守护线程也随之结束static void yield
礼让线程,让当前线程让出CPU的使用权void join()
插入线程或插队线程
- 优先级
package alive.b_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:30
* @Description
*/
public class ThreadExt extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
package alive.b_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:32
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadExt t1 = new ThreadExt();
t1.setName("t1");
ThreadExt t2 = new ThreadExt();
t2.setName("t2");
t2.setPriority(10);
t1.setPriority(1);
System.out.println("t1优先级=>" + t1.getPriority());
System.out.println("t12优先级=>" + t2.getPriority());
/**
*获取优先级
* MIN_PRIORITY = 1 :最小优先级
* NORM_PRIORITY = 5:默认优先级
* MAX_PRIORITY = 10:最大优先级
*/
t2.start();
t1.start();
}
}
- 守护线程
package alive.c_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:47
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadExt threadExt = new ThreadExt();
threadExt.setName("ext");
ThreadGuard threadGuard = new ThreadGuard();
threadGuard.setName("guard");
threadGuard.setDaemon(true);
threadGuard.start();
threadExt.start();
}
}
package alive.c_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:43
* @Description
*/
public class ThreadExt extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(ThreadGuard.currentThread().getName() + "=>i => " + i);
}
}
}
package alive.c_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:45
* @Description
*/
public class ThreadGuard extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(ThreadGuard.currentThread().getName() + "=>i => " + i);
}
}
}
- 礼让线程
package alive.b_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:32
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadExt t1 = new ThreadExt();
t1.setName("t1");
ThreadExt t2 = new ThreadExt();
t2.setName("t2");
t2.start();
t1.start();
}
}
package alive.b_thread;
/**
* @Author zyj
* @Date 2024/09/04 22:30
* @Description
*/
public class ThreadExt extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
Thread.yield();
System.out.println(Thread.currentThread().getName() + "===>" + i);
}
}
}
- 插入线程
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:15
* @Description
*/
public class Test {
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
threadDemo.setName("alex");
// 开启线程
threadDemo.start();
threadDemo.join();
System.out.println("Thread.currentThread().getName() = " + Thread.currentThread().getName());
for (int i = 0; i < 10; i++) {
System.out.println("main" + i);
}
// 每次执行结果不一样,因为是CPU抢占式
}
}
package alive.a_thread;
/**
* @Author zyj
* @Date 2024/09/04 21:19
* @Description
*/
public class ThreadDemo extends Thread {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "-i=" + i);
}
}
}
alex-i=0
alex-i=1
alex-i=2
alex-i=3
alex-i=4
alex-i=5
alex-i=6
alex-i=7
alex-i=8
alex-i=9
Thread.currentThread().getName() = main
main0
main1
main2
main3
main4
main5
main6
main7
main8
main9
Process finished with exit code 0
实现Runnable接口
- 创建类,实现
Runable
接口 - 重写
run
方法,设置线程任务 - 利用
Thread
类的构造方法:Thread(Runnable target)
创建Thread对象(线程对象),将自定义的类当参数传递到Thread构造中 - 调用Thread中的是start方法,开启线程,Jvm 自动调用run方法;
package alive.d_thread;
/**
* @Author zyj
* @Date 2024/09/05 17:24
* @Description
*/
public class ThreadImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=>" + i);
}
}
}
package alive.d_thread;
/**
* @Author zyj
* @Date 2024/09/06 9:38
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadImpl thread = new ThreadImpl();
Thread t1 = new Thread(thread);
t1.start();// 开启线程
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=>" + i);
}
}
}
区别
1. 继承Thread:继承只支持单继承,有继承的局限性
2. 实现Runnable:没有继承的局限性,MyThread extends Fu implements Runable
匿名内部类创建多线程
一个意义上来说,匿名内部类方式不属于多线程方式其中之一,因为匿名内部类形式建立实现Runnable接口的基础上完成的
匿名内部类
new 接口/抽象类(){
重写方法…
}.重写的方法();
接口名/类名 对象名 = new 接口/抽象类(){
重写方法…
}
对象名.重写方法();
package alive.d_thread;
/**
* @Author zyj
* @Date 2024/09/06 9:38
* @Description
*/
public class Test {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=>" + i);
}
}
}, "t1").start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "=>" + i);
}
}
}, "t2 ").start();
}
}
线程安全
多个线程访问同一个资源,导致数据被多个线程使用
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:37
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadImpl thread = new ThreadImpl();
Thread t1 = new Thread(thread, "t1");
Thread t2 = new Thread(thread, "t2");
Thread t3 = new Thread(thread, "t3");
t1.start();
t2.start();
t3.start();
}
}
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:38
* @Description
*/
public class ThreadImpl implements Runnable {
int ticket = 100;
@Override
public void run() {
while (ticket >= 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
}
t1获取了=>10张票
t2获取了=>10张票
t3获取了=>10张票
t2获取了=>8张票
t1获取了=>9张票
t2获取了=>6张票
t3获取了=>7张票
t2获取了=>4张票
t1获取了=>5张票
t2获取了=>2张票
t3获取了=>3张票
t2获取了=>0张票
t1获取了=>1张票
t1 获取到ticket的时,ticket -- 未执行,CPU在高速切换,t2就被执行
- 同步代码块
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:38
* @Description
*/
public class ThreadImpl implements Runnable {
int ticket = 10;
Object obj = new Object();
@Override
public void run() {
while (ticket >= 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (obj) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
}
}
}
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:37
* @Description
*/
public class Test {
public static void main(String[] args) {
ThreadImpl thread = new ThreadImpl();
Thread t1 = new Thread(thread, "t1");
Thread t2 = new Thread(thread, "t2");
Thread t3 = new Thread(thread, "t3");
t1.start();
t2.start();
t3.start();
}
}
同步方法
- 普通同步方法(非静态)
格式
修饰符 synchronized 返回值类型 方法名(参数){
}
默认锁:this
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:38
* @Description
*/
public class ThreadImpl implements Runnable {
int ticket = 100;
@Override
public void run() {
while (ticket >= 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
updateTicket();
}
}
public synchronized void updateTicket() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
}
public void updateTicket2() {
synchronized (this) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
}
- 静态同步方法
格式
修饰符 static synchronized 返回值类型 方法名(参数){
方法体…
return 返回结果
}
默认锁:class 对象
package alive.e_thread;
/**
* @Author zyj
* @Date 2024/09/08 15:38
* @Description
*/
public class ThreadImpl implements Runnable {
static int ticket = 10;
@Override
public void run() {
while (ticket >= 0) {
try {
Thread.sleep(100L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// updateTicket();
updateTicket2();
}
}
public static synchronized void updateTicket() {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
public static void updateTicket2() {
synchronized (ThreadImpl.class) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "获取了=>" + ticket + "张票");
ticket--;
}
}
}
}
死锁
指的是两个或者两个以上的线程在执行的过程中由于同步竞争同步锁而产生的一种阻塞现象;如果没有外力作用,他们将无法继续执行下去,这种情况称为死锁
根据上图所示,
线程1 正在持有锁1,但是线程1必须再拿到锁2,才能继续执行
线程2 正在持有锁2,但是线程2需要再拿到锁1,才能继续执行
此时,两个线程处于互相等待的状态,就是死锁,在程序中的死锁将出现在同步代码块中嵌套
package alive.f_synchronized;
/**
* @Author zyj
* @Date 2024/09/08 16:36
* @Description
*/
public class Lock implements Runnable {
Boolean flag = false;
public Lock(Boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (LockA.lockA) {
System.out.println("lockA ...");
synchronized (LockB.lockB) {
System.out.println("lockB ...");
}
}
} else {
synchronized (LockB.lockB) {
System.out.println("lockB ...");
synchronized (LockA.lockA) {
System.out.println("lockA...");
}
}
}
}
}
package alive.f_synchronized;
/**
* @Author zyj
* @Date 2024/09/08 16:40
* @Description
*/
public class LockA {
public static LockA lockA = new LockA();
}
package alive.f_synchronized;
/**
* @Author zyj
* @Date 2024/09/08 16:40
* @Description
*/
public class LockB {
public static LockB lockB = new LockB();
}
package alive.f_synchronized;
/**
* @Author zyj
* @Date 2024/09/08 16:36
* @Description
*/
public class Test {
public static void main(String[] args) {
Lock l1 = new Lock(true);
Lock l2 = new Lock(false);
new Thread(l1).start();
new Thread(l2).start();
}
}
线程状态
介绍
线程状态 | 发生条件 |
---|---|
new(新建) | 线程刚被创建,但是并未启动,还没调用start方法 |
Runnable(可运行) | 线程可以在java虚拟机中运行的状态,可能正在运行自己的代码,也可能没有,这取决于操作系统处理器 |
Blocked(锁阻塞) | 当一个线程试图获取一个对象锁,而该对象被其他的线程持有,则该线程进入Blocked状态;单该线程持有锁时,该线程将变成Runnable状态 |
Waiting(无限等待) | 一个线程在等待另一个线程执行时,该线程进入Waiting状态,进入这个状态后是不能自动唤醒,必须等待另一个线程调用notify或者notifyAll方法才能唤醒 |
Timed Waiting(计时等待) | 同Waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting ,这个状态将一直保持到超时期满或者接受到唤醒通知,带有超时参数的常用方法有:Thread.sleep、Object.wait |
Terminated(被终止) | 因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡或者调用过时方法stop() |
生命周期图
- sleep(time)和wait(time)有什么区别
- sleep(time):线程睡眠,在睡眠的过程中,线程是不会释放锁,此时其他线程抢不到锁,设置的时间一旦超时,自动醒来,继续执行;
- wait(time):线程等待,在等待的过程中会释放锁,其他线程就有可能抢到锁,如果在等待的过程中被唤醒或者时间超时,会和其他的线程重新抢锁,如果抢到了继续执行,抢不到,锁阻塞。
- wait()和notify()
- wait():空参wait会进入无限等待状态,会释放锁,需要其他线程调用notify(一次唤醒一条等待的线程,唤醒是随机的)或者notifyAll方>法(将所有等待线程全唤醒),被唤醒之后,会和其他的线程冲销抢锁,抢到了则继续执行,没抢到,则进入锁阻塞
- notify():notify会唤醒正在等待的线程,一次只能唤醒一条等待的线程,如果是多条线程在等待,notify会随机一条唤醒
- notifyAll():唤醒所有等待的线程
- wait和notify两个方法的用法
- 两个方法都需要锁对象调用,所以两个方法需要用到同步代码块,同步方法中;
- 两个方法的调用必须是同一个锁对象调用
可以理解为同一个锁对象,将多条线程分到一组中,这样notify就知道唤醒的是直接的等待线程
等待唤醒机制
方法 | 说明 |
---|---|
void wait() | 线程等待,等待的过程中线程会释放锁,需要被其他线程调用notify方法将其唤醒,重新抢锁执行 |
void notify() | 线程唤醒,一次唤醒一个等待线程,如果有多条线程等待,则随机唤醒一条等待线程 |
void notifyAll() | 唤醒所有等待线程 |
wait和notify方法需要锁对象调用,所以需要用到同步代码块中,而且必须是同一个锁对象
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:38
* @Description 基础类
*/
public class Base {
private int count;
private boolean flag;
public Base() {
}
public Base(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
public void getCount() {
System.out.println("消费了===>" + count + "个count");
}
public void setCount() {
count++;
System.out.println("生产了===>" + count + "个count");
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:41
* @Description 生产者
*/
public class Producer implements Runnable {
public Producer() {
}
public Producer(Base base) {
this.base = base;
}
private Base base;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (base) {
if (base.isFlag()) {
try {
base.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
base.setCount();
base.setFlag(true);
base.notify();
}
}
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:41
* @Description 消费者
*/
public class Consumer implements Runnable {
private Base base;
public Consumer(Base base) {
this.base = base;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (base) {
if (!base.isFlag()) {
try {
base.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
base.getCount();
base.setFlag(false);
base.notify();
}
}
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:37
* @Description
*/
public class Test {
public static void main(String[] args) {
Base base = new Base();
Producer producer = new Producer(base);
Consumer consumer = new Consumer(base);
Thread t1 = new Thread(producer, "t1");
Thread t2 = new Thread(consumer, "t2");
t2.start();
t1.start();
}
}
同步方法改造
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:38
* @Description 基础类
*/
public class Base {
private int count;
private boolean flag;
public Base() {
}
public Base(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
public synchronized void getCount() {
if (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("消费了===>" + count + "个count");
this.flag = false;
this.notify();
}
public synchronized void setCount() {
if (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
count++;
System.out.println("生产了===>" + count + "个count");
this.flag = true;
this.notify();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:41
* @Description 消费者
*/
public class Consumer implements Runnable {
private Base base;
public Consumer(Base base) {
this.base = base;
}
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
base.getCount();
}
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:41
* @Description 生产者
*/
public class Producer implements Runnable {
public Producer() {
}
public Producer(Base base) {
this.base = base;
}
private Base base;
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
base.setCount();
}
}
}
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:37
* @Description
*/
public class Test {
public static void main(String[] args) {
Base base = new Base();
Producer producer = new Producer(base);
Consumer consumer = new Consumer(base);
Thread t1 = new Thread(producer, "t1");
Thread t2 = new Thread(consumer, "t2");
t2.start();
t1.start();
}
}
多等待多唤醒
package alive.g_synchronized;
/**
* @Author zyj
* @Date 2024/09/09 15:38
* @Description 基础类
*/
public class Base {
private int count;
private boolean flag;
public Base() {
}
public Base(int count, boolean flag) {
this.count = count;
this.flag = flag;
}
public synchronized void getCount() {
while (!this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("消费了===>" + count + "个count");
this.flag = false;
this.notifyAll();
}
public synchronized void setCount() {
while (this.flag) {
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
count++;
System.out.println("生产了===>" + count + "个count");
this.flag = true;
this.notifyAll();
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}
}
Lock锁
基本使用
- Lock是一个接口
- 实现类:ReentrantLock
- 方法:
lock()获取锁
unlock()释放锁
package alive.h_lock;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Author zyj
* @Date 2024/09/09 17:43
* @Description
*/
public class Ticket implements Runnable {
private int ticket = 300;
Lock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
lock.lock();
Thread.sleep(100L);
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "抢到了第》" + ticket + "张票");
ticket--;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
}
}
package alive.h_lock;
/**
* @Author zyj
* @Date 2024/09/09 17:46
* @Description
*/
public class Test {
public static void main(String[] args) {
Ticket t = new Ticket();
new Thread(t, "t1").start();
new Thread(t, "t2").start();
new Thread(t, "t3").start();
}
}
synchronized:无论是同步代码块还是同步方法,都需要在结束一对{}之后,释放该对象
Lock:是通过两个方法控制需要被同步的代码
Callable接口
- 概述:Callable< V >是一个接口,类似于Runnable
- 方法:
V call() 设置线程任务,类似于run方法- call方法和run方法的区别
相同点:都是设置线程任务的
不同点:
run方法没有返回值,而且有异常不可以throws
call方法没有返回值,而且有异常可以throws- < V >
< V > 叫做泛型
泛型:用于指定操作的数据类型,<>中只能写引用数据类型,如果泛型不写,默认是Object类型数据
实现Callable接口时,指定的泛型是什么类型,重新的call方法返回值就是什么类型- 获取call方法返回值:FutureTask< V >
FutureTask< V > 实现了接口 :Future< V >
V get() 获取call方法的返回值
package alive.i_callable;
import java.util.concurrent.Callable;
/**
* @Author zyj
* @Date 2024/09/10 20:28
* @Description
*/
public class CallableImpl implements Callable<String> {
@Override
public String call() throws Exception {
return "String impl";
}
}
package alive.i_callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Author zyj
* @Date 2024/09/10 20:31
* @Description
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
CallableImpl callable = new CallableImpl();
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread t1 = new Thread(futureTask);
t1.start();
System.out.println(futureTask.get());
}
}
线程池
问题:一个线程任务,就需要创建一个线程对象去执行,用完还要销毁对象,如果线程任务多了,就需要频繁的创建和销毁线程对象,而耗费内存,所以需要考虑线程对象循环使用。
如何创建线程池对象?
Executors
获取线程池对象?
Executors中的静态方法,
static ExecutorService newFixedThreadPool(int nThreads)
- 参数:指定线程池中最多创建的线程对象条数
- 返回值ExecutorService是线程池,用了管理线程对象;
执行线程任务:ExecutorService中的方法
Future<?> submit(Runnable task)提交一个Runnable任务用于执行;
Future< T > submit(Callable< T > task) 提交一个Callable任务用于执行;
submit 方法的返回值:Future接口
用于接收run方法或者call方法返回值,但是run方法没有返回值,所有可以不用Future接收,执行Call方法需要用Future接收
Future中有一个方法:V get() 用于获取call方法返回值
ExecutorService中的方法
void shutdown() 启动有序关闭,其中先提交的任务将被执行,但不会接受任何新任务
package alive.j_pool;
/**
* @Author zyj
* @Date 2024/09/13 10:55
* @Description
*/
public class RunnableImpl implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "》》》》执行了");
}
}
package alive.j_pool;
import java.util.concurrent.Callable;
/**
* @Author zyj
* @Date 2024/09/13 11:00
* @Description
*/
public class CallableImpl implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 1;
}
}
package alive.j_pool;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @Author zyj
* @Date 2024/09/13 10:56
* @Description
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建线程池对象
ExecutorService es = Executors.newFixedThreadPool(3);
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
es.submit(new RunnableImpl());
Future<Integer> submit = es.submit(new CallableImpl());
System.out.println(submit.get());
}
}
练习
package alive.k_pool;
import java.util.concurrent.Callable;
/**
* @Author zyj
* @Date 2024/09/13 11:08
* @Description
*/
public class StringThreadImpl implements Callable<String> {
@Override
public String call() throws Exception {
return "但是我有胆量去征服海洋";
}
}
package alive.k_pool;
import java.util.concurrent.Callable;
/**
* @Author zyj
* @Date 2024/09/13 11:09
* @Description
*/
public class SumThreadImpl implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
package alive.k_pool;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @Author zyj
* @Date 2024/09/13 11:10
* @Description
*/
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService es = Executors.newFixedThreadPool(2);
Future<String> f1 = es.submit(new StringThreadImpl());
Future<Integer> f2 = es.submit(new SumThreadImpl());
System.out.println("f1 = " + f1.get());
System.out.println("f2 = " + f2.get());
}
}
定时器
定时器
Timer();
方法
void schedule(TimerTask task , Date firstTimer , long period)
task:抽象类
fistTime:开始执行的时间
period:每隔多长时间执行一次,设置的是毫秒值;
package alive.l_timer;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
/**
* @Author zyj
* @Date 2024/09/13 11:24
* @Description
*/
public class Test {
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("2秒执行一次");
}
}, new Date(), 2000L);
}
}
集合框架
数组是定长的,所以如果添加或删除一个数据,数组并不好使,需要新创建数组,
集合的特点
- 只能存储引用数据类型的数据
- 长度可变
- 集合中有大量的方法,方法便于操作
分类
- 单例集合:一个元素就一个组成部分
list.add(“张三”)- 双列集合:一个元素有两个部分构成,value和key
map.put(“key1”,"value1)->
key,value叫做键值对
Collection接口
单列集合的顶级接口
Collection< E > 对象名 = new 实现类对象< E >()
< E > 泛型,决定了集合中能存储什么类型的数据,可以统一元素类型
泛型只能写引用数据类型,如果不写默认是Object类,
泛型细节:
等号前面的泛型必须写,等号后面泛型可以不用写,(可推导可省略)JVM可以根据前面的类型推导后面的类型
常用方法:
boolean add(E e):将给定的元素添加到当前集合中(我们一般调用add时,不用boolean接收,因为add一定会成功);
boolean addAll(Collection< ? extends E > c):将另一个集合元素添加到当前集合中(集合合并);
void clear():清除集合中所有元素;
boolean contains(Object o):判断当前集合中是否包含指定元素;
boolean isEmpty():判断当前集合是否为空
boolean remove(Object obj):将指定的元素从集合中删除
int size():返回集合中的元素个数
Object[] toArray():将集合中的元素,存储到数组中
package alive.a_collection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* @Author zyj
* @Date 2024/09/13 13:09
* @Description
*/
public class Test {
public static void main(String[] args) {
Collection<String> arr1 = new ArrayList<>();
Collection<String> arr2 = new ArrayList<>();
arr1.add("啼魂兽");
arr1.add("噬金虫");
arr2.add("韩立");
arr2.add("南宫婉");
arr2.add("紫玲");
System.out.println("arr1 = " + arr1);
System.out.println("arr2 = " + arr2);
arr2.addAll(arr1);
System.out.println("arr2 = " + arr2);
System.out.println("是否存在:" + arr2.contains("韩立"));
arr1.clear();
System.out.println(arr1);
System.out.println(arr1.isEmpty());
arr2.remove("啼魂兽");
System.out.println(arr2);
System.out.println(arr2.size());
Object[] arr = arr2.toArray();
System.out.println("arr ==>" + Arrays.toString(arr));
}
}
迭代器
迭代器是属于设计模式之一,迭代器提供了一种方法来顺序访问一个聚合对象中各个元素,而不保留该对象的内部表示;
Iterator对象称为迭代器,只要用于遍历Collection集合元素。
所有的Collection接口的集合类都有一个Iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
Iterator仅用于遍历集合,Iterator本身并不存放对象。
package alive.b_collection;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @Author zyj
* @Date 2024/09/13 14:08
* @Description
*/
public class Test {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("灵界");
list.add("小灵天");
list.add("广寒界");
list.add("魔界");
System.out.println("list = " + list);
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String el = iterator.next();
System.out.println("el = " + el);
}
}
}
- next()方法在获取的时候不要连续使用多次
迭代过程
int cursor;下一个元素索引位置
int lastRet;上一个元素索引位置
① 获取集合的 迭代器 Iterator iterator = list.iterator();
② 进入while 循环,调用hasNext()判断是否有下一个元素,返回true,Iterator.next()移动一个位置,将该位置的元素返回;
③ 进入while 循环,再次调用hasNext()判断是否有下一个元素,返回true,iterator.next()移动一个位置,将该位置的元素返回
④ 进入while 循环,再次调用hasNext()判断是否有下一个元素,返回false,循环结束。
迭代器底层原理
- 调用iterator()时,会创建一个内部类Itr,itr是Iterator的实现类
HashSet的迭代器,实现了Iterator
并发修改异常
package alive.c_collection;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @Author zyj
* @Date 2024/09/13 16:29
* @Description
*/
public class Test {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<>();
arr.add("韩立");
arr.add("南宫婉");
arr.add("紫玲");
System.out.println(arr);
Iterator<String> iterator = arr.iterator();
while (iterator.hasNext()) {
String el = iterator.next();
arr.add("玄骨");
System.out.println(el);
}
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
当预期的操作次数和实际操作次数不相等,则会抛出异常;
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
modCount,操作都会+1,expectedModCount初始就被赋值modCount,所以导致不相等
数据结构
ArrayList源码
- new ArrayList():会创建一个长度为0的数组
- 调用 add():会创建数组长度增长至10,并将插入数组中
- 如果插入超过10,就会将老数组拷贝到新的数组,且新的数组长度为15
LinkedList集合
- 概述:LinkedList是List接口的实现类
- 特点:
元素有序,元素可重复,有操作索引的方法,不代表本质上具有索引
线程不安全
数据结构:双向链表- 方法:
- public void addFirst(E e):将指定元素插入此列表的开头
- public void addLast(E e):将指定元素添加到此列表的结尾
- public E getFirst():返回此列表的第一个元素
- public E getLast():返回此列表的最后一个元素
- public E removeFirst():移除并返回此列表的第一个元素
- public E removeLast():移除并返回此列表的最后一个元素
- public E pop():从此列表所表示的堆栈处弹出一个元素
- public void push(E e):将元素推出此列表所表示的堆栈
- public boolean isEmpty():如果列表中没有元素,则返回true
package alive.d_collection;
import java.util.Iterator;
import java.util.LinkedList;
/**
* @Author zyj
* @Date 2024/09/14 10:55
* @Description
*/
public class Test {
public static void main(String[] args) {
LinkedList<String> arr = new LinkedList();
arr.add("韩立");
arr.add("南宫婉");
arr.add("紫灵仙子");
arr.addFirst("蛮胡子");
arr.addLast("萧诧");
System.out.println(arr);
arr.pop();
System.out.println(arr);
arr.push("push");
System.out.println(arr);
System.out.println(arr.getFirst());
System.out.println(arr.getLast());
Iterator<String> iterator = arr.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
LinkedList底层成员
- LinkedList底层成员
- transient int size = 0 :元素个数
- transient Node< E > first:第一个元素
- transient Node< E >last:最后一个元素
transient
用于修饰类的成员变量,表示该成员变量不会被序列化,
- 防止数据被序列化
- 提高性能
- 默认值处理
Node 代表的是节点对象
private static class Node<E> {
E item;// 节点上的元素
Node<E> next;// 下一个节点地址
Node<E> prev;// 最后一个节点地址
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
增强for循环
- 增强for遍历集合时,底层实现原理是迭代器
- 增强for遍历数组时,底层实现原理是普通for循环
- 在使用过程中,遍历数组时不要随意修改集合长度,否则会出现并发修改异常
Collections集合工具类
- 集合工具类
- 方法:
- static < T > boolean addAll(collection <? super> c, T … element):批量添加元素
- static < T > void shuffle(List<?> list):将集合中的元素顺序打乱
- static < T > void sort(List <?> list):将集合中的元素按照默认规则排序
- static < T > void sort(List <?> list,Comparator< ? super T> c):将集合中的元素按照指定规则排序
package alive.f_collections;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* @Author zyj
* @Date 2024/09/14 15:39
* @Description
*/
public class Test {
public static void main(String[] args) {
ArrayList<String> arr = new ArrayList<>();
Collections.addAll(arr, "a", "b", "c", "d", "e", "f");
System.out.println("arr = " + arr);
Collections.shuffle(arr);
System.out.println("arr = " + arr);
Collections.sort(arr);
System.out.println("arr = " + arr);
ArrayList<Person> ps = new ArrayList<>();
Collections.addAll(ps, new Person(10, "alex"), new Person(6, "Tom"), new Person(15, "Jack"));
System.out.println("ps = " + ps);
Collections.sort(ps, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
System.out.println("ps = " + ps);
ArrayList<Student> ss = new ArrayList<>();
Collections.addAll(ss, new Student("Tom", 60), new Student("Jack", 100), new Student("Alex", 80));
System.out.println("ss = " + ss);
Collections.sort(ss);
System.out.println("ss = " + ss);
}
}
package alive.f_collections;
/**
* @Author zyj
* @Date 2024/09/14 15:39
* @Description
*/
public class Person {
private int age;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
package alive.f_collections;
/**
* @Author zyj
* @Date 2024/09/14 15:47
* @Description
*/
public class Student implements Comparable<Student> {
private String name;
private int score;
public Student() {
}
public Student(String name, int score) {
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
@Override
public int compareTo(Student o) {
return o.getScore() - this.getScore();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", score=" + score +
'}';
}
}