Java并发编程

线程的基本知识:
线程和进程的区别
并行和并发的区别
线程创建的方式有哪些
runnable 和callable有什么区别
线程包括哪些状态,状态之间是如何变化的
在java中wait和sleep方法的不同
新建三个线程,如何保证他们按顺序执行
notify() 和 notifyAll()有什么区别
线程的run()和start() 有什么区别
如何停止一个正在运行的线程
线程中并发安全:
synchronized 关键字的底层原理
你谈谈JVM(Java 内存模型)
什么是CAS?
什么是AQS?
ReentrantLock的实现原理
synchronizedLock有什么区别
死锁产生的条件是什么?
如何进行死锁的诊断
请谈谈你对volatile的理解
聊一聊ConcurrentHashMap
导致并发程序出现问题的根本原因是什么?
线程池:
说一下线程池的核心参数(线程池的执行原理知道吗)
线程池中有哪些常见的阻塞队列
如何确定核心线程数
线程池的种类有哪些?
为什么不建议使用Executors创建线程池
使用场景:
线程池使用场景(你们项目中哪些地方用到线程池)
如何控制某一个方法允许并发访问线程的数量
谈谈你对ThreadLocal的理解

进程与线程的区别

程序由指令和数据组成的,但这些指令要运行,数据要读写,就必须将指令加载到CPU,数据加载到内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理IO的

当一个程序被运行,从磁盘中加载这个程序的代码至内存,这时就开始了一个进程。
在这里插入图片描述
向浏览器和txt文本可以被打开多氛,那么就是多实例进程,而向企业微信,微信只能打开一个,那么就是单实例进程。

一个线程就是一个指令流,将指令流中的一条指令以一定的顺序交给CPU执行

一个进程之内可以分为一到多个线程。
在这里插入图片描述
二者对比:

  • 进程是正在运行程序的实力,进程包含了线程,每个线程执行不同的任务
  • 不同的进程使用了不同的内存空间,在当前进程下的所有线程可以共享内存空间
  • 线程更加轻量,线程的上下文切换成本一般上要比进程上下文切换低(上下文切换指的是从一个线程切换到另一个线程)

并行与并发的区别

单核CPU

  • 单核CPU下线程实际还是串行执行的
  • 操作系统中有一个组件叫做任务调度器,将cpu的时间片(windows下时间片最小约为15毫秒)分给不同的程序,只是由于cpu在线程间(时间片很短)的切换非常的快,人类的感觉是同时运行的。
  • 总结为一句话就是:微观串行,宏观并行
  • 一般会将这种线程轮流使用CPU的做法成为并发(concurrent)

在这里插入图片描述

多核CPU

  • 每个核(core)都可以调度运行线程,这时候线程可以是并行的。

并行和并发有什么区别?

  • 并发(concurrent)是同一时间应对(dealing with)多件事情的能力
  • 并行(parallel)是同一时间动手做(doing)多件事情的能力
家庭主妇做饭,打扫卫生、给孩子喂奶,他一个人轮流交替做这多件事情,这时就是并发
家庭主妇雇佣了一个保姆,他们一起做这些事情,这是既有并发,也有并行(这时会产生竞争,例如一口锅,一个人用锅的时候,另一个人就得等待)
雇佣了3个保姆,一个专门做饭,一个专门打扫卫生,一个专门喂奶,互不干扰,这是是并行

并行和并发有什么区别呢?

现在多核CPU,在多核CPU下

  • 并发是同一时间应对多件事情的能力,多个线程轮流使用一个或多个CPU
  • 并行是同一时间动手做多件事情的能力,4核CPU同时执行4个线程

创建线程的方式有哪些?

继承Thread类

package com.an.thread;

/**
 * 创建线程的方式
 * 第一种继承 Thread接口 实现run方法
 *
 * @author An
 * @Date 2023/5/25 14:04
 */
public class CreateThread extends Thread {

    @Override
    public void run() {
        System.out.println("Thread run......");
    }

