- 概念:在操作系统中,所有的运行中的任务通常对应一个进程,当一个程序进入到内存运行时,即变成一个进程,进程是运行过程中的程序,并具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位。
- 进程:是正在运行的程序,是系统进行组员分配和调用的独立单位,每个进程都有它自己的内存空间和系统资源。
- 进程有三个特性,独立性,动态性,并发性。
- 线程:是进程中的单个顺序控制流,是一条执行路径
- 单线程:一个进程如果有一条执行路径,则称为单线程程序
- 多线程:一个进程如果有多条执行路径,则称为多线程程序
- 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。最简单的比喻多线程就像火车的每一节车厢,而进程则是火车。车厢离开火车是无法跑动的,同理火车也不可能只有一节车厢。多线程的出现就是为了提高效率。同时它的出现也带来了一些问题。(来自百度百科)
实现方式
-
继承Thread类
-
写一个类继承Thread类,重写run()方法,线程要实现的功能,任务
-
在main方法中创建类的对象,低啊用红start()方法开启线程执行任务
public class ThreadDemo extends Thread { @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class Test01 { public static void main(String[] args) { ThreadDemo demo = new ThreadDemo(); Thread thread = new Thread(demo); /*demo.run();*/ thread.start(); } }
-
-
实现Runnable接口
- 定义一个类实现Runnable接口,重写接口中的run()方法
- 在main方法忠创建Thread类对象,把接口实现类对象放在参数中,调用Thread对象的start()方法
public class ThreadDemo implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(Thread.currentThread().getName()+":"+i); } } } public class Test01 { public static void main(String[] args) { ThreadDemo demo = new ThreadDemo(); Thread thread = new Thread(demo); /*demo.run();*/ thread.start(); } }
** 如果调用run()方法,是不会开启线程的。
生命周期
- 新建:线程的对象刚刚创建
- 就绪:线程调用了start()方法,在这个状态,线程等待CPU执行
- 执行:线程抢到了CPU的执行权,可以执行了
- 阻塞:线程调用了sleep()方法或wait(1000)方法,就会进入阻塞状态,这个时候,线程没有执行权,阻塞结束了,会进入就绪状态
- 无限阻塞:线程调用了wait()方法,进入无限阻塞状态,需要别的线程叫醒它,它才能够进入就绪状态
- 死亡:执行任务结束就进入死亡
解决线程死锁的两种方式
解决线程安全问题的两种方式
1.同步代码块
synchronized(任意对象){
}
2.同步方法:
成员同步方法:它的同步锁是this
public synchronized void sell(){
}
静态同步方法:它的同步锁是类名.class
public static synchronized void sell(){
}
3.Lock锁
Lock lock = new ReentrantLock();
lock.lock();
lock.unlock();
try{
lock.lock();
...
}finally{
lock.unlock();
}
线程池
线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。
线程池家族
1.线程池的最上层接口是Executor,这个接口定义了一个核心方法execute(Runnabel command),这个方法最后被ThreadPoolExecutor类实现,这个方法是用来传入任务的。而且ThreadPoolExecutor是线程池的核心类,此类的构造方法如下:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
corePoolSize:核心线程池的大小,如果核心线程池有空闲位置,这是新的任务就会被核心线程池新建一个线程执行,执行完毕后不会销毁线程,线程会进入缓存队列等待再次被运行。
maximunPoolSize:线程池能创建最大的线程数量。如果核心线程池和缓存队列都已经满了,新的任务进来就会创建新的线程来执行。但是数量不能超过maximunPoolSize,否侧会采取拒绝接受任务策略,我们下面会具体分析。
keepAliveTime:非核心线程能够空闲的最长时间,超过时间,线程终止。这个参数默认只有在线程数量超过核心线程池大小时才会起作用。只要线程数量不超过核心线程大小,就不会起作用。
unit:时间单位,和keepAliveTime配合使用。
workQueue:缓存队列,用来存放等待被执行的任务。
threadFactory:线程工厂,用来创建线程,一般有三种选择策略。 (ArrayBlockingQueue;LinkedBlockingQueue;SynchronousQueue;)
handler:拒绝处理策略,线程数量大于最大线程数就会采用拒绝处理策略,四种策略为 :
- ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
Executor接口有一个子接口ExecutorService,ExecutorService的实现类为AbstracExecutorService,而ThreadPoolExcutor正是AbstrcExecutorService的子类。
ThreadPoolExecutor还有两个常用的方法shutdown和submit,两者都用来关闭线程池,但是后者有一个结果返回。
import java.util.concurrent.*;
public class BoxDemo {
public static void main(String[] args) {
Box b = new Box();
Producer p = new Producer(b);
Customer c = new Customer(b);
//创建一个同步队列,容量为1(线程工厂)
BlockingQueue<Runnable> bq = new ArrayBlockingQueue<>(1);
//创建一个线程池,核心线程的大小为2,能创建的最大线程数为5。
// new ThreadPoolExecutor.AbortPolicy()当超过最大线程任务时,
// 丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor pool = new ThreadPoolExecutor(2,5,60L,
TimeUnit.MILLISECONDS,bq,new ThreadPoolExecutor.AbortPolicy());
pool.execute(p);
pool.execute(c);
pool.shutdown();
}
}
####ForkJoinPool
1.说明:提供ForkJoinPool支持将一个大任务拆分成多个小任务,再把小任务合并成总的计算结果,是一种特殊的线程池。
发布-订阅框架
1.该框架是基于异步响应流,非常方便地处理异步线程之间的流数据交换,该框架不需要使用数据中心来缓冲数据,同时具有非常高效的性能。
2.该框架使用Flow类的4个静态内部接口作为核心API:
方法名 | 说明 |
---|---|
Flow.Processor<T,R> | 作为订阅者和发布者的组件。 |
Flow.Publisher | 订阅者接收的项目(和相关控制消息)的生产者。 |
Flow.Subscriber | 消息的接收者。 |
Flow.Subscription | 代表发布者和订阅者之间的链接纽带,订阅者可以使用request()方法来获取数据项,也可以使用cancel()方法来取消订阅。 |
为了处理一些通用发布者的场景,Flow.Publisher提供了一个SubmissionPublisher实现类,它可向当前订阅者异步提交非空的数据项,直到它被关闭,每个订阅者都能以相同的顺序接收新提交的数据项。
package com.thread;
import java.util.List;
import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;
public class PubSubTest {
public static void main(String[] args) {
//创建一个发布者
SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
//创建订阅者
MySubscriber<String> subscriber= new MySubscriber<>();
//注册订阅者
publisher.subscribe(subscriber);
System.out.println("开始发布数据........");
List.of("java","python","Android","IOS","Lua","Redis").forEach(m ->{
publisher.submit(m);
try{
Thread.sleep(500);
}catch (Exception ex){}
});
publisher.close();
synchronized ("error")
{
try{
"error".wait();
}
catch (Exception ex){}
}
}
}
class MySubscriber<T> implements Flow.Subscriber<T> {
private Flow.Subscription subscription;
@Override
//订阅时触发该方法。
public void onSubscribe(Flow.Subscription subscription) {
this.subscription = subscription;
subscription.request(1);
}
@Override
//接收到数据时触发该方法。
public void onNext(T item) {
System.out.println("接收到数据"+item);
subscription.request(1);
}
@Override
//订阅出错调用该方法
public void onError(Throwable throwable) {
throwable.printStackTrace();
synchronized ("error")
{
"error".notifyAll();
}
}
@Override
//订阅结束时触发该方法
public void onComplete() {
System.out.println("订阅结束");
synchronized ("error")
{
"error".notifyAll();
}
}
}