NEW 新建
RUNNABLE 准备就绪
BLOCK 阻塞
WAITING 不见不散
TIMED_WAITING 过时不候
TERMINATED 中结
sleep 是Thread的静态方法,wait是Object的方法,任何对象实例都可以调用。
sleep不会释放锁,它也不需要占用锁。若当前线程占有锁,那么wait会释放锁。
他们都可以被interrupted 方法中断
两方法在哪睡,在哪醒
一次只能取得一个任务,并执行这个任务
多项工作一起执行,之后再汇总。例如:泡面,一边烧水一边放调料
同一时刻多个线程访问同一个资源,多线程对一个点。例如:春运抢票,电商秒杀
Monitor 监视器,所说的锁
是一种同步机制,保证同一个时间,只有一个线程访问被保护数据或者代码
jvm同步基于进入和退出,使用管程对象实现的
自定义线程:new Thread
守护线程:运行在后台,没有用户线程,都是守护线程,JVM就会结束。比如垃圾回收
public class Main {
public static void main(String[] args) {
Thread aa = new Thread(() -> {
System.out.println(Thread.currentThread().getName() +
"::" + Thread.currentThread().isDaemon());
while (true) {
}
}, "aa");
// aa.isDaemon
aa.start();
System.out.println(Thread.currentThread().getName()+"over");
}
}
是一种同步锁,他修饰的对象有以下几种:
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是"{}"括起来的代码
修饰一个方法,被修饰的方法称为同步方法,起作用范围是整个方法,作用的对象是调用这个方法的对象
修改一个静态方法,其作用的范围就是整个静态方法,作用的对象是这个类的所有对象
修改一个类,其作用方位是 synchronized 后面括号括起来的部分,作用的对象是这个类所有对象
package sync;
class Ticket{
private int number = 30;
public synchronized void sale(){
// 判断是否还有票
if (number > 0){
System.out.println(Thread.currentThread().getName() +
" : 卖出: " +(number--) + " 剩下: " + number);
}
}
}
public class saleTicker {
// 创建资源类
// 定义属性和操作方法
public static void main(String[] args) {
Ticket ticket = new Ticket();
// 创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++){
ticket.sale();
}
}
}, "AA").start();
// 创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++){
ticket.sale();
}
}
}, "BB").start();
// 创建三个线程
new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0; i < 40; i++){
ticket.sale();
}
}
}, "CC").start();
}
}
Lock与 Synchronized 区别:
Lock 不是java 语言内置的,synchronized 是java语言关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问。
Lock需要手动释放锁
在 synchronized发生异常时会在自动释放线程占有的锁,因此不会发生死锁现象;Lock需要主动释放锁才会避免死锁现象,需要在finally块中释放锁。
Lock可以让等待锁的线程响应中断,而synchronized不行会一直等待
通过Lock 可以知道有没有成功获取锁
Lock可以提高多个线程进行读操作的频率
package lockSale;
class share{
private int num = 0;
// +1
public synchronized void incr() throws InterruptedException {
if (num == 0){
num++;
System.out.println(Thread.currentThread().getName()+"::"+ num);
}else {
this.wait();
}
this.notify();
}
public synchronized void del() throws InterruptedException {
if (num == 1){
num--;
System.out.println(Thread.currentThread().getName()+"::"+ num);
}else {
this.wait();
}
this.notify();
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
share share = new share();
new Thread(() ->{
for (int i = 0; i < 10; i++){
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"AA").start();
new Thread(() ->{
for (int i = 0; i < 10; i++){
try {
share.del();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"BB").start();
}
}
if判断只判断一次,wait在哪睡在哪醒,唤醒时会+1,然后加的线程抢到锁,再加1就会导致出现2的情况
package lockSale;
class share{
private int num = 0;
// +1
public synchronized void incr() throws InterruptedException {
while (num == 0){
num++;
System.out.println(Thread.currentThread().getName()+"::"+ num);
}else {
this.wait();
}
this.notify();
}
public synchronized void del() throws InterruptedException {
while (num == 1){
num--;
System.out.println(Thread.currentThread().getName()+"::"+ num);
}else {
this.wait();
}
this.notify();
}
}
public class ThreadDemo1 {
public static void main(String[] args) {
share share = new share();
new Thread(() ->{
for (int i = 0; i < 10; i++){
try {
share.incr();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"AA").start();
new Thread(() ->{
for (int i = 0; i < 10; i++){
try {
share.del();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"BB").start();
}
}
条件判断必须加while。
ArryList()不安全,add方法没有加 synchronized 关键字
解决方案:
Vector(古老):add方法 加了synchronized
实现Collection的synchronizedList方法
Collection.synchronizedList(new ArrayList<>());
使用juc的CopyOnWriteArrayList:
new CopyOnWriteArrayList<>();
写时复制技术
每次写时复制一个原集合,再写入,合并,最后读取复制集合
HashSet()不安全解决方案:
CopyOnWriteArraySet:
new CopyOnWriteArraySet<>();
HashMap()不安全解决方案:
new CopyOnWriteArrayMap<>();
注意:如果synchronized加载static上就锁的是字节码 .class 文件。
private Lock lock = new ReentrantLock(false);
即在ReentrantLock()内可填入 true(fair) & false(true)
默认采用非公平锁,效率高,非公平锁获取资源会询问,降低了效率。
synchronized 隐式
Lock 显示
可重入锁,可以实现自由进出多层锁的功能。
package sync;
// 可重入锁
public class reAcc {
public static void main(String[] args) {
Object o = new Object();
new Thread(()->{
synchronized (o){
System.out.println(Thread.currentThread().getName()+"outer");
synchronized (o){
System.out.println(Thread.currentThread().getName()+"middle");
synchronized (o){
System.out.println(Thread.currentThread().getName()+"inner");
}
}
}
},"reAcc").start();
}
}
可重入锁也叫递归锁
在使用lock方法实现可重入锁时,如果内层锁不释放,也可以正常使用,但是新线程无法获取锁。
两个或者两个以上进程在执行过程中,因为抢夺资源造成一种互相等待的现象,如果没有外力干涉,无法再执行下去。
产生死锁三种原因:
系统资源不足
进程运行推进顺序不当
资源分配不当
package sync;
public class dieLock {
public static void main(String[] args) {
Object a = new Object();
Object b = new Object();
new Thread(()->{
synchronized (a){
System.out.println(Thread.currentThread().getName()+ "持有a获取b");
synchronized (b){
System.out.println(Thread.currentThread().getName()+ "获取b");
}
}
},"A").start();
new Thread(()->{
synchronized (b){
System.out.println(Thread.currentThread().getName()+ "持有b获取a");
synchronized (a){
System.out.println(Thread.currentThread().getName()+ "获取a");
}
}
},"B").start();
}
}
jps:类似linux ps -ef 查看进程
jstack:jvm自带堆栈跟踪工具
继承Thread类
实现Runnable接口
Callable接口
线程池
Callable有返回值
Callable抛出异常
Callable用call()
Callable不能直接替换Runnable,需要使用FutureTask中间类来创建
FutureTask未来任务,累加线程,最后总和
package sync;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Callable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask futureTask = new FutureTask<>(() ->{
System.out.println(Thread.currentThread().getName() + "Callable");
return 1024;
});
new Thread(futureTask, "aa").start();
while (!futureTask.isDone()){
System.out.println("wait...");
}
System.out.println(futureTask.get());
System.out.println(Thread.currentThread().getName()+"::isDne");
}
}
减少计数
package juc;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
//设置初始值
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i<= 6; i++){
new Thread(()->{
System.out.println(Thread.currentThread().getName() + " 号同学离开了教室");
countDownLatch.countDown();// 计数器减一
}, String.valueOf(i)).start();
}
// 阻塞
countDownLatch.await();
System.out.println(Thread.currentThread().getName()+"锁门");
}
}
循环阻塞
package juc;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo {
private static final int NUM = 7;
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(NUM, ()->{
System.out.println("makabak");
});
for (int i = 0; i<7;i++){
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"done");
//
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
}
}
信号灯
package juc;
import java.util.Random;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphoreDemo {
public static void main(String[] args) {
//创建
Semaphore semaphoreDemo = new Semaphore(3);
// 模拟
for (int i = 1; i<=6;i++){
new Thread(() -> {
try {
semaphoreDemo.acquire();
System.out.println(Thread.currentThread().getName() + "抢到了");
// 设置随机时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "离开车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
semaphoreDemo.release();
}
},String.valueOf(i)).start();
}
}
}
只有一个线程可以获取资源,解锁后其他线程可以争抢资源。
多个线程获取资源,但是会添加版本号,再修改时,比较二者版本号是否一致,一致进行修改。
上锁:
lock.readLock.lock;
上锁:
lock.writLock.lock;
读写锁都存在死锁问题,关键:做好自己的事,不要想着多个功能呢一起完成。
一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享。
将写锁降级为读锁
获取写锁 --> 获取读锁 --> 释放写锁 --> 释放读锁
package juc;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class Demo1 {
public static void main(String[] args) {
ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
ReentrantReadWriteLock.ReadLock readLock = reentrantReadWriteLock.readLock();
ReentrantReadWriteLock.WriteLock writeLock = reentrantReadWriteLock.writeLock();
// 锁降级
writeLock.lock();
System.out.println("giao~!");
readLock.lock();
System.out.println("___read");
writeLock.unlock();
readLock.lock();
}
}
读锁无法升级为写锁
俩线程,一个拿一个放,在阻塞时挂起线程。
ArrayBlockingQueue: 由数组结构组成的一个有界的阻塞队列。
LinkedBlockingQueue: 由链表结构组成的有界(默认大小为integer.MAX_VALUE)阻塞队列。
DelayQueue:使用有衔接队列实现的延迟无界阻塞队列。
PriorityBlockingQueue:支持优先级排序的无界阻塞队列。
SynchronousQueue:不存储元素阻塞队列,单个元素队列
LinkedTransferQueue:由链表结构组成的无界阻塞队列。
LinkedBlockingDeque:由链表组成的双向阻塞队列
方法类型 | 抛出异常 | 特殊值 | 阻塞 | 超时 |
---|---|---|---|---|
插入 | add(e) | offer(e) | put(e) | offer(e, time,unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
检查 | element() | peek() | 不可用 | 不可用 |
是一种线程使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。
线程池做的工作只要是控制运行的线程数量,处理过程种将任务放入队列,然后再线程创建后启动这些任务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来执行。
降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁的小号
提高响应速度
提高线程的可管理性,统一分配,调用和监控
Java种线程池是通过EXecutor框架实现的,
线程池使用方式:
Executors.newFixedThreadPool(int):一池N线程
Executors.newSingleThreadPool():一个任务一个任务的执行,一池一线程
Executors.newCachedThreadPool():线程池根据需求创建线程,可扩容,遇强则强
package pool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo1 {
public static void main(String[] args) {
// 一池5线程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);
// 一池1线程
ExecutorService threadPool2 = Executors.newSingleThreadExecutor();
// 一池自动扩容线程
ExecutorService threadPool3 = Executors.newCachedThreadPool();
try {
for (int i = 0; i< 10;i++){
threadPool3.execute(() ->{
System.out.println(Thread.currentThread().getName() + "办理业务");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPool3.shutdown();
}
}
}
上述3种方式都是new 一个ThreadPoolExcutor来实现线程池创建
参数 | 释义 |
---|---|
int corePoolSize | 常驻线程数量(核心) |
int maximumPoolSize | 最大线程数量 |
long keepAliveTime | 线程存活时间 |
TimeUnit | 时间单位 |
BlockingQueue |
阻塞队列 |
ThreadFactory threadFactory | 线程工厂 |
RejectedExecutionHandler handler | 拒绝策略 |
JDK内置拒绝策略:
AbortPolicy(默认):直接抛出RejectedExecutionException 异常阻止系统正常运行。
CallerRunsPolicy:不会抛弃任务,也不会抛出异常,而是将某些任务退回到调用者,从而降低新任务的流量。
DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前仍无加入队列中尝试再次提交当前业务。
DiscardPolicy:默默丢弃无法处理的任务,不做任何处理,也不抛异常。
实际使用线程池,不适用之前三种方式,因为其最大值为Integer.MAX_VALUE可能会堆积大量请求。导致OOM。
package pool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolDemo2 {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
2
, 5
, 2L
, TimeUnit.SECONDS
, new ArrayBlockingQueue<>(3)
, Executors.defaultThreadFactory()
, new ThreadPoolExecutor.AbortPolicy()
);
try {
for (int i = 0; i < 7; i++){
threadPoolExecutor.execute(() ->{
System.out.println(Thread.currentThread().getName() + "办理");
});
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
threadPoolExecutor.shutdown();
}
}
}
Fork
Join