    public static void main(String[] args) {
        // 创建 CreateThread 对象
        CreateThread t1 = new CreateThread();
        CreateThread t2 = new CreateThread();

        // 调用start方法启动线程
        t1.start();
        t2.start();

    }
}

实现Runnable接口

package com.an.thread;

/**
 * 创建线程的方式
 * 第二种方式:实现runnable接口
 *
 * @author An
 * @Date 2023/5/25 14:11
 */
public class CreateRunnable implements Runnable {

    @Override
    public void run() {
        System.out.println("runnable run....");
    }

    public static void main(String[] args) {
        // 创建 CreateRunnable 对象
        CreateRunnable t1 = new CreateRunnable();
        CreateRunnable t2 = new CreateRunnable();

        // 调用run方法
        t1.run();
        t2.run();
    }
}

实现Callable接口

package com.an.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

/**
 * 线程创建的方式
 * 第三种方式:实现callable接口
 *
 * @author An
 * @Date 2023/5/25 14:15
 */
public class CreateCallable implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println(Thread.currentThread().getName());
        return "ok";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 创建CreateCallable 对象
        CreateCallable mc = new CreateCallable();
        // 创建 FutureTask对象
        FutureTask<String> ft = new FutureTask<String>(mc);
        // 创建Thread对象
        Thread t1 = new Thread();
        Thread t2 = new Thread();
        // 调用start方法启动线程
        t1.start();
        // 调用ft.get方法获取执行结果
        String result = ft.get();
        // 输出
        System.out.println(result);
    }
}

创建线程池

package com.an.thread;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 创建线程的方式
 * 第四种方式:创建线程池
 *
 * @author An
 * @Date 2023/5/25 14:23
 */
public class CreateExecutors implements Runnable {

    @Override
    public void run() {
        System.out.println("CreateExecutors run......");
    }

    public static void main(String[] args) {
        // 创建线程池对象
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        threadPool.submit(new CreateExecutors());

        // 关闭线程池
        threadPool.shutdown();
    }
}

实现Runnable和Callable接口有什么区别呢?

  1. Runnable接口的run方法没有返回值
  2. Callable接口的call方法有返回值,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果
  3. Callable接口的call方法允许抛出异常;而Runnable接口的run()方法的异常只能在内部消化,不能继续上抛

在启动线程的时候,可以使用run()方法吗?run() 和start()方法有什么区别?

package com.an.thread;

/**
 * start方法和run方法的区别
 *
 * @author An
 * @Date 2023/5/25 14:38
 */
public class StartAndRunDiff {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("running......");
            }
        };
        // run 表示封装要被线程执行的代码,可以被调用多次
        thread.run();
        //thread.run();
        //thread.run();

        // 用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次
        //thread.start();
        //thread.start();
        //thread.start();
    }


}

参考回答:

start():用来启动线程,通过该线程调用run方法执行run方法中所定义的逻辑代码,start方法只能被调用一次
run():封装了要被线程执行的代码,可以被调用多次
  1. 创建线程的方式有哪些?
    • 继承Thread类
    • 实现runnable接口
    • 实现callable接口
    • 线程池创建线程(项目中使用)
  2. runnable和callable有什么区别
    • Runnable接口的run方法没有返回值
    • Callable接口的call方法有返回值,需要FutureTask获取结果
    • Callable接口的call()方法允许抛出异常,而Runnable接口的run方法的异常只能内部消化,不能继续往上抛出
  3. run()和start()有什么区别?
    • start():用来启动线程,通过线程调用run方法执行run方法所定义额逻辑代码,start方法只能被调用一次
    • run():封装了要被线程执行的代码,可以被调用多次。

线程中有哪些状态,状态之间是如何变化的?

Thread类的内部有个state枚举里面描述了线程的状态:

public enum State {
    /**
         * Thread state for a thread which has not yet started.
         * 尚未启动的线程的线程状态
         */
    NEW,

