什么是进程:
你把它理解成一个软件
什么是线程:
你把它理解成软件里面的一个功能,做的事情
什么是多线程:
你把它理解成 软件里面的某一个功能,原先是一个人累死累活的在那里完成,现在好了,多个人一起完成,轻松又快活
什么是线程不安全:
你把它理解成 软件里面的某一个功能,原先是一个人累死累活的在那里完成,虽然累,但是,数据不会出错,但是现在多个人一起来弄,结果,张三读取的数据是被李四修改过的数据,不是最新的数据,这样就是线程不安全的
什么是线程安全:
你把它理解成 软件里面的某一个功能,原先是一个人累死累活的在那里完成,虽然累,但是,数据不会出错,但是现在多个人一起来弄,我让你们排好队,一个一个,有序的来操作数据,如果有人正在操作,我就让你在外面等着,必须等前面的人做完了自己的事情,你才能进去
什么是并行:
你把它理解成 软件里面的某一个功能,同时进行多个任务
什么是并发:
你把它理解成 软件里面的某一个功能,一下子来了很多请求,如果不处理,会导致程序宕机,卡死等
线程不安全和线程安全,
针对ArrayList HashSet HashMap 来进行举例
ArrayLists 线程不安全 ; Vector、Collections 、CopyOnWriteArrayList 线程安全
HashSet 线程不安全 ; CopyOnWriteArraySet 线程安全
HashMap 线程不安全 ; ConcurrentHashMap 线程安全
下面用代码的方式,来进行模拟一下多线程,
需求:四位售票员进行卖票操作,加锁
package com.japhet.util;
//卖票
class Ticket{
//总票数
private int ticket = 3000;
//卖票逻辑递减
public synchronized void seal(){
if(ticket<=0){
return;
}
ticket--;
System.out.println(Thread.currentThread().getName()+"票还剩余"+ticket);
}
}
public class ThreadUtils {
public static void main(String[] args) {
Ticket ticket = new Ticket();
//售票员1进行卖票
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 4000; i++) {
ticket.seal();
}
}
},"AA").start();
//售票员2进行卖票
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 4000; i++) {
ticket.seal();
}
}
},"BB").start();
//售票员3进行卖票
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 4000; i++) {
ticket.seal();
}
}
},"CC").start();
//售票员4进行卖票
new Thread(()->{
for (int i = 0; i < 4000; i++) {
ticket.seal();
}
},"DD").start();
}
}
下面用代码的方式,来进行模拟一下线程之间的通讯,
需求:因为是多线程,当11线程抢到资源,执行完自己的程序之后,他是不知道下一次会是谁成功的抢到资源,那我可以进行设置谁能抢到资源 (通过 await 等待 signal 通知某个线程执行),这里就让11线程执行了就让22线程去执行,22线程执行了就让33线程去执行,33线程执行了,就让11线程执行,依次循环10次这个动作
package com.japhet.util;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class Srouce{
// 线程标识
private int temp = 1;
// 锁
private Lock lock = new ReentrantLock();
// 三个线程
private Condition condition1 = lock.newCondition();
private Condition condition2 = lock.newCondition();
private Condition condition3 = lock.newCondition();
public void test(){
lock.lock();
try {
while (temp!=1){//防止虚假唤醒,所以得用while循环
condition1.await();
}
System.out.println("线程1111进来了");
temp = 2;//修改标识,
condition2.signal();//通知线程2去干活
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void test2(){
lock.lock();
try {
while (temp!=2){//防止虚假唤醒,所以得用while循环
condition2.await();
}
System.out.println("线程2222进来了");
temp = 3;//修改标识,
condition3.signal();//通知线程2去干活
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
public void test3(){
lock.lock();
try {
while (temp!=3){//防止虚假唤醒,所以得用while循环
condition3.await();
}
System.out.println("线程3333进来了");
temp = 1;//修改标识,
condition1.signal();//通知线程2去干活
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
public class ThreadUtils2 {
public static void main(String[] args) {
Srouce srouce = new Srouce();
new Thread(()->{
for (int i = 0; i < 10; i++) {
srouce.test();
}
},"11").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
srouce.test2();
}
},"22").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
srouce.test3();
}
},"33").start();
}
}
什么是死锁
多个线程同事进行操作
线程A持有A锁
线程B持有B锁
程序还在进行中,都没有释放锁,然后线程A要获取线程B的内容 ,同时,线程B也要获取线程A的内容,那这个时候,线程A、B都处于互相等待的过程中,就会陷入死循环,你等我释放,我也再等你释放
什么是公平锁和非公平锁
非公平锁:(速度快,效率高)
比如我有三个线程 A,B,C ,当我运行的时候,可能A线程的能力强,那么一套程序运行下来,A线程的运行概率多很多,其他B,C线程基本上没怎么动,会被饿死,这个就是非公平锁的概念
公平锁:(速度慢,效率低)
比如我有三个线程 A,B,C ,当我运行的时候,会相对公平的分摊到每个线程里面去,看源码
FutureTask 未来任务使用demo
package com.japhet.util;
import java.util.concurrent.FutureTask;
public class FutureTaskDemo {
public static void main(String[] args) {
System.out.println("主线程开始....."+Thread.currentThread().getName());
FutureTask<Integer> futureTask = new FutureTask<>(()->{
System.out.println("--------"+Thread.currentThread().getName());
return 1;
});
new Thread(futureTask).start();
try {
while (!futureTask.isDone()){
System.out.println("线程还没有执行完,等待...."+Thread.currentThread().getName());
}
Integer num = futureTask.get();
Integer num2 = futureTask.get();
System.out.println("最后结果 "+num);
System.out.println("最后结果2 "+num2);
System.out.println("主线程结束....."+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
}
效果图
什么是阻塞队列 (BlockingQueue)
所谓队列:即 先进先出 ,队列有大小限制
线程A可以往队列里面放元素,线程B可以获取队列里面的元素,如果这个队列元素被线程A放满了,就阻塞状态 ; 如果线程B把队列里面的数据获取完了,也成阻塞状态
什么是线程池 (Executors)
避免资源浪费开销,用到线程就开一个线程,不用线程就关闭线程,这种频繁的开关,就会造成资源浪费
创建线程池
package com.japhet.util;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolDemo {
public static void main(String[] args) {
// 定义线程池里面初始化10个线程
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 定义线程池里面就1个线程
ExecutorService executorService2 = Executors.newSingleThreadExecutor();
//定义线程池里面就n个线程,可扩容,根据你调用
ExecutorService executorService3 = Executors.newCachedThreadPool();
try {
for (int i = 0; i < 20; i++) {
final int x = i;
// executorService.execute(()->{
// executorService2.execute(()->{
executorService3.execute(()->{
System.out.println(x+"线程---"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
// 关闭
// executorService.shutdown();
// executorService2.shutdown();
executorService3.shutdown();
}
}
}
(注意: 不推荐使用这三种方式进行创建线程池,因为阿里巴巴java开发手册提到,如果用这三种方式进行创建,会造成OOM内存溢出,会堆积大量的请求,来不及处理,一般都是自己进行创建线程池,传入7个参数)
java创建线程池有哪几种,为什么用线程池,以及拒绝策略
为什么要用线程池:不需要反复的创建和销毁线程,因为比较耗资源,提高管理性,统一分配调优监控
newSingleThreadPool、newFixedThreadPool、newCachedThreadPool、newScheduledThreadPool、newWorkStealingPool
里面的参数:
corePoolSize : 表示线程池核心线程数,当初始化线程池时,会创建核心线程进入等待状态,即使它是空闲的,核心线程也不会被摧毁,从而降低了任务一来时要创建新线程的时间和性能开销。
maximumPoolSize : 表示最大线程数,意味着核心线程数都被用完了,那只能重新创建新的线程来执行任务,但是前提是不能超过最大线程数量,否则该任务只能进入阻塞队列进行排队等候,直到有线程空闲了,才能继续执行任务。
keepAliveTime : 表示线程存活时间,除了核心线程外,那些被新创建出来的线程可以存活多久。意味着,这些新的线程一但完成任务,而后面都是空闲状态时,就会在一定时间后被摧毁。
unit : 存活时间单位。
workQueue : 表示任务的阻塞队列,由于任务可能会有很多,而线程就那么几个,所以那么还未被执行的任务就进入队列中排队,队列我们知道是 FIFO 的,等到线程空闲了,就以这种方式取出任务。这个一般不需要我们去实现。
threadFactoy: 线程工厂
拒绝策略(handler):当workqueue满了,maximumPoolSize也满了,就应该要拒绝新的线程进来 ,常见四种拒绝策略
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务
自定义线程池
package com.japhet.util;
import java.util.concurrent.*;
//创建自定义线程池,
// 只能处理 循环里面的前8个,后面的12个就被拒绝策略给拒绝了
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 < 20; i++) {
final int x = i;
threadPoolExecutor.execute(()->{
System.out.println(x+"线程---"+Thread.currentThread().getName());
});
}
} catch (Exception e) {
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
}
}
}