可运行对象:可以理解为一个任务,是Runnable接口的实例。
线程:一个任务从头到尾的执行流程,本质上是便于任务执行的对象。
线程状态:线程状态是操作系统中线程生命周期的一个阶段。
进程:内存中运行的应用程序。一个进程中可以启动多个线程。
任务类中一般包含一些属性和方法,这些属性和方法可以用来描述任务的状态和行为。
下面是定义一个任务类并创建一个线程的示例。
public class Task implements Runnable {
private String name;
public Task(String name) {
this.name = name;
}
@Override
public void run() {
// 执行任务
System.out.println("Executing task: " + name);
}
public static void main(String[] args) {
// 创建一个任务
Task task = new Task("Task 1");
// 创建一个线程来执行任务
Thread thread = new Thread(task);
// 启动线程
thread.start();
}
}
调用start方法后,run方法变成了Thread默认要执行的普通方法。
我觉得考...
程序改错
1.
forFun
类实现了Runnable
接口,但是在forFun
构造函数中,它创建了一个新的forFun
对象,并尝试在一个新的线程中运行它。然而,这个新的forFun
对象并没有实现Runnable
接口,所以它不能作为Runnable
传递给Thread
构造函数public class forFun implements Runnable { public static void main(String[] args) { new forFun(); } public forFun(){ new Thread(this).start(); } @Override public void run() { System.out.println("1"); } }
这样,就输出5遍test
2. 调用了两次start
每个线程只能启动一次,删掉一个start就好了
使用Platform.runLater的目的是什么?
告诉系统在应用程序线程中运行Runnable对象。
★线程状态(这个肯定考)
新建(New):当一个线程被创建,但还没有开始运行时,它处于新建状态。
可运行(Runnable):线程已经准备好运行,但可能正在等待CPU的调度。
运行(Running):线程正在CPU上执行。
阻塞(Blocked):线程正在等待某个事件(如I/O操作完成),所以它暂时停止执行,等待事件发生。
等待(Waiting):线程在等待另一个线程执行某个操作。
计时等待(Timed Waiting):线程在等待另一个线程执行某个操作,但有时间限制。
终止(Terminated):线程已经执行完毕,或者被其他线程中断。
当我们创建一个新的线程时,它最初处于新建new状态。然后,当我们调用start()方法时,线程开始运行,进入可运行Runnable状态。如果CPU调度器选择执行这个线程,那么它就会进入运行状态。如果线程在运行过程中遇到需要等待的事件(如I/O操作),那么它就会进入阻塞blocked状态。当等待的事件完成后,线程会再次进入可运行状态,等待CPU调度器的选择。如果是主动等待,那么进入waiting状态,等待被唤醒后再次进入Runnable状态,如果是主动睡眠,就进入time waiting状态,等待睡眠时间期限满后,进入Runnable状态。注意,当running状态异常中断或执行完毕,则进入dead阶段。
(看图加深理解,上面是我自己总结的)
使用线程池的好处?
使用线程池可以有效地管理和调度线程,提高系统的并发性能,同时可以有效地管理系统的资源,避免系统资源耗尽,提高系统的稳定性和响应性。
锁
分为两种,同步锁Synchronized和重入锁ReentrantLock
1.同步锁
public class LockExample { private final Object lock = new Object(); public void method() { synchronized (lock) { // 这里是临界区,只有一个线程可以进入 // 其他线程需要等待这个线程释放锁 } } }
2.重入锁
同一个线程,可以多次获取这个锁。但是一定要记得及时释放锁,以免出现死锁。
import java.util.concurrent.locks.ReentrantLock; public class LockExample { private final ReentrantLock lock = new ReentrantLock(); public void method() { lock.lock(); try { // 这里是临界区,只有一个线程可以进入 // 其他线程需要等待这个线程释放锁 } finally { lock.unlock(); } } }
还有另一种描述的方式
1.创建 private static Lock lock=new ReentrantLock();
2.得到 lock.lock()
3.释放 lock.unlock();
4.创建锁条件 lock.newCondition()
await() 释放锁,挂起线程。一旦条件满足,它就会被唤醒,再次获得lock。
signal()/signalAll():将condition.await()中FIFO队列中第一个Node唤醒(或者全部唤醒)。
死锁:两个或多个进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。
如何避免死锁:实行资源排序,让各线程加锁的顺序保持相同。
存款提款的那个程序,应该会考吧(后面有)
只要余额发生变化,存款任务都会通知提款任务。当唤醒提款任务时,条件(balance
wait() 这个方法应该在一个循环中使用,而不是在一个if语句中。 修改后,只有异常情况中wait才会被唤醒。
synchronized(object){
while(!condition){
try{
object.wait();
}
catch(InterruptedException ex){
// 处理异常
}
}
}
Buffer类
Buffer类中的read和write通常是在单线程环境下调用的,以确保数据的正确性和一致性。
当调用read方法时,如果队列为空,那么read方法会一直等待,直到有新的数据可以读取。
当调用write方法时,如果队列已满,那么write方法也会阻塞,直到队列中有足够的空间来存储新的数据。
阻塞队列
(1)阻塞队列:支持两个附加操作的队列,这两个附加的操作支持阻塞的插入和移除方法。
阻塞队列是线程安全的队列访问方式
当阻塞队列需要插入元素,若队列已满,则线程会阻塞直至队列非满。
当阻塞队列需要删除元素,若队列为空,则线程会阻塞直至队列非空。
(2)Java中支持的阻塞队列:
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
信号量 Semaphore
创建:Semaphore semaphore = new Semaphore(3);
获取一个信号量:Semaphore.acquire();
释放一个信号量:Semaphore.release();
锁只能锁一个共享资源,信号量可以锁多个共享资源。
=>对多个资源的访问控制用信号量,对一个资源的访问控制用锁。
同步合集
同步合集:实现线程安全的Collections。
ArrayList不是同步的,让它实现synchronizedList使其同步。
Fork/Join
是一个抽象类
Fork/Join框架:Fork分割,Join合并。用于执行并行任务,将大问题分割成子问题。
定义:
public abstract class ForkJoinTask
implements Future , Serializable{ } V:任务执行的结果的类型
Future
:表示ForkJoinTask实现了Future接口,因此可以用来获取任务执行结果。 RecursiveAction(用于不返回值的任务)和RecursiveTask(用于返回值的任务),是ForkJoinTask的两个子类
娱乐一下,ForkPool
RecursiveAction mainTask = new SortTask(list); ForkJoinPool pool = new ForkJoinPool(); pool.invoke(mainTask);
一个包含了同步锁Synchronized的示例代码,实现启动1000个线程,每个线程给初始值为0的变量sum加1
import java.util.concurrent.*;
public class forFun {
private Integer sum = new Integer(0);
public static void main(String[] args) {
forFun test = new forFun();
System.out.println("What is sum ? " + test.sum);
}
public forFun() {
synchronized(this){//同步代码块,确保只有一个线程可以访问以下代码
ExecutorService executor = Executors.newFixedThreadPool(1000);//创建大小1000 的线程池
for (int i = 0; i < 1000; i++) {
executor.execute(new SumTask());//将SumTask交给线程池
}
executor.shutdown();
while(!executor.isTerminated()) {//等待任务完成
}
}
}
class SumTask implements Runnable {// 定义一个实现Runnable接口的SumTask类
public void run() {
synchronized(this){// 同步代码块,确保只有一个线程可以访问以下代码
int value = sum.intValue() + 1;
sum = new Integer(value);
}
}
}
}
来个好玩的,账户存款/取款任务
有线程池,也有同步锁。lz之前跟着导师做区块链天天用Solidity写这种,看到就无比亲切,爽翻了
import java.util.concurrent.*;
public class forFun {
private static Account account = new Account();
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小为2的线程池
executor.execute(new DepositTask());
executor.execute(new WithdrawTask());
executor.shutdown();
System.out.println("Deposit Task\t\tWithdraw Task\t\tBalance");
}
// 存款任务
public static class DepositTask implements Runnable {
public void run() {
try {
while (true) {
account.deposit((int) (Math.random() * 10) + 1);
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
// 取款任务
public static class WithdrawTask implements Runnable {
public void run() {
while (true) {
account.withdraw((int) (Math.random() * 10) + 1);
}
}
}
// 账户类
public static class Account {
private int balance = 0;
// 获取账户余额
public int getBalance() {
return balance;
}
// 存款方法,使用 synchronized 保证线程安全
public synchronized void deposit(int amount) {
balance += amount;
System.out.println("Deposit " + amount +
"\t\t\t\t\t" + getBalance());
notifyAll(); // 唤醒其他等待线程
}
// 取款方法,使用 synchronized 保证线程安全
public synchronized void withdraw(int amount) {
try {
while (balance < amount) // 当余额不足时,进入等待状态
wait();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
balance -= amount;
System.out.println("\t\t\tWithdraw " + amount +
"\t\t" + getBalance());
}
}
}