    /**
         * Thread state for a runnable thread.  A thread in the runnable
         * state is executing in the Java virtual machine but it may
         * be waiting for other resources from the operating system
         * such as processor.
         * 可运行线程的线程状态
         */
    RUNNABLE,

    /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         * 线程阻塞等待监视器锁的线程状态
         */
    BLOCKED,

    /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         * 等待线程的线程状态
         */
    WAITING,

    /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         * 具有指定等待时间的等待线程的线程状态
         */
    TIMED_WAITING,

    /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         * 已终止线程的线程状态,线程已完成执行
         */
    TERMINATED;
}

线程中的状态执行的详情:

在这里插入图片描述

线程中主要包括哪些状态?

新建(NEW),可运行(RUNNABLE),阻塞(BLOCKED),等待(WAITING),时间等待(计时等待)(TIME_WAITING),终止(TERMINATED)

线程状态之间是如何变化的

  • 创建线程对象是新建状态
  • 调用了start()方法转变为可执行状态
  • 线程获取到了CPU的执行权,执行结束时终止状态
  • 可执行状态的过程中,如果没有获取CPU的执行权,可能会切换其他状态
    • 如果没有获取锁(synchronized或lock)进入阻塞状态,获得锁再切换成可执行状态
    • 如果线程调用了wait()方法进入等待状态,其他线程调用notify()唤醒后可以切换成可执行状态。
    • 如果线程调用了sleep(50)方法,进入计时等待状态,到时间可切换为可执行状态

线程的顺序执行

join方法

示例:

package com.an.thread;

/**
 * 线程的顺序执行join()方法
 *
 * @author An
 * @Date 2023/5/25 15:39
 */
public class ThreadJoin {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            System.out.println("t1");
        });

        Thread t2 = new Thread(() -> {
            try {
                t1.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("t2");
        });

        Thread t3 = new Thread(() -> {
            try {
                t2.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("t3");
        });

        t1.start();
        t2.start();
        t3.start();
    }

}

notify和notifyAll有什么区别

  • notifyAll:唤醒所有wait的线程
  • notify:只随机唤醒一个wait线程
package com.an.thread;

/**
 * 线程等待唤醒
 * notify和notifyAll的区别
 *
 * @author An
 * @Date 2023/5/25 15:47
 */
public class WaitNotify {

    static Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()+ "...wait...");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+ "...被唤醒了");
            }
        }, "t1");

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()+ "...wait...");
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println(Thread.currentThread().getName()+ "...被唤醒了");
            }
        }, "t2");

        t1.start();
        t2.start();

        Thread.sleep(2000);

        synchronized (lock) {
            // 随机唤醒一个wait等待的线程
            //lock.notify();
            // 唤醒所有wait等待的线程
            lock.notifyAll();
        }
    }

}

wait方法和sleep方法的不同

共同点:

wait(),wait(long)和sleep(long)的效果都是让当前线程暂时放弃CPU的使用权,进入阻塞状态

不同点

  1. 方法的归属不同
    • sleep(long)是Thread的静态方法
    • 而wait(),wait(long)都是Object的成员方法,每个对象都有
  2. 醒来机制不同
    • 执行sleep(long)和wait(long)的线程都会在等待相应毫秒后醒来
    • wait(long)和wait()还可以被notify唤醒,wait()如果不唤醒就一直等下去
    • 他们都可以被打断唤醒
  3. 锁特性不同(重点)
    • wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
    • wait方法执行后必须释放对象锁,允许其他线程获得该对象锁(我放弃cpu,但你们还可以用)
    • 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
package com.an.thread;

/**
 * wait和sleep方法有什么不同
 * <p>
 * 分为三点
 * * wait方法的调用必须先获取wait对象的锁,而sleep则无此限制
 * * wait方法执行后必须释放对象锁,允许其他线程获得该对象锁(我放弃cpu,但你们还可以用)
 * * 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
 *
 * @author An
 * @Date 2023/5/25 16:30
 */
