java 多线程
2022/2/16 1:11:52
本文主要是介绍java 多线程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
多线程创建方式一
方式一、继承Thread类
1、创建一个继承于Thread的子类
2、重写run()方法
3、创建Thread类的子类对象
4、通过对象调用start()方法
start方法的作用:启动当前线程 调用当前线程的run方法(启动一个新线程)
-
如果直接调用run()方法不会启动新线程,还是在主进程里执行。
-
不能让已经start()的线程再次执行第二次,需要再次新建一个
-
最终结果是交替执行,执行结果和顺序不确定
方式二、匿名子类
public class ThreadDemo { public static void main(String[] args) { // 创建线程的匿名子类方法 new Thread() { @Override public void run() { for(int i = 0;i < 100;i++){ if(i%2 == 0){ System.out.println(Thread.currentThread().getName()+":" + i); } } } }.start(); new Thread() { @Override public void run() { for(int i = 1;i < 100;i++){ if(i % 2 != 0){ System.out.println(Thread.currentThread().getName()+":" + i); } } } }.start(); } }
线程常用方法
- start():启动当前线程,调用当前线程的run()
- run():通常需要重写Thread()方法
- String getName():返回线程名称
- void setName(String name):设置线程名称
- currentThread():静态方法,返回执行当前代码的线程
Thread.currentThread().setName("")....
-
yield():释放cpu的执行权,有可能cpu还会执行此线程
-
join():在线程a中调用线程b的join方法,此时线程a进入阻塞状态,b进入运行态直到线程b完全执行完毕,线程a结束阻塞状态
-
sleep(long milltime):休眠阻塞一段时间
线程优先级
-
等级
- MAX_PRIORITY: 10
- MIN_PRIORITY: 1
- NORM_PRIORITY: 5 默认的优先级
-
方法
- getPriority()
- setPriority()
优先级高的表示被cpu执行的概率高
线程创建二——实现Runnable接口
- 1、创建一个实现了Runnable接口的类
- 2、实现类去实现Runnable中的抽象方法run()
- 3、创建实现类的对象
- 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
- 5、通过Thread类的对象调用start()方法
/** 创建多线程Runnable接口 * 1、创建一个实现了Runnable接口的类 * 2、实现类去实现Runnable中的抽象方法run() * 3、创建实现类的对象 * 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 * 5、通过Thread类的对象调用start()方法 * @author DingJun * @date 2021/7/12 19:29 */ // 1、创建一个实现了Runnable接口的类 class MThread implements Runnable { // 2、实现类去实现Runnable中的抽象方法run() @Override public void run() { for(int i = 0;i < 100; i++){ if(i % 2 == 0){ System.out.println(i); } } } } public class ThreadTest1 { public static void main(String[] args) { // 3、创建实现类的对象 MThread mThread = new MThread(); // 4、将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread t1 = new Thread(mThread); // 通过Thread类的对象调用start() // 1、启动进程 // 2、调用当前线程的run()--->调用了Runnable类型target中的run t1.start(); } }
源码Thread的构造函数中有public Thread(Runnable target)
的构造方法,源码中Thread.run()方法如下:
public void run() { if(target != null) { target.run(); } }
比较创建线程的方式
优先选择实现Runnable接口的方式
原因: 1、实现方式没有类的单继承的局限性
2、实现的方式更适合处理多个线程有共享数据的情况
联系: public class Thread implements Runnable
都要重写run方法
线程安全问题
因为线程同步问题,cpu抢占时机不对而造成对共享资源的不正当访问而发生错误的现象。
线程同步机制
方法一:同步代码块 synchronized
synchronized(同步监视器) {
// 需要被同步的代码(操作共享数据的代码)
// 这里面的代码当有线程进入时,则其他线程不能进入
}
同步监视器 锁:任何一个类的对象都可以充当锁
要求:多个线程必须共用同一把锁
补充:在实现Runnable接口创建多线程的方式中,我们可以使用this充当同步监视器
在继承Thread 类创建多线程的方式中,慎用this充当同步监视器,考虑使用当前类(类.class)充当同步监视器。
方法二:同步方法
public synchronized void run()
,在函数返回类型前面添加关键字
在同步代码内部是操作共享的代码,注意进行分割和组合
同步监视器:this
当使用同步方法对继承Thread类的多线程时,使用static synchronized void show()
,同步监视器是当前的类(唯一的,Window.class)。
- 同步方法仍然涉及到同步监视器,只是不需要我们声明
- 非静态的同步方法,同步监视器是:this
- 静态的同步方法,同步监视器是:当前类本身
方式三:LOCK(显式锁)
- 手动地启动同步(lock()),手动地关闭锁(unlock()).
private ReentrantLock lock = new ReentrantLock(); lock.lock() ------ lock.unlock()
线程的通信
wait、notify、notifyAll
三个方法必须使用在同步代码块或同步方法中
这三个方法调用都省略了this(也就是当前对象),注意要与同步代码块或同步方法中的同步监视器一致...
这三个方法都是定义在java.lang.Object类中。
/** 线程通信,两个线程交替打印1到100 * @author DingJun * @date 2021/7/13 21:04 * wait(): 一旦执行此方法:当前线程进入阻塞状态,并释放同步监视器(锁) * notify():唤醒wait的一个线程,如果有多个线程被wait,优先唤醒优先级高的线程 * notifyAll():唤醒所有被wait的线程 */ class Number implements Runnable { private int num = 1; @Override public void run() { while (true) { synchronized (this) { // 唤醒线程 notify(); if(num <= 100) { // try { // Thread.sleep(1000); // } catch (InterruptedException e) { // e.printStackTrace(); // } System.out.println(Thread.currentThread().getName() + ":" +num); num++; try { // 使得当前进程进入阻塞状态 wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public class CommuncitionTest { public static void main(String[] args) { Number number = new Number(); Thread t1 = new Thread(number); Thread t2 = new Thread(number); t1.start(); t2.start(); } }
sleep()和wait()的异同
1、同:都可以使得当前的线程进入阻塞状态
2、不同: 1) 两个方法声明位置不同:Thread类中声明sleep(),Object类中声明 wait().
2)调用的范围要求不同:sleep可以在任何需要的场景调用。wait()必须使用在同步代码块或同步方法中.
3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁.
生产者和消费者例题
package com.ding; /** 消费者和生产者问题 * 生产者将产品交给店员,消费者取走产品,店员一次只能持有固定数量的产品,如果生产者试图生产更多的产品,如果有空位放产品了再通知生产者 * 继续生产;如果没有产品了,店员会告诉消费者等一下,如果店中有产品了再通知消费者来取走产品 * 共享数据:店员、产品数量 * @author DingJun * @date 2021/7/14 10:59 */ class Clerk { private int productCount = 0; // 生产产品 public synchronized void produceProduct() { if(productCount < 20) { productCount++; System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "个产品"); // 唤醒消费者线程,此时店员处有产品 notify(); }else { // 等待 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } // 消费产品 public synchronized void consumerProduct() { if(productCount > 0) { System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "个产品"); productCount--; // 唤醒生产者线程,此时产品被消费了一个肯定小于20 notify(); }else { // 等待 try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } // 生产者线程 class Producer extends Thread { private Clerk clerk = new Clerk(); public Producer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(getName() + ":生产者开始生产..."); while (true) { try { Thread.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } clerk.produceProduct(); } } } // 消费者线程 class Consumer extends Thread { private Clerk clerk = new Clerk(); public Consumer(Clerk clerk) { this.clerk = clerk; } @Override public void run() { System.out.println(getName() +":消费者正在消费"); while (true) { try { Thread.sleep(600); } catch (InterruptedException e) { e.printStackTrace(); } clerk.consumerProduct(); } } } public class TransportTest { public static void main(String[] args) { Clerk clerk = new Clerk(); // 声明店员对象 // 共用同一个店员 Producer p1 = new Producer(clerk); Consumer c1 = new Consumer(clerk); p1.setName("生产者"); c1.setName("消费者"); p1.start(); c1.start(); } }
线程创建三
实现Callable接口
get返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值
- 创建一个实现Callable接口的实现类
- 实现call方法,将此线程需要执行的操作声明在call()中
- 创建Callable接口实现类的对象
- 将此Callable接口实现类对象传递到FutureTask构造器中,创建FutureTask对象
- 将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start()..
实现Callable接口的方式创建多线程比实现Runnable接口更强大?
- call()可以有返回值
- call()可以抛出异常,被外面的操作捕获,获取异常的信息
- Callable是支持泛型的
线程创建四——线程池
经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响比较大
-
提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用
-
提高响应速度,降低资源消耗,便于线程管理
// 1、指定线程数量的线程池,ExecutorService是一个接口 ExecutorService service = Executors.newFixedThreadPool(10); // 设置线程池的属性 ThreadPoolExecutor service1 = (ThreadPoolExecutor) service; service1.setCorePoolSize(15); service1.setKeepAliveTime(); maximumPoolSize(); // 2、执行指定的线程操作,需要提供实现Runnable 接口或Callable接口实现类的对象 service.execute(new NumThread()); service.execute(new NumThread1()); // 适用于Runnable接口 // service.submit(Callable callable); // 适用于Callable接口实现类 // 3、关闭连接池 service.shutdown();
上面的ExecutorService
只是一个接口,ThreadPoolExeutor类实现了此接口,该类有设置线程池属性的方法,进行强转。
线程生命周期
- 暂停/挂起线程(suspend)和恢复线程(resume())---暂停线程意味着线程还可以恢复运行
这篇关于java 多线程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-07-04TiDB 资源管控的对撞测试以及最佳实践架构
- 2024-07-03万字长文聊聊Web3的组成架构
- 2024-07-02springboot项目无法注册到nacos-icode9专业技术文章分享
- 2024-06-26结对编程到底难不难?答案在这里
- 2024-06-19《2023版Java工程师》课程升级公告
- 2024-06-15matplotlib作图不显示3D图,怎么办?
- 2024-06-1503-Loki 日志监控
- 2024-06-1504-让LLM理解知识 -Prompt
- 2024-06-05做软件测试需要懂代码吗?
- 2024-06-0514-ShardingSphere的分布式主键实现