Java学习篇 -- 多线程笔记(带你看不一样的多线程~)

/**

  • 打印1-100之间的偶数

*/

class EvenThread extends Thread {

@Override

public void run() {

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

if (i % 2 == 0) {

System.out.println(“偶数” + i);

}

}

}

}

/**

  • 打印1-100之间的奇数

*/

class OddThread extends Thread {

@Override

public void run() {

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

if (i % 2 != 0) {

System.out.println(“奇数” + i);

}

}

}

}

多线程执行流程

  1. 执行main方法的线程,称为主线程,执行run方法的线程,称为子线程

  2. 执行start方法之前,只执行一次主线程,当调用start方法之后,线程数瞬间由1个变成2个,其中主线程继续执行main方法,然后新创建的子线程执行run方法

  3. main方法执行完毕,主线程结束,run方法执行完毕,子线程结束

Thread类常用方法:
  1. Thread() 创建新对象

  2. Thread(String threadname) 指定线程名称创建线程

  3. void start() 启动线程

  4. run() 线程的主要实现业务在该方法内实现

  5. getName() 返回线程的名称

  6. setName() 设置线程名称

  7. static void sleep(long milis) 让当前线程指定毫秒数休眠

  8. static Thread currentThread() 获得当前线程

  9. setPriority(int newPriority) 更改线程的优先级

  10. void join() 等待该线程终止

  11. void interrupt() 中断线程

  12. booleans isAlive() 测试线程是否出于活动状态

Thread类方法练习

public class ThreadTest {

public static void main(String[] args) {

//创建线程,并调用start方法启动多线程

new Thread(new Thread1()).start();

//将主线程优先级调为最大,但不会保证一定会是主线程执行

Thread.currentThread().setPriority(Thread.MAX_PRIORITY);

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

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

if (i == 5) {

//当i==5时等待主线程终止

try {

Thread.currentThread().join();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

}

class Thread1 extends Thread {

@Override

public void run() {

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

System.out.println(i);

//当i==8时终止线程,判断线程是否存活

// if (i == 8) {

// Thread.currentThread().interrupt();

// System.out.println(Thread.currentThread().getName() + " - :线程终止");

// boolean flag = Thread.currentThread().isAlive();

// System.out.println(Thread.currentThread().getName() + “是否存活:” + flag);

// }

//每循环一次休眠100毫秒

try {

Thread.sleep(100);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

}

}

龟兔赛跑小练习

需求:编写龟兔赛跑多线程程序,设赛跑长度为30米

兔子每跑完10米休眠10秒

乌龟跑完20米休眠1秒

乌龟和兔子每跑完1米输出一次结果,看看最后谁先跑完

public class Test {

public static void main(String[] args) {

Rabbit rabbit = new Rabbit();

Tortoise tortoise = new Tortoise();

rabbit.setName(“兔子”);

tortoise.setName(“乌龟”);

rabbit.start();

tortoise.start();

}

}

class Rabbit extends Thread {

@Override

public void run() {

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

if (i == 30) {

System.out.println(Thread.currentThread().getName() + “跑完了”);

break;

}

System.out.println(Thread.currentThread().getName() + “跑了” + i + “米”);

if (i % 10 == 0) {

System.out.println(Thread.currentThread().getName() + “开始休眠”);

try {

Thread.sleep(10000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + “休眠完毕,开始跑了”);

}

}

}

}

class Tortoise extends Thread {

@Override

public void run() {

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

if (i == 30) {

System.out.println(Thread.currentThread().getName() + “跑完了”);

break;

}

System.out.println(Thread.currentThread().getName() + “跑了” + i + “米”);

if (i % 20 == 0) {

System.out.println(Thread.currentThread().getName() + “开始休眠”);

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName() + “休眠完毕,开始跑了”);

}

}

}

}

方式2:实现Runnable接口实现多线程

public class ThreadTest {

public static void main(String[] args) {

Thread1 t = new Thread1();

new Thread(t).start();

}

}

class Thread1 implements Runnable{

@Override

public void run() {

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

System.out.println(i);

}

}

}

方式3:实现Callable+FutureTask实现多线程

实现Callable接口的方式可以将子线程的结果返回到主线程,也可以处理异常

步骤:

  1. 创建一个类,实现Callable接口

  2. 重写call方法,编写多线程代码,并返回结果

  3. 创建FutrueTask对象,并将Callable接口的实现类对象传递到FutrueTask构造方法

  4. 创建Thread对象,并将FutrueTask对象传递到构造方法,并调用start方法

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

public class ThreadTest {

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

CallableTest call = new CallableTest();

FutureTask ftask = new FutureTask<>(call);

new Thread(ftask).start();

Integer i = ftask.get();

System.out.println(“返回的结果为:” + i);

}

}