public class WaitSleepCase {
    static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        illegalWait();

    }

    private static void illegalWait() throws InterruptedException {
        // 而sleep如果在synchronized代码块中执行,并不会释放对象锁(我放弃了cpu,你们也用不了)
        synchronized (LOCK) {
            LOCK.wait();
        }
    }

    private static void waiting() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    //get("t").debug("waiting...");
                    LOCK.wait();
                    //get("t").debug("running...end...");
                } catch (InterruptedException e) {
                    //get("t").debug("interrupted...end...");
                    throw new RuntimeException(e);
                }
            }
        }, "t1");
        t1.start();
        Thread.sleep(100);
        synchronized(LOCK) {
            //main.debug("other...");
        }
    }

    private static void sleeping() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            synchronized (LOCK) {
                try {
                    //get("t").debug("waiting...");
                    Thread.sleep(5000L);
                    //get("t").debug("running...end...");
                } catch (InterruptedException e) {
                    //get("t").debug("interrupted...end...");
                    throw new RuntimeException(e);
                }
            }
        }, "t1");
        t1.start();
        Thread.sleep(100);
        synchronized(LOCK) {
            //main.debug("other...");
        }
    }


}

停止一个正在执行的线程

通过标记正常退出

package com.an.thread;

/**
 * 如何停止一个正在运行的线程
 *
 * @author An
 * @Date 2023/5/25 16:51
 */
public class Interrupt1 extends Thread {
    volatile boolean flag = false;   // 线程执行退出标记

    @Override
    public void run() {
        while (!flag) {
            System.out.println("running...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建 Thread 对象
        Interrupt1 t1 = new Interrupt1();
        t1.start();
        // 主线程休眠6秒
        Thread.sleep(6000);
        // 更改标记为 true
        t1.flag = true;
    }
}

采用stop方法退出(不推荐)

package com.an.thread;

/**
 * 如何停止一个正在运行的线程
 * 第二种方法采用stop方法退出
 *
 * @author An
 * @Date 2023/5/25 16:51
 */
public class Interrupt2 extends Thread {
    volatile boolean flag = false;   // 线程执行退出标记

    @Override
    public void run() {
        while (!flag) {
            System.out.println("running...");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 创建 Thread 对象
        Interrupt2 t1 = new Interrupt2();
        t1.start();
        // 主线程休眠6秒
        Thread.sleep(6000);
        // 采用stop方法退出  (不推荐)
        t1.stop();
    }
}

第三种方法通过Iterrupt方法退出线程

package com.an.thread;

/**
 * 如何停止一个正在运行的线程
 * 第三种方法:使用interrupt方法中断线程
 *              打断阻塞的线程(sleep,wait,join)的线程,线程会抛出InterruptedException异常
 *              打断正常的线程,可以根据打断状态来标记是否退出线程
 *
 * @author An
 * @Date 2023/5/25 16:51
 */
public class Interrupt3 extends Thread {

    public static void main(String[] args) throws InterruptedException {
        // 1. 打断阻塞的线程
        /*Thread t1 = new Thread(() -> {
            System.out.println("t1 正在运行...");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1");
        t1.start();
        Thread.sleep(500);
        t1.interrupt();
        System.out.println(t1.isInterrupted());*/

        // 2. 打断正常的线程
        Thread t2 = new Thread(() -> {
            while (true) {
                Thread currentThread = Thread.currentThread();
                boolean interrupted = currentThread.isInterrupted();
                if (interrupted) {
                    System.out.println("打断状态:" + interrupted);
                    break;
                }
            }
        }, "t1");
        t2.start();
        Thread.sleep(500);
        t2.interrupt();
    }
}

如何停止一个正在运行的线程?

有三种方式可以停止线程:

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止(不推荐,方法已经作废)
  • 使用interrupt方法中断线程
    • 打断阻塞的线程(sleep,wait,join)的线程会抛出 InterruptException异常
    • 打断正常的线程,可以根据打断的状态来标记是否退出线程

笔记是对黑马课程中的知识进行的个人总结,图片借鉴了课程视频中的资料,感谢黑马程序员的开源精神,哈哈,如有问题联系我删除!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值