JAVA

您所在的位置:网站首页 java多线程入门书籍 JAVA

JAVA

2023-03-09 13:52| 来源: 网络整理| 查看: 265

目录 一、 多线程基础1.1 进程和线程的区别1.2 并发与并行的区别 二、多线程的创建和使用2.1 线程的创建方式2.1.1 继承Thread类创建线程2.1.2 实现Runnable接口创建线程2.1.3 使用Calable和Future创建线程2.1.4 使用线程池创建线程线程池种类: 三、线程的生命周期1. 生命周期状态定义2. 线程状态详解和转换 四、线程的同步1. 背景2. 线程同步的方法2.1 Synchronized同步代码块2.2 Synchronized同步方法2.3 Lock锁 3.synchronized 与Lock 的对比 五、线程的通信1. 线程通信方法2. 案例 六、线程池线程池种类: 七、面试题1.java中有几种方法可以实现一个线程(jdk5.0之前)?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?2. suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,3. sleep() 和 wait() 有什么区别?4. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。5. 启动一个线程是用run()还是start()?6. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?7. 请说出你所知道的线程同步的方法。8. 多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?9. 线程的基本概念、线程的基本状态以及状态之间的关系10. 简述synchronized和java.util.concurrent.locks.Lock的异同 ? 八、应用案例

一、 多线程基础 1.1 进程和线程的区别

进程:是指一个程序的执行过程,简而言之,每个应用就是一个进程 线程:进程的细化,进程内可以开启多个线程,一个进程统一时间开启多个线程并行执行,则是多线程执行

1.2 并发与并行的区别

并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。 并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。

二、多线程的创建和使用 2.1 线程的创建方式 2.1.1 继承Thread类创建线程

创建步骤:

定义类并继承Thread,重写该类的run()方法,该方法的方法体就是线程需要完成的任务,run()方法也称为线程执行体。创建Thread子类的实例,也就是创建了线程对象启动线程,即调用线程的start()方法

在这里插入图片描述

代码:

/** * @title: ExtendThread * @Author Liuyang Tian * @Date: 2023/3/2 15:10 * @Version 1.0 */ public class ExtendThread { public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); System.out.println("主线程:" + Thread.currentThread().getName()); } } class MyThread extends Thread{ @Override public void run() { System.out.println("创建子线程:" + currentThread().getName()); } } 2.1.2 实现Runnable接口创建线程

创建步骤:

定义子类,实现Runnable接口子类中重写Runnable接口中的run方法通过Thread类含参构造器创建线程对象将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法

在这里插入图片描述

代码:

/** * @title: ImpRunnable * @Author Liuyang Tian * @Date: 2023/3/2 15:20 * @Version 1.0 */ public class ImpRunnable { public static void main(String[] args) { MyThread1 myThread1 = new MyThread1(); Thread thread = new Thread(myThread1); thread.start(); System.out.println("主线程:" + Thread.currentThread().getName()); } } class MyThread1 implements Runnable{ @Override public void run() { System.out.println("创建子线程:" + Thread.currentThread().getName()); } } 2.1.3 使用Calable和Future创建线程

创建步骤:

创建Callable接口的实现类,并实现call()方法,然后创建该类的实例使用Future Task类来包装Callable对象。该FutureTask对象封装了Callable对象的call()方法的返回值使用FutureTask对象作为Thread对象的target创建并启动线程(因为FutureTask实现了Runnable接口并在重写的run方法中执行call方法)调用FutureTask对象的get方法来获取线程执行结束后的返回值

在这里插入图片描述

代码:

/** * @title: ImpCallable * @Author Liuyang Tian * @Date: 2023/3/2 15:34 * @Version 1.0 */ public class ImpCallable { public static void main(String[] args) throws ExecutionException, InterruptedException { Mythread2 mythread2 = new Mythread2(); FutureTask task = new FutureTask(mythread2); new Thread(task).start(); System.out.println(task.get()); System.out.println("主线程:" + Thread.currentThread().getName()); } } class Mythread2 implements Callable{ @Override public String call() throws Exception { System.out.println("创建子线程:" + Thread.currentThread().getName()); return "我是子线程返回值"; } } 2.1.4 使用线程池创建线程 线程池种类:

线程池这里只简单列举下,详细知识点请往下看第六部分线程池部分;

FixedThreadPool:定长线程池CachedThreadPool:可缓冲线程池ScheduledThreadPool:调度线程池SingleThreadExecutor:单线程化线程池

创建步骤:

创建定长线程池创建好Runnable类线程对象并重新run方法向线程池提交任务

在这里插入图片描述

代码

/** * @title: UseThreadPool * @Author Liuyang Tian * @Date: 2023/3/2 15:49 * @Version 1.0 */ public class UseThreadPool { public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); Runnable task =new Runnable(){ public void run() { System.out.println("子线程:"+ Thread.currentThread().getName()); } }; // 使用线程池创建5个子线程 for (int i = 0; i public static void main(String[] args) { Ticket t = new Ticket(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); t1.start(); t2.start(); t3.start(); } } class Ticket implements Runnable{ private int ticketNum = 10; @SneakyThrows @Override public void run() { while (true){ if (ticketNum > 0){ //模拟出票 Thread.sleep(10); System.out.println(Thread.currentThread().getName() +" 售出车票,ticket号为:"+ticketNum--); }else { break; } } } } 2. 线程同步的方法 2.1 Synchronized同步代码块

在这里插入图片描述

2.2 Synchronized同步方法

在这里插入图片描述

2.3 Lock锁

在这里插入图片描述

3.synchronized 与Lock 的对比 Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放Lock只有代码块锁,synchronized有代码块锁和方法锁使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) 五、线程的通信 1. 线程通信方法 wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待notifyAll():唤醒正在排队等待资源的所有线程结束等待. 2. 案例

使用两个线程打印1-100。线程1, 线程2 交替打印

/** * @title: MyThread3 * @Author Liuyang Tian * @Date: 2023/3/2 16:21 * @Version 1.0 */ public class MyThread3 { public static void main(String[] args) { Ticket t = new Ticket(); for (int i = 0; i private final ReentrantLock lock = new ReentrantLock(); private int num = 1; @SneakyThrows @Override public void run() { while (true){ synchronized (this){ notify(); if (num break; } try { wait(); }catch (Exception e){ e.printStackTrace(); } } } } } 六、线程池 线程池种类: FixedThreadPool:定长线程池CachedThreadPool:可缓冲线程池ScheduledThreadPool:调度线程池SingleThreadExecutor:单线程化线程池

下次补充

七、面试题 1.java中有几种方法可以实现一个线程(jdk5.0之前)?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

答:有两种实现方法,分别是继承Thread类与实现Runnable接口。 用synchronized关键字修饰同步方法,反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。

2. suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,

指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

3. sleep() 和 wait() 有什么区别?

答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

4. 同步和异步有何异同,在什么情况下分别使用他们?举例说明。

答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

5. 启动一个线程是用run()还是start()?

答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法就是正常的对象调用方法的执行,并不是使用分线程来执行的。

6. 当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

答:不能,一个对象的一个synchronized方法只能由一个线程访问。

7. 请说出你所知道的线程同步的方法。

答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。 sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。 notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。 notityAll():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

8. 多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么?

答:多线程有两种(具体可以说4种)实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify

9. 线程的基本概念、线程的基本状态以及状态之间的关系

答:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。 Java中的线程有四种状态分别是:创建、就绪、运行、阻塞、结束

10. 简述synchronized和java.util.concurrent.locks.Lock的异同 ?

答:主要相同点:Lock能完成synchronized所实现的所有功能 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

八、应用案例

下次补充



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3