class CallableTest implements Callable {

@Override

public Integer call() throws Exception {

int sum = 0;

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

System.out.println(i);

sum += i;

Thread.sleep(100);

}

return sum;

}

}

运行结果如下:

在这里插入图片描述

使用Callable实现多线程会返回一个指定类型的结果,get方法获得返回的具体数据。

方式4:线程池实现多线程

思路:

可以先初始化一个线程池,需要时从线程池中取出线程执行异步任务,使用完再归还到线程池,这样可以避免频繁的创建、销毁线程,从而提升系统的性能

步骤:

  1. 初始化一个线程池,并指定线程个数【通常是CPU内核数*2】

  2. 从线程池中取出线程执行异步任务

public class ThreadTest {

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

//初始化线程池,创建方式为指定线程池数量创建

ExecutorService pool = Executors.newFixedThreadPool(15);

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

CallableTest call = new CallableTest(“callable” + i);

Future ft = pool.submit(call);

System.out.println(call.getName() + “随机到的数字 :” + ft.get());

}

pool.shutdown();

}

}

class CallableTest implements Callable {

private String name;

public CallableTest(String name) {

this.name = name;

}

@Override

public Integer call() throws Exception {

//当前线程睡一会在执行

Thread.sleep(100);

//返回一个10-100的整数

return (int)(Math.random()*90)+10;

}

public String getName() {

return name;

}

}

原生方式创建线程池:

核心7个参数:

corePoolSize:核心线程池的数量,初始化线程池时创建的线程个数

maximumPoolSize,线程池中最多创建多少个线程

keepAliveTime,保持存活的时间,异步任务执行完毕后,等待多长时间销毁多余的线程

unit,存活时间单位,例如:时、分、秒

workQueue,工作队列/阻塞队列,如果任务有很多,就会将多余的的任务放到队列里面,只要有线程空闲,就会去队列里面取出新的任务执行,通常使用 LinkedBlockingQueue(无限队列)

threadFactory,创建线程的工厂,采用默认值{Executors.defaultThreadFactory}

handler,阻塞队列满了,按照我们指定的拒绝策略拒绝执行任务

//初始化线程池,创建方式为指定线程池数量创建

public static ThreadPoolExecutor pool =

new ThreadPoolExecutor(

//核心线程池的数量

5,

//最大线程能开到多少

100,

//线程空闲后,多开的线程多长时间休眠

10,

//线程休眠的时间单位

TimeUnit.SECONDS,

//工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列

new LinkedBlockingDeque<>(100),

//默认工厂模式

Executors.defaultThreadFactory(),

//阻塞队列满后,采用拒绝策略拒绝服务

new ThreadPoolExecutor.AbortPolicy()

);

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

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

CallableTest call = new CallableTest(“callable” + i);

Future ft = pool.submit(call);

System.out.println(call.getName() + “随机到的数字 :” + ft.get());

}

pool.shutdown();

}

}

class CallableTest implements Callable {

private String name;

public CallableTest(String name) {

this.name = name;

}

@Override

public Integer call() throws Exception {

//当前线程睡一会在执行

Thread.sleep(100);

//返回一个10-100的整数

return (int)(Math.random()*90)+10;

}

public String getName() {

return name;

}

}

线程安全

当多个线程操作同一个共享数据时,当一个线程还未结束时,其他的线程参与进去,此时就会导致共享数据的不一致。

例:

public class ThreadTest {

//初始化线程池,创建方式为指定线程池数量创建

public static ThreadPoolExecutor pool =

new ThreadPoolExecutor(

//核心线程池的数量

5,

//最大线程能开到多少

100,

//线程空闲后,多开的线程多长时间休眠

10,

//线程休眠的时间单位

TimeUnit.SECONDS,

//工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列

new LinkedBlockingDeque<>(100),

//默认工厂模式

Executors.defaultThreadFactory(),

//阻塞队列满后,采用拒绝策略拒绝服务

new ThreadPoolExecutor.AbortPolicy()

);

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

CallableTest call = new CallableTest();

Future ft = null;

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

ft = pool.submit(call);

}

System.out.println(ft.get());

pool.shutdown();

}

}

class CallableTest implements Callable {

private int num = 0;

@Override

public Integer call() throws Exception {

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

num ++;

}

return num;

}

}

结果应该是10000,因为多个线程同时去改变资源,导致数字不是10000,共有2个问题导致数字不对:

  1. 主线程提前运行,不管子线程是否执行完毕

  2. 子线程互相争夺资源,导致资源不一致,A线程取出数字,改变数字后想要放进去,但B线程已经拿走了A线程未改变之前的值,A线程改变完后放入,B线程取出改变后放入,这样就看起来是1一个线程改变了值,所以说原本是3,结果确是2,线程多了,资源也就差的越来越多。

