JUC就是java.util.concurrent工具包的简称。这是一个处理线程的工具包。
进程:是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础;简而言之:进程就是系统中正在运行的一个应用程序,程序一旦运行就是进程
线程:是操作系统进行运算调度的最小单位;简而言之:系统分配处理器时间资源的基本单位。
协程:可以在一个线程内部创建多个协程,这些协程之间可以共享同一个线程的资源,协程是在一个线程内部运行的,不需要操作系统的介入,可以在用户空间内实现协作式多任务处理,因此,协程的创建和销毁开销很小,可以高效的利用资源。(java19之后才支持虚拟线程,也就是协程)
进程之间不共线全局变量,线程之间共享全局变量
//线程的状态
新建状态(new)->创建线程对象->线程没有运行start方法
就绪状态(Runnable)->start方法->状态的线程有可能正在执⾏,也有可能没有正在执⾏,等待被分配CPU资源
阻塞状态(blocked)->无法获得锁对象->
等待状态(waiting)->wait方法->
计时状态(timed_waiting)->sleep方法->
结束状态(terminated)->全部代码运行完毕->run方法执行完毕或者捕获到了异常,终止了run方法
//wait与sleep的区别
sleep是Thread的静态方法,wait是object的方法,任何对象实例都能调用。
sleep不会释放锁,他也不会占用锁。wait会释放锁,但调用它的前提是当下线程占有锁(代码要在sychronized中)
他们都可以被interrupted方法中断;
并发:底层程序交替执行,给人的感觉是同时发生
并行:同一时刻,程序可以分配到多个处理器,实现多任务并行执行。底层程序是同时执行。
public void start() {
synchronized (this) {
// zero status corresponds to state "NEW".
if (holder.threadStatus != 0)
throw new IllegalThreadStateException();
start0();//这里启动了一个新的线程
}
}
//start0的声明如下:
private native void start0();
也就是创建新线程启动的时候调用的是 native start0()
方法,而这些native
方法的注册是在Thread
对象初始化的时候完成的。
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing does. */
private static native void registerNatives();
static {
registerNatives();
}
...
}
Thread类有一个 registerNatives()
方法,该方法的主要作用就是注册一些本地方法供Thread类使用,如start0(),stop0()
等
registerNatives()
方法被放在静态代码块当中,所以在该类被加载的时候,方法就会执行。
方法 | 功能 | 说明 |
---|---|---|
public void start() | 启动⼀个新线程;Java虚拟机调⽤此线程的run⽅法 | start ⽅法只是让线程进⼊就绪,里面的代码不一定立刻运行(时间片还没有分配给它)。每个线程的start方法只能调用一次。 |
public void run() | 线程启动后调⽤该⽅法 | 如果在构造 Thread 对象时传递了Runnable参数,则线程启动后会调用Runnable中的run方法,否则默认不执行任何操作,但可以创建子对象来覆盖该方法的默认行为 |
public void setName(String name) | 给当前线程设置名字 | |
public void getName() | 获取当前线程的名字,线程存在默认名字:Thread-索引,主线程就是main | |
public static Thread currentThread() | 获取当前线程对象 | |
public static void sleep(long time) | 让当前线程休眠多少毫秒再继续执行 | |
public static native void yield() | 提示线程调度器尽⼒让出当前线程对CPU的使用 | |
public final int getPriority() | 获取当前线程的优先级 | |
public final void setPriority(int priority) | 更改此线程的优先级,默认是5 | java中规定线程优先级是1~10的整数,较大的优先级能提高该线程被CPU调度的机率 |
public void interrupt() | 中断这个线程 | 仅仅是设置线程的中断状态为true,不会停⽌线程 |
public static boolean interrupted() | 判断当前线程是否被打断,清除打断标记 | 返回当前线程的中断状态,通过检查中断标志位,判断当前线程是否被中断 |
public boolean isInterrupted() | 判断当前线程是否被打断,不清楚打断标记 | 2、将当前线程的中断状态设为false |
public state getState() | 获取线程状态 | Java 中线程状态是⽤ 6 个enum表示,分别是NEW,RUNNABLE,BLOCKED,WAITING,TIME_WAITING,TERMINATED |
public long getId() | 获取线程长整型的id | id唯一 |
public final void setDaemon(boolean on) | 将此线程设置为守护线程 | |
public final native boolean isAlive() | 判断线程是否存活 | |
public final void join() | 等待这个线程结束 | join() ⽅法的作⽤是调⽤线程等待该线程完成后,才能继续往下运⾏ |
run方法是同步方法,而start方法是异步方法
run方法的作用是存放任务代码,而start方法是启动线程
执行run方法不会产生新的线程,而start方法会产生新的线程
run⽅法可以被执⾏⽆数次,⽽star⽅法只能被执⾏⼀次,原因就在于线程不能被重复启动。
Thread.yield()
方法的作用是:暂停当前正在执⾏的线程对象(及放弃当前拥有的cup资源),并执⾏其他线程
sleep与yield的区别:
sleep是是当前线程进入停滞状态,执行sleep方法,指定时间内线程不会被执行。sleep ⽅法使当前运⾏中的线程睡眼⼀段时间,进⼊不可运⾏状态,这段时间的⻓短是由程序设定的
yield是使当前线程回到可执行状态,该线程可能会被马上执行。yield ⽅法使当前线程让出 CPU 占有权,但让出的时间是不可设定的
Blocked 进⼊ Runnable
想要从 Blocked 状态进⼊ Runnable 状态,我们上⾯说过必须要线程获得 monitor 锁,但是如果想进⼊其他状态那么就相对⽐较特殊,因为它是没有超时机制的,也就是不会主动进⼊
Waiting 进⼊ Runnable
Timed Waiting 进⼊ Runnable
总结:
线程池就是一个可以容纳多个线程的对象,其中的线程可以反复利用,省去了频繁创建线程对象的操作,从而过多的消耗资源
在所有的并发容器中,BlockingQueue是最为常见的一种,它是一个带阻塞功能的队列,当入队列时,若队列已满,则阻塞调用者;当出队列时,若队列已空,则阻塞调用者
ArrayBlockingQueue(核心是一把锁,两个条件)
它是一个用数组实现的环形队列,在构造函数中,要求传入数组的容量
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/** The queued items */
@SuppressWarnings("serial") // Conditionally serializable
final Object[] items;//数组
/** items index for next take, poll, peek or remove */
int takeIndex;//队列的头索引
/** items index for next put, offer, or add */
int putIndex;//队列的尾索引
/** Number of elements in the queue */
int count;//数组大小
/** Main lock guarding all access */
final ReentrantLock lock;//锁
/** Condition for waiting takes */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notEmpty;
/** Condition for waiting puts */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notFull;
/**
* Shared state for currently active iterators, or null if there
* are known not to be any. Allows queue operations to update
* iterator state.
*/
transient Itrs itrs;
//构造函数
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and the specified access policy.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
//put函数
public void put(E e) throws InterruptedException {
Objects.requireNonNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//可中断的lock
try {
while (count == items.length)
notFull.await();//若队列满了,则阻塞
enqueue(e);
} finally {
lock.unlock();
}
}
//take函数
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();//若队列为空,则阻塞
return dequeue();
} finally {
lock.unlock();
}
}
}
LinkedBlockingQueue
是一种基于单向链表的阻塞队列,因为队头和队尾是两个指针分开操作的,所以用了两个条件+两把锁,同时有一个Atomicleteger的原子变量记录count数
public class LinkedBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/** The capacity bound, or Integer.MAX_VALUE if none */
private final int capacity;//容量
/** Current number of elements */
private final AtomicInteger count = new AtomicInteger();
/**
* Head of linked list.
* Invariant: head.item == null
*/
transient Node<E> head;
/**
* Tail of linked list.
* Invariant: last.next == null
*/
private transient Node<E> last;
/** Lock held by take, poll, etc */
private final ReentrantLock takeLock = new ReentrantLock();
/** Wait queue for waiting takes */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notEmpty = takeLock.newCondition();
/** Lock held by put, offer, etc */
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notFull = putLock.newCondition();
}
private final ReentrantLock putLock = new ReentrantLock();
/** Wait queue for waiting puts */
@SuppressWarnings("serial") // Classes implementing Condition may be serializable.
private final Condition notFull = putLock.newCondition();
}