java 镶嵌创建线程_Java多线程 6种实现方式、线程同步的详细Demo、(等待唤醒机制 & 生命周期)【图解】【敲敲极详细!!】...

多线程

作用:

防止线程阻塞,执行某个耗时任务

让多个程序能够看起来像是**“同时执行”**

单独执行某个任务

文章目录

1.进程和线程

进程: 正在执行的程序 【前台进程和后台进程或者一些服务进程】

线程: 一条执行路径

一个进程可以包含一个线程,也可以包含多个线程

一条线程包含了一条原子性语句,也可以包含n条原子性语句

原子性语句: 不可再分割的语句 i ++ ; i = 10 ;

CPU在某一个时间刻度上只能够执行一条线程的一条原子性语句

2.多线程的特点

开启多线程可以提高CPU的使用率,从而间接提高进程的使用率

CPU在分配给线程资源的时候是随机的

Java 虚拟机允许应用程序并发地运行多个执行线程。

并发: 同一个时间段同时执行多个线程

并行: 同一个时间刻度【不能分割的时间单位】下同时执行多个线程 多核处理器

高并发: 线程数太大

3.多线程的实现方法(6种)

线程的封装:线程中需要用到的任务参数通过外界使用构造方法传入

3.1方式一:继承Thread类

自定义类MyThread继承Thread类

MyThread类里面重写run()方法

创建线程对象

启动线程

public class ThreadDemo {

public static void main(String[] args) {

CalculateThread t2 = new CalculateThread(1, 1000);//创建线程对象

t2.start();//启动线程

}

}

class CalculateThread extends Thread {//继承Thread类

private int m;

private int n;

public CalculateThread() {

super();

}

public CalculateThread(int m, int n) {//任务参数通过外界使用构造方法传入

super();

this.m = m;

this.n = n;

}

@Override

public void run() {//重写run()方法

int sum = 0;

for (int i = m; i <= n; i++) {

System.out.println("subThread: " + i);

sum += i;

}

System.out.println(m + "~" + n + "的和为: " + sum);

}

public int getM() {

return m;

}

public void setM(int m) {

this.m = m;

}

public int getN() {

return n;

}

public void setN(int n) {

this.n = n;

}

}

3.2方式二:实现Runnable接口

​1.自定义类MyRunnable实现Runnable接口

​2.重写run()方法

​3.创建MyRunnable类的对象

​4.创建Thread类的对象,并把步骤3创建的对象作为构造参数传递

​5.启动线程

public class ThreadDemo {

public static void main(String[] args) {

// 3.创建IteratorFileThread类的对象

IteratorFileThread ift = new IteratorFileThread(new File("D:\\JavaSE"));

// 4.创建Thread类的对象,并把步骤3创建的对象作为构造参数传递

Thread t = new Thread(ift);

CalculateThread t2 = new CalculateThread(1, 1000);

// 5.启动线程

t.start();

t2.start();

for (int i = 0; i <= 1000; i++) {

System.out.println("mainThread: " + i);

}

}

}

// 开启线程一 遍历文件夹中所有的文件

class IteratorFileThread implements Runnable {

private File srcFile;

public IteratorFileThread() {}

public IteratorFileThread(File srcFile) {

this.srcFile = srcFile;

}

@Override

public void run() {

getAllFile(srcFile);

}

public static void getAllFile(File srcFile) {

if (srcFile == null) {

throw new RuntimeException("文件对象不能够为null!");

}

// 2.遍历文件对象下面所有的文件或者文件夹

File[] files = srcFile.listFiles();

if (files == null) {

throw new RuntimeException("文件遍历失败!");

}

// 说明文件夹中没有文件

if (files.length == 0) {

return;

}

// 3.获取到每一个File对象

for (File f : files) {

// 4.判断该对象是文件还是文件夹

if (f.isDirectory()) {

// a.是文件夹 递归,回到第二步

getAllFile(f);

} else {

String fileName = f.getName();

// b.是文件

System.out.println(fileName);

}

}

}

public File getSrcFile() {

return srcFile;

}

public void setSrcFile(File srcFile) {

this.srcFile = srcFile;

}

}