改变:

利用synchronized同步代码块

利用synchronized同步方法

利用Lock显示锁

synchronized隐式锁实现线程安全

public class ThreadTest {

//初始化线程池,创建方式为指定线程池数量创建

public static ThreadPoolExecutor pool =

new ThreadPoolExecutor(

//核心线程池的数量

5,

//最大线程能开到多少

100,

//线程空闲后,多开的线程多长时间休眠

10,

//线程休眠的时间单位

TimeUnit.SECONDS,

//工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列

new LinkedBlockingDeque<>(100),

//默认工厂模式

Executors.defaultThreadFactory(),

//阻塞队列满后,采用拒绝策略拒绝服务

new ThreadPoolExecutor.AbortPolicy()

);

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

CallableTest call = new CallableTest();

List<Future> fts = new ArrayList<>();

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

Future ft = pool.submit(call);

fts.add(ft);

}

int num = -1;

for (Future future : fts) {

num = future.get() > num ? future.get() : num;

}

System.out.println(num);

pool.shutdown();

}

}

class CallableTest implements Callable {

private int num = 0;

@Override

public Integer call() throws Exception {

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

//同步代码块实现线程同步

// synchronized (this) {

// num ++;

// }

intPlus();

}

return num;

}

//同步方法实现线程同步

public synchronized void intPlus() {

num++;

}

}

Lock锁显示锁实现线程安全

public class ThreadTest {

//初始化线程池,创建方式为指定线程池数量创建

public static ThreadPoolExecutor pool =

new ThreadPoolExecutor(

//核心线程池的数量

5,

//最大线程能开到多少

100,

//线程空闲后,多开的线程多长时间休眠

10,

//线程休眠的时间单位

TimeUnit.SECONDS,

//工作/阻塞队列,一旦核心线程放满后,多余的任务会放进该队列,采用链表阻塞队列

new LinkedBlockingDeque<>(100),

//默认工厂模式

Executors.defaultThreadFactory(),

//阻塞队列满后,采用拒绝策略拒绝服务

new ThreadPoolExecutor.AbortPolicy()

);

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

CallableTest call = new CallableTest();

List<Future> fts = new ArrayList<>();

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

Future ft = pool.submit(call);

fts.add(ft);

}

int num = -1;

for (Future future : fts) {

num = future.get() > num ? future.get() : num;

}

System.out.println(num);

pool.shutdown();

}

}

class CallableTest implements Callable {

private int num = 0;

//该类实现于Lock接口,是Lock接口的实现类,一般使用该类进行开锁及关锁操作

ReentrantLock lock = new ReentrantLock();

@Override

public Integer call() throws Exception {

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

lock.lock();

//为什么要加上try/finally呢?

//因为在开锁之后有可能会发生异常导致当前线程一直占用资源不释放,不加的话后面的代码不会被执行

//加上try/finally后不管有没有异常都能正常执行关锁操作,不会导致一个线程一直占用资源发生死锁

try {

num++;

} finally {

lock.unlock();

}

//同步代码块实现线程同步

// synchronized (this) {

// num ++;

// }

// intPlus();

}

return num;

}

//同步方法实现线程同步

// public synchronized void intPlus() {

// num++;

// }

}

线程通信

线程通信:线程通信就是A线程满足一定条件后通知B线程执行操作。

例子:生产者与消费者模型

生产者生产好产品后通知消费这消费。

wait让当前线程进入阻塞状态,同时会释放锁

线程通信用到的3个方法

wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。

notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。

notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

说明:

1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。

2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。

否则,会出现IllegalMonitorStateException异常

3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

// }

}

线程通信

线程通信:线程通信就是A线程满足一定条件后通知B线程执行操作。

例子:生产者与消费者模型

生产者生产好产品后通知消费这消费。

wait让当前线程进入阻塞状态,同时会释放锁

线程通信用到的3个方法

wait() 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。

notify():一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。

notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。

说明:

1.wait(),notify(),notifyAll()三个方法必须使用在同步代码块或同步方法中。

2.wait(),notify(),notifyAll()三个方法的调用者必须是同步代码块或同步方法中的同步监视器。

否则,会出现IllegalMonitorStateException异常

3.wait(),notify(),notifyAll()三个方法是定义在java.lang.Object类中。

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-LhahSWoJ-1715731828237)]

[外链图片转存中…(img-JIH7yVlb-1715731828238)]

[外链图片转存中…(img-gzDLYgkd-1715731828238)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,不论你是刚入门Java开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门!

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值