先上图,有点长,比较碎,有xmind文件......,详细内容均在图片里介绍了,提供了PDF文件
1.线程简介
进程是操作系统中正在执行的不同的应用程序,例如:我们可以同时打开Word和记事本
线程是一个应用程序进程中不同的执行路径。进程是不活泼的。进程从来不执行任何东西,它只是线程的容器。线程总是在某个进程环境中创建的,而且它的整个寿命期都在该进程中。
进程是线程的容器。一个进程可以有多个线程,至少有一个线程。而一个线程只能在一个进程的地址空间活动
2.多线程的三种实现方式
3.线程常用方法
3.1.Thread
//Thread
public class MyThread extends Thread{
@Override
public void run(){
for (int i = 0; i < 10; i++) {
System.out.println(getName() + "run方法里的函数");
}
}
}
//创建开启线程
public abstract class ThreadDemo {
public static void main(String[] args) {
// 创建 MyThread 类的实例
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
t1.setName("线程1");
t2.setName("线程2");
// 启动线程
t1.start();
t2.start();
}
}
3.2.Runnable
//Runnable
public class MyRunnable implements Runnable {
@Override
public void run() {
//获取当前线程的对象
Thread thread = Thread.currentThread();
System.out.println(thread.getName() + "MyRunnable.run方法实列");
}
}
//创建开启线程
public class ThreadDemo {
public static void main(String[] args) {
/*
* 多线程的第二中启动方式:
* 1.自己定义一个类实现Runnable方法
* 2.重写里面的run方法
* 3.创建自己的类的对象
* 4.创建一个Thread类的对象,并开启线程
* */
MyRunnable myRunnable = new MyRunnable();
Thread thread1 = new Thread(myRunnable);
Thread thread2 = new Thread(myRunnable);
thread1.setName("线程1");
thread2.setName("线程2");
thread1.start();
thread2.start();
}
}
3.3.Callable
//Callable
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
return 100;
}
}
//第三种方式
public class ThreadDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
/*
*多线程的第三种是实现方式
* 特点:可以获取到多线程运行的结果
* 1、创建一个类MyCallable实现Callable接口
* 2、重写call方法(有返回值的,表示多线程运行的结果)
* 3、创建MyCallable的对象(表示多线程要执行的任务)
* 4、创建FutureTalk的对象(作用是管理多线程运行的结果)
* 5、创建Thread类的对象,并启动(表示线程)
* */
//创建MyCallable的对象(表示多线程要执行的任务)
MyCallable myCallable = new MyCallable();
//创建FutureTalk的对象(作用是管理多线程运行的结果
FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
//创建对象
Thread thread = new Thread(futureTask);
//启动线程
thread.start();
//获取多线程运行的结果
Integer i = futureTask.get();
System.out.println(i);
}
}
4.锁
锁是一种同步机制,可以用来协调多个线程的并发访问,以保证对共享资源的安全访问。可以理解为防止一件东西同时被多个人使用。用于保护线程安全的一种机制。
4.1.synchronized锁
static int ticket = 0;
@Override
public void run() {
while (true) {
synchronized (MyThread.class) {
if (method()) break;
}
}
}
private synchronized boolean method() {
if (ticket < 99){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(Thread.currentThread().getName() + "正在卖第 " + ticket + "票!");
}else{
return true;
}
return false;
}
4.2.lock锁
public class MyThread extends Thread {
static int ticket = 100;
static Lock lock = new ReentrantLock();
@Override
public void run() {
/*
* Lock实现提供比使用sunchronized方法和语句更广泛的锁定操作
* lock中提供了获得锁和释放锁的方法
* void lock():获得锁
* void unlock():释放锁
*
* Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
* ReentrantLock的构造方法
*
* ReentrantLock():创建一个ReentrantLock的实例
*
* */
while (true) {
//同步代码块,lock上锁
lock.lock();
if (ticket == 100){
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
//释放锁
lock.unlock();
}
ticket++;
System.out.println(Thread.currentThread().getName() + "在卖第" + ticket);
}
}
}
}
4.3.死锁
详细介绍可看开头的图片
5.生产者和消费者问题
5.1.线程的等待和唤醒
public class Desk {
/*
* 控制生产者和消费者的执行
* */
//是否有面条 0:没有 1:有
public static int foodFlag = 0;
//总个数
public static int count =10;
//锁对象
public static Object lock = new Object();
}
//消费者
public class Cook extends Thread {
/*
* 消费者
* */
@Override
public void run() {
while (true) {
synchronized (Desk.lock){
if(Desk.count == 0){
break;
}else{
//判断桌子上是否有食物
//如果有就等待,没有就唤醒
if(Desk.foodFlag == 1){
try {
//等待
Desk.lock.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else{
System.out.println("厨师做了一碗面条");
//修改桌子状态
Desk.foodFlag = 1;
//唤醒
Desk.lock.notifyAll();
}
}
}
}
}
}
//生产者
public class Foodie extends Thread {
/*
生产者
* */
@Override
public void run() {
/*
* 1.循环
* 2.同步代码块
* 3.判断共享数据是否到了末尾(到了末尾)
* 4.判断共享数据是否到了末尾(没有到达末尾,执行核心逻辑)
*
* */
while (true) {
synchronized (Desk.lock){
//先判断桌子上是否有面条
if(Desk.count == 0){
break;
}else{
if(Desk.foodFlag == 0){
//如果没有
try {
Desk.lock.wait();//让当前线程和锁进行绑定
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}else{
//把吃的总数-1
Desk.count--;
//如果有就开吃
System.out.println(Desk.count + "碗!!!");
//吃完之后,唤醒厨师继续做
Desk.lock.notifyAll();
//修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
5.2.阻塞队列
public class ThreadDemo {
public static void main(String[] args) {
/*
* 阻塞队列的继承结构
* 接口:Iterable
* Collection
* Queue
* BlockingQueue
* 实现类L:ArrayBlocking : 数组 ,有界
* LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为int的最大值+
*
* */
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
Cook cook = new Cook(queue);
Foodie foodie = new Foodie(queue);
cook.start();
foodie.start();
}
}
6.线程池
线程池是一种多线程处理形式,处理过程中可以将任务添加到队列中,然后在创建线程后自动启动这些任务。
6.1.ExecutorService
public class MyThreadPoolDemo {
public static void main(String[] args) {
/*
public static ExecutorService newCachedThreadPool() 创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThread) 创建有上限的线程池
*/
//1.获取线程池对象,池子本身是空的,提交任务的时,池子会创建新的线程对象,
// 任务执行完毕,线程归回给池子,下回再次提交任务时,不需要创建新的线程,直接复用已有的线程
ExecutorService pool = Executors.newCachedThreadPool();
ExecutorService pool1 = Executors.newFixedThreadPool(3);
//提交任务
pool.submit(new MyRunnable());
pool.submit(new MyRunnable());
//销毁线程池
pool.shutdown();
}
6.2.ThreadPoolExecutor
public class ThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3,
6,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
threadPoolExecutor.submit(new MyRunnable());
}
}