3.3方式三:匿名内部类开启线程

3.3.1继承Thread方式

new Thread() {

@Override

public void run() {

for (int i = 1; i <= 1000; i++) {

System.out.println("匿名内部类 继承Thread方式开启线程: " + i);

}

}

}.start();

3.3.2实现Runnable方式

new Thread(new Runnable() {

@Override

public void run() {

for (int i = 1; i <= 1000; i++) {

System.out.println("匿名内部类 实现Runnable方式方式开启线程: " + i);

}

}

}).start();

3.3.3Thread和Runnable镶嵌

如果继承Thread和实现Runnble同时实现,继承Thread优先

new Thread(new Runnable() {

@Override

public void run() {

for (int i = 1; i <= 1000; i++) {

System.out.println("==>匿名内部类 实现Runnable方式方式开启线程: " + i);

}

}

}) {

public void run() {

for (int i = 1; i <= 1000; i++) {

System.out.println("==>匿名内部类 继承Thread方式开启线程: " + i);

}

};

}.start();

3.4方式四:Lambda表达式方式

new Thread(()-> {

for (int i = 1; i <= 1000; i++) {

System.out.println("==>Lambda表达式方式: " + i);

}

}) .start();

3.5方式五:实现Callable方式开启线程

重写run方法可以书写线程的任务,但是run方法是无参数无返回值的,同时没有异常

所以开启线程的线程无法知道被开启线程的异常和返回值

Java提供了Callable接口开启线程,借助一个中间类 FutrueTask

public class CallableTest {

public static void main(String[] args) {

//public interface RunnableFuture extends Runnable, Future

//RunnableFuture继承了 Runnable, Future,所以可以Thread多态调用Runnable的调用

//public class FutureTask implements RunnableFuture

//且public FutureTask(Callable callable)

//FutureTask是RunnableFuture子类,FutureTask包含Callable

//Thread -> Runnable -> RunnableFuture -> FutureTask -> Callable

FutureTask task = new FutureTask<>(new MyCallable(1, 100));

Thread t = new Thread(task);

t.start();

try {

Integer result = task.get();

System.out.println(result);

} catch (InterruptedException | ExecutionException e) {

e.printStackTrace();

}

System.out.println("over");

}

}

class MyCallable implements Callable {

private int m;

private int n;

public MyCallable() {

super();

}

public MyCallable(int m, int n) {

super();

this.m = m;

this.n = n;

}

@Override

public Integer call() throws Exception {

int sum = 0;

for (int i = m; i <= n; i++) {

System.out.println(Thread.currentThread().getName() + "|" + i);

sum += i;

}

return sum;

}

}

3.6方式六:线程池开启线程

Executors工厂类来产生线程池

3.6.1构造方法

说明

public static ExecutorService newCachedThreadPool()

public static ExecutorService newFixedThreadPool(int nThreads)

public static ExecutorService newSingleThreadExecutor()public class ThreadPoolDemo {

public static void main(String[] args) throws InterruptedException, ExecutionException {

// 创建线程池对象

ExecutorService pool = Executors.newFixedThreadPool(3);

// 将线程提交到线程中

pool.submit(new MyRunnable());

Future future = pool.submit(new MyCallable(1, 50));

pool.submit(new Runnable() {

@Override

public void run() {

for (int i = 1; i <= 100; i++) {

System.out.println(Thread.currentThread().getName() + "|" + i);

}

}

});

Integer result = future.get();

System.out.println(result);

pool.shutdown();

}

}

class MyRunnable implements Runnable {

@Override

public void run() {

for (int i = 1; i <= 100; i++) {

System.out.println(Thread.currentThread().getName() + "|" + i);

}

}

}

4.设置和获取线程名称

通过构造方法

说明

Thread(String name)

分配新的 Thread 对象。

Thread(Runnable target, String name)

分配新的 Thread 对象。通过线程的成员方法

说明

public final String getName()

public final void setName(String name)通过静态方法

public static Thread currentThread()

可以获取任意方法所在的线程名称

Thread.currentThread().getName();

可以获取任意线程的线程名称

Thread.currentThread().setName();

可以设置任意线程的线程名称class NameRunnable implements Runnable {

@Override

public void run() {

for (int i = 1; i <= 1000; i++) {

System.out.println(Thread.currentThread().getName() + ":" + i);

}

}

}

4.1继承Thread开启线程和实现Runnable开启线程的区别

继承Thread的方式可以更为方便地访问线程的API

继承Thread方式如果一旦某个类已经继承了一个类,那么就不能够再继承Thread类 【单一继承性】

5.调度模型

Java使用的是抢占式调度模型

优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个,优先级高的线程获取的 CPU 时间片相对多一些。 (并不是优先级高的先执行,只是过去执行权的可能性大)

设置和获取线程的优先级

说明

public final int getPriority()

public final void setPriority(int newPriority)t1.setPriority(Thread.MAX_PRIORITY);//10

t2.setPriority(Thread.NORM_PRIORITY);//5

t3.setPriority(Thread.MIN_PRIORITY);//1

System.out.println(t1.getPriority());

System.out.println(t2.getPriority());

System.out.println(t3.getPriority());

6.线程休眠

该方法是一个阻塞方法,会阻塞正在执行的线程

sleep方法是一个静态方法,可以让任意线程睡眠

public static void sleep(long millis)

// 使用线程休眠模拟时钟

class SleepRunnable implements Runnable {

@Override

public void run() {

while (true) {

String dateStr = new SimpleDateFormat("yyyy年MM月dd号 HH点mm分ss秒").format(new Date());

System.out.println("现在是北京时间: " + dateStr);

// 线程休眠

try {

Thread.sleep(100000L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

6.1实现Runnable的类不能用thorws抛异常

因为Runnable没有抛出任何异常,子类抛出的异常不能大于父类,父类没有抛异常,子类也不能抛异常

6.2Thread & Runnable 不足

Thread和Runnable没有异常,子类也不能有异常,所以run只能使用try…catch处理异常,不用抛异常

run 方法的返回值类型是void,所以没有返回值

7.中断线程

说明

public final void stop()

public void interrupt()

7.1面试题: stop和interrupt的区别

stop:会中断子线程

interrupt:主线程向子线程抛出一个异常 【InterruptedException】,但线程能继续执行下去

可以通过System.exit(0)来中断线程

8.后台线程

后台线程 / 守护线程 / 服务线程 / 非用户线程

一般来说,JVM(JAVA虚拟机)中一般会包括俩种线程,分别是用户线程和后台线程。

说明

public final void setDaemon(boolean on)

用户线程的设置后台线程守护的是用户线程(后台线程不是不可或缺的)

用户线程存在则守护线程存在,用户线程死亡了,那么所有的守护该用户线程的线程都将死亡

9.线程加入

说明

public final void join()

非静态方法,需要执行某个线程对象去执行,是让join之外的线程处于阻塞状态join需要加在某线程之后,而不是所有线程启动之后

10.线程礼让

唯一一个方法能够让线程从运行态到就绪态的方法

public static void yield()

主动让出CPU的执行权,让出之后自己重新参与抢夺CPU资源的队列中重新抢

11.线程同步

线程同步的问题,简单来说根本原因就是 CPU在某一个时间刻度上只能够执行一条原子性语句

11.1程序模拟该火车站售票

11.1.1线程同步实现的三个问题

问题一: 继承Thread方式不能够共享同一份数据

需要对数据进行static修饰

问题二: 卖出了负票

窗口2正在出售第1张票!!!

窗口1正在出售第0张票!!!

窗口3正在出售第-1张票!!!

原因: 原子性语句导致的同时还对共享数据做了写操作

问题三: 卖出了同票

窗口1正在出售第53张票!!!

窗口3正在出售第53张票!!!

原因: 当多个线程争夺资源的时候,原子性语句导致的

11.1.1.1线程同步问题的产生原因:

必须存在多线程环境

必须多线程环境下存在多条原子性语句操作共享数据

并且对共享数据做写的操作

11.1.2解决方案

通过加锁【互斥锁 同步锁】可以解决以上不同步问题

同步代码块

//格式:

synchronized(对象){需要同步的代码;}

特点:

需要显示创建对象

不知道在哪里加锁,在哪里释放锁(在出了同步代码块之后)

// synchronized(对象){需要同步的代码;}

while (true) {

synchronized (MyLock.LOCKA) {//通过枚举,创建同一个锁对象

if (tickets > 0) {

// 模拟网络延迟

try {

Thread.sleep(100L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票!!!");

}

}

}

//枚举

enum MyLock {

LOCKA,LOCKB,LOCKC

}

同步方法

如果锁对象是this,就可以考虑使用同步方法

//格式:

public synchronized 返回值 方法名(参数列表) {

//需要同步的代码块

}

特点

不知道锁对象是谁

能够不用单独编写锁对象

不知道在哪里加锁,在哪里释放锁 (出了同步方法就等于解锁)

//public synchronized 返回值 方法名(参数列表) {

//需要同步的代码块

//}

public synchronized void sellTickets() {

if (tickets > 0) {

// 模拟网络延迟

try {

Thread.sleep(100L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票!!!");

}

}

Lock锁 【JDK提供的一种锁对象】

特点:

明确锁对象

在何时加锁以及何时释放锁非常清晰,更加满足了面向对象思想

class SellTicketRunnable implements Runnable {

private int tickets = 100;

private Lock lock = new ReentrantLock();//

@Override

public void run() {

while (true) {

lock.lock();//

if (tickets > 0) {

// 模拟网络延迟

try {

Thread.sleep(100L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票!!!");

}

lock.unlock();//

}

}

}

11.2继承Thread和实现Runnable的区别?

好处

继承Thread的方式可以方便地访问线程的API

实现Runnable方式可以避免单继承带来的缺陷

实现Runnable方式可以在多个线程之间共享同一个Runnble接口,也就是说可以共享同一份数据【变量】和同一份代码【方法】

SellTicketRunnable sr = new SellTicketRunnable();

Thread t1 = new Thread(sr);

Thread t2 = new Thread(sr);

Thread t3 = new Thread(sr);

缺陷

继承Thread类每个线程都有自己的一份数据

继承Thread方式单一继承性

实现Runnable方式不能够直接访问线程的API,我们可以间接使用 Thread.currentThread() 这个方法来获取当前正在执行run方法的线程对象

然后通过这个对象来访问线程API。

11.2.1静态 / 非静态

静态方法: Class对象

当使用的是Thread继承,run()重写的方法需要加上static,使所有成员共享同一个数据

//因为Thread继承需要创建三个对象,所以需要static是方法静态,

//Class c1 = SellTicketThread.class(锁对象)

//同一个类下,不管创建多少个对象,都共用同一个class对象(.class字节码文件)

SellTicketThread t1 = new SellTicketThread();

SellTicketThread t2 = new SellTicketThread();

SellTicketThread t3 = new SellTicketThread();

class SellTicketThread extends Thread {

private static int tickets = 100;

@Override

public void run() {

sellTickets();

}

public static synchronized void sellTickets() {

if (tickets > 0) {

// 模拟网络延迟

try {

Thread.sleep(100L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + "正在出售第" + (tickets--) + "张票!!!");

}

}

}

非静态方法: this

指调用类的对象

12.死锁

死锁:指两个或者两个以上的线程在执行的过程中,因争夺资源产生的一种互相等待现象。

public class DeadLockThread {

public static void main(String[] args) {

DieLock t1 = new DieLock(true);

DieLock t2 = new DieLock(false);

try {

Thread.sleep(1000L);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

t1.start();

t2.start();

}

}

class DieLock extends Thread {

private boolean flag;

public DieLock() {

super();

}

public DieLock(boolean flag) {

super();

this.flag = flag;

}

@Override

public void run() {

if (flag) {//1. DieLock t1 = new DieLock(true);调用MyLock.LOCKA,进行上锁,

//当CPU执行权被抢

synchronized(MyLock.LOCKA) {

System.out.println("if语句中的LOCKA锁");

synchronized (MyLock.LOCKB) {

System.out.println("if语句中的LOCKB锁");

}

}

} else {//2. DieLock t2 = new DieLock(false);调用MyLock.LOCKB,进行上锁,

synchronized(MyLock.LOCKB) {

System.out.println("else语句中的LOCKB锁");

synchronized (MyLock.LOCKA) {

System.out.println("else语句中的LOCKA锁");

}

}

}

}

}

//到时AB同时上锁,都在等待,B因为A上锁等待解锁,A因为B上锁等待解锁

//枚举,创建锁对象

enum MyLock {

LOCKA, LOCKB

}

13.未捕获异常

替代JVM,自行捕获未捕获异常

说明

static interface Thread.UncaughtExceptionHandler

这个就是未捕获异常处理器public class ThreadDemo01 {

public static void main(String[] args) {

//1.捕获所有异常

Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

// 2.需求: 希望主线程未捕获异常自己处理 ,子线程还是虚拟机处理

Thread.currentThread().setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

@Override

public void uncaughtException(Thread t, Throwable e) {

System.out.println(t.getName() + "线程出现了问题!");

System.out.println(e);

}

});

ExceptionThread t = new ExceptionThread();

t.setName("子线程");

// 3.需求: 希望子线程未捕获异常自己处理 ,主线程还是虚拟机处理

t.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {

@Override

public void uncaughtException(Thread t, Throwable e) {

System.out.println(t.getName() + "线程出现了问题!");

System.out.println(e);

}

});

t.start();

System.out.println("start");

int a = 10;

int b = 0;

System.out.println(a/b);

System.out.println("end");

}

}

class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {

@Override

public void uncaughtException(Thread t, Throwable e) {

/*

* t: 表示出现异常的线程对象

* e: 表示出现的异常信息

*/

System.out.println(t.getName() + "线程出现了问题!");

e.printStackTrace();

}

}

class ExceptionThread extends Thread {

@Override

public void run() {

for (int i = 1; i <= 100; i++) {

System.out.println(getName() + ":" + i);

if (i == 10) {

System.out.println(i / 0);

}

}

}

}

14.线程组

ThreadGroup :为了更加方便批量管理线程

14.1线程组和线程池有关系吗?

没有关系,

单个线程也可以使用线程组

线程池也可以使用线程组进行管理

public class ThreadDemo {

public static void main(String[] args) {

ThreadGroup tg1 = new ThreadGroup("三国演义");

ThreadGroup tg2 = new ThreadGroup("西游记");

MyRunnable mr = new MyRunnable();

Thread t1 = new Thread(tg1, mr, "诸葛亮");

Thread t2 = new Thread(tg1, mr, "赵云");

Thread t3 = new Thread(tg1, mr, "吕布");

Thread t4 = new Thread(tg2, mr, "二郎神");

Thread t5 = new Thread(tg2, mr, "嫦娥");

Thread t6 = new Thread(tg2, mr, "铁扇公主");

System.out.println(t6.toString());

tg1.setDaemon(true);

System.out.println(tg1.getName());

//t1.start();

//t2.start();

//t3.start();

//t4.start();

//t5.start();

//t6.start();

//集合也可以对线程进行管理

List threads = new ArrayList();

threads.add(t1);

threads.add(t2);

threads.add(t3);

threads.add(t4);

threads.add(t5);

threads.add(t6);

for (Thread thread : threads) {

thread.start();

}

}

}

class MyRunnable implements Runnable {

@Override

public void run() {

for (int i = 1; i <= 100; i++) {

System.out.println(Thread.currentThread().getName() + "|" + i);

}

}

}

15.等待唤醒机制

线程通信的一种方式 wait() notify()

47b97806c4ea5754388444ea5c7fff2e.png

15.1为什么 wait和nofity这些方法不设计在线程Thread类里面,而设计在Object类中?

因为锁对象是任意对象,所以锁的类型是Object类型

16.生命周期

3ab750a38923fca4f0badded816c7852.png

点赞

收藏

分享

文章举报

2a87567fba7b810bf52de4d164b50047.png

d1358d4695d8660de2972cc1f6e682b2.png

大鱼吃狗

发布了51 篇原创文章 · 获赞 14 · 访问量 1390

私信

关注

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值