java ee13_一口气了解多线程及其Java实现

进程:进程就是应用程序在内存中分配的空间,也就是正在运行的程序,各个进程之间不干扰。同时进程保存着程序每一个时刻运行的状态。

程序:用某种编程语言(java、python等)编写,能够完成一定任务或者功能的代码集合,是指令和数据的有序集合,是一段静态代码。

线程:通常一个进程中可以包含若干个线程。进程单独占有一定的内存地址空间,而线程共享所属进程占有的内存地址空间和资源。

在Java中,我们是如何使用多线程的呢?

1.使用Thread 类和 Runnalble 接⼝来实现自己的“线程”类。

// 继承Thread类

public class MyThread1 extends Thread {

@Override

public void run() {

System.out.println("MyThread1*********");

}

}

// 实现Runnable接口

public class MyThread2 implements Runnable {

@Override

public void run() {

System.out.println("MyThread2@@@@@@@@@");

}

}

public class ThreadTest {

public static void main(String[] args) {

Thread myThread1 = new MyThread1();

myThread1.start();

Thread myThread2 = new Thread(new MyThread2());

myThread2.start();

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("MyThread3########");

}

}).start();

new Thread(() -> {

System.out.println("MyThread4%%%%%%%%");

}).start();

}

}

2.我们使用Runnable和Thread来创建一个新的线程。但是他们有一个弊端,就是run方法是没有返回值的。而有时候我们希望开启一个线程去执行一个任务,并且这个任务执行完成之后有一个返回值。JDK提供了Callable接口和Future类为我们解决了这个问题。

public class CallableTask implements Callable {

@Override

public String call() throws Exception {

Thread.sleep(1000);

return "中奖啦~";

}

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

// 1.使用Future接口

// ExecutorService可以使用submit方法来让⼀个Callable接口执行。

ExecutorService executor1 = Executors.newCachedThreadPool();

CallableTask task = new CallableTask();

Future result = executor1.submit(task);

// 注意调用get方法会阻塞当前线程,直到得到结果。

// 所以实际编码中建议使用可以设置超时时间的重载get方法。

System.out.println(result.get() + "@@");

// 2.使用FutureTask类:FutureTask 是实现的 RunnableFuture 接口的,

// 而 RunnableFuture 接口同时继承了 Runnable 接口和 Future 接口。

ExecutorService executor2 = Executors.newCachedThreadPool();

FutureTask futureTask = new FutureTask<>(new CallableTask());

executor2.submit(futureTask);

System.out.println(futureTask.get() + "**");

}

}

什么是线程组和线程优先级?

1.线程组:每个Thread必然存在于⼀个ThreadGroup中,Thread不能独⽴于ThreadGroup存在。执⾏main()⽅法线程的名字是main,如果在new Thread时没有显式指定,那么默认将⽗线程(当前执⾏new Thread的线程)线程组设置为⾃⼰的线程组。

public class ThreadGroupTest {

public static void main(String[] args) {

Thread testThread1 = new Thread(() -> {

System.out.println("testThread1当前线程组名字:" +

Thread.currentThread().getThreadGroup().getName());

System.out.println("testThread1线程名字:" +

Thread.currentThread().getName());

});

Thread testThread2 = new Thread(() -> {

System.out.println("testThread2当前线程组名字:" +

Thread.currentThread().getThreadGroup().getName());

System.out.println("testThread2线程名字:" +

Thread.currentThread().getName());

});

testThread1.start();

testThread2.start();

System.out.println("执⾏main⽅法线程名字:" + Thread.currentThread().getName());

}

}

输出结果:

执⾏main⽅法线程名字:main

testThread2当前线程组名字:main

testThread2线程名字:Thread-1

testThread1当前线程组名字:main

testThread1线程名字:Thread-0

2.线程优先级:Java中线程优先级可以指定,范围是1~10 。但并不是所有的操作系统都支持10级优先级的划分,Java只是给操作系统一个优先级的参考值,线程最终在操作系统的优先级是多少还是由操作系统决定。通常情况下,⾼优先级的线程将会⽐低优先级的线程有更⾼的⼏率得到执⾏。

public static void main(String[] args) {

Thread a = new Thread();

System.out.println("我是默认线程优先级:"+a.getPriority());

Thread b = new Thread();

b.setPriority(10);

System.out.println("我是设置过的线程优先级:"+b.getPriority());

}

我是默认线程优先级:5

我是设置过的线程优先级:10

线程状态

926ee133e8ed

线程状态转换图

锁与同步

无锁的时候,如下代码中两个线程A、B各自执行:

public class ThreadNoneLock {

static class ThreadA implements Runnable {

@Override

public void run() {

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

System.out.println("Thread A " + i);

}

}

}

static class ThreadB implements Runnable {

@Override

public void run() {

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

System.out.println("Thread B " + i);

}

}

}

public static void main(String[] args) {

new Thread(new ThreadA()).start();

new Thread(new ThreadB()).start();

}

}

Thread A 0

Thread A 1

Thread A 2

Thread B 0

Thread B 1

Thread B 2

Thread B 3

...

Thread B 97

Thread B 98

Thread B 99

Thread A 3

Thread A 4

Thread A 5

加锁之后(用synchronized关键字加上了同一个对象锁lock),A线程先执行完,B随后执行:

public class ThreadWithLock {

private static Object lock = new Object();

static class ThreadA implements Runnable {

@Override

public void run() {

synchronized (lock) {

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

System.out.println("Thread A " + i);

}

}

}

}

static class ThreadB implements Runnable {

@Override

public void run() {

synchronized (lock) {

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

System.out.println("Thread B " + i);

}

}

}

}

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

new Thread(new ThreadA()).start();

Thread.sleep(10);

new Thread(new ThreadB()).start();

}

}

Thread A 97

Thread A 98

Thread A 99

Thread B 0

Thread B 1

Thread B 2

等待/通知机制

如下例子中,线程A和线程B首先打印出自己需要的东西,然后使用notify()方法叫醒另一个正在等待的线程,然后自己使用wait()方法陷入等待并释放lock锁。

public class WaitAndNotify {

private static Object lock = new Object();

static class ThreadA implements Runnable {

@Override

public void run() {

synchronized (lock) {

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

try {

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

lock.notify();

lock.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

lock.notify();

}

}

}

static class ThreadB implements Runnable {

@Override

public void run() {

synchronized (lock) {

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

try {

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

lock.notify();

lock.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

lock.notify();

}

}

}

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

new Thread(new ThreadA()).start();

Thread.sleep(1000);

new Thread(new ThreadB()).start();

}

}

Thread A: 0

Thread B: 0

Thread A: 1

Thread B: 1

Thread A: 2

Thread B: 2

Thread A: 3

Thread B: 3

Thread A: 4

Thread B: 4

信号量

public class Signal {

/**

* volatile关键字可以保证内存的可见性,如果用volatile关键字声明了一个变量,在一个线程里改变了这个变量值,那其他线程是立马可见更改后的值的。

*/

private static volatile int i = 0;

static class ThreadA implements Runnable {

@Override

public void run() {

while (i < 10) {

if (i % 2 == 0) {

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

synchronized (this) {

i++;

}

}

}

}

}

static class ThreadB implements Runnable {

@Override

public void run() {

while (i < 10) {

if (i % 2 == 1) {

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

synchronized (this) {

i++;

}

}

}

}

}

public static void main(String[] args) {

new Thread(new ThreadA()).start();

new Thread(new ThreadB()).start();

}

}

Thread A: 0

Thread B: 1

Thread A: 2

Thread B: 3

Thread A: 4

Thread B: 5

Thread A: 6

Thread B: 7

Thread A: 8

Thread B: 9

ThreadLocal

多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对同一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。TreadLocal是除了加锁这种同步方式之外的一种保证规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全的问题了。

线程池

为什么要使用线程池:1.创建/销毁线程需要消耗系统资源,线程池可以复用已创建的线程。2.控制并发的数量。并发数量过多,可能会导致资源消耗过多,从而造成服务器崩溃。3.可以对线程做统一管理。

ThreadPoolExecutor

public class ThreadPoolExecutorTest {

public static void main(String[] args) {

// ThreadPoolExecutor提供了四个构造函数: 1至5; 1至5 + 6; 1至5 + 7; 1至5 + 6 + 7

// 参数一:int corePoolSize 该线程池中核心线程数最大值,线程池新建的时候,如果当前线程总数小于corePoolSize,则新建核心线程,如果超过corePoolSize,则新建的是非核心线程

// 参数二:int maximumPoolSize 该线程池中线程总数最大值

// 参数三:long keepAliveTime 该线程池中非核心线程闲置超时时长

// 参数四:TimeUnit unit keepAliveTime的单位

// 参数五:BlockingQueue workQueue 该线程池中的任务队列,维护着等待执行的Runnable对象。常见类型:

// SynchronousQueue:这个队列接收到任务的时候,会直接提交给线程处理,而不保留它,如果所有线程都在工作怎么办?那就新建一个线程来处理这个任务!所以为了保证不出现的错误,使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大

// LinkedBlockingQueue:这个队列接收到任务的时候,如果当前线程数小于核心线程数,则新建线程(核心线程)处理任务;如果当前线程数等于核心线程数,则进入队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都将被添加到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过corePoolSize

// ArrayBlockingQueue:可以限定队列的长度,接收到任务的时候,如果没有达到corePoolSize的值,则新建线程(核心线程)执行任务,如果达到了,则入队等候,如果队列已满,则新建线程(非核心线程)执行任务,又如果总线程数到了maximumPoolSize,并且队列也满了,则发生错误

// DelayQueue:队列内元素必须实现Delayed接口,这就意味着你传进去的任务必须先实现Delayed接口。这个队列接收到任务时,首先先入队,只有达到了指定的延时时间,才会执行任务

// 参数六:ThreadFactory threadFactory 给线程起名字

// 参数七:RejectedExecutionHandler handler 异常处理

ThreadFactory threadFactory = new ThreadFactory() {

@Override

public Thread newThread(Runnable r) {

return new Thread(r,"thread name...");

}

};

// lambda表达式写法

// ThreadFactory threadFactory = (Runnable r) -> new Thread(r,"thread name");

ThreadPoolExecutor executor = new ThreadPoolExecutor(5,5,0L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(), threadFactory, new ThreadPoolExecutor.AbortPolicy());

executor.execute(new Runnable() {

@Override

public void run() {

System.out.println("@@@@");

}

});

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/343426.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

apache camel_Apache Camel中的断路器模式

apache camel骆驼通常在分布式环境中用于访问远程资源。 远程服务可能由于各种原因和期间而失败。 对于短时间后暂时不可用且可恢复的服务&#xff0c;重试策略可能会有所帮助。 但是某些服务可能会失败或挂起更长时间&#xff0c;从而使调用应用程序无响应且速度缓慢。 防止级…

matlab 形态学 颗粒_数字图像处理Matlab-形态学图像处理(附代码)

这是一篇基于matlab&#xff0c;数字图像处理的形态学研究与实现的文章&#xff0c;希望能对你产生帮助。我还写了一套《数字图像处理》(冈萨雷斯版本)的学习笔记&#xff0c;欢迎关注我的csdn同名主页&#xff0c;一起学习成长~1.Objectives:1&#xff0e;利用 MATLAB 研究二值…

视频光端机维护三大步骤

视频光端机分为发射端设备和接收端设备&#xff0c;发射端设备和摄像机一样置于室外&#xff0c;工程人员通常是对发射端设备过行维护测试。对于视频光端机的检修工作&#xff0c;我们一般分为三个步骤来进行测试。接下来就由飞畅科技的小编来带大家详细了解下视频光端机维护的…

2021年广东-国家开放大学考试指南(必看)-远程辅助以及微信公众号查题

考前准备工作 一、电脑以及摄像头、浏览器准备 1&#xff1a;一台比较流畅的电脑&#xff0c;这个千万不能马虎&#xff0c;万一考试电脑蓝屏或者卡顿&#xff0c;那是很要命的。 2&#xff1a;摄像头&#xff1a;笔记本有自带的可以用自带的&#xff0c;如果是台式机&#…

飞畅科技——视频光端机用光模块的选型详解

光模块的出现简化了数字视频光端机的设计&#xff0c;我们只要把光模块当作一个具有光电转换功能的部件就可以了。那么针对各种不同的光端机&#xff0c;应该怎样选择相应的光模块呢&#xff1f;接下来就由飞畅科技的小编来为大家详细介绍下视频光端机用光模块的选型&#xff0…

视频监控中的光端机是干什么用的?

现如今&#xff0c;随着社会的进步和经济的发展&#xff0c;人民生活开始富裕&#xff0c;经济宽裕的家庭用户已开始考虑使用监控产品来保护自己&#xff0c;监控已经呈现融入家庭生活的趋势。就数字监控而言&#xff0c;虽然是后起之秀&#xff0c;但依然是现阶段发展趋势。光…

每个Java开发人员都应该知道的10个基本工具

大家好&#xff0c;我们已经到了2019年的第二个月&#xff0c;我相信你们所有人都已经为2019年的学习目标以及如何实现这些目标制定了目标。 我一直在撰写一系列文章&#xff0c;为您提供一些知识&#xff0c;使您可以学习和改进以成为2019年更好的全方位开发人员&#xff0c;例…

spring集成jndi_Spring应用程序与JNDI连接池的集成测试

spring集成jndi我们都知道&#xff0c;无论何时连接到数据库&#xff0c;都需要使用连接池。 所有使用JDBC 4类的现代驱动程序都支持它。 在本文中&#xff0c;我们将概述Spring应用程序中的连接池&#xff0c;以及如何在非JEE环境&#xff08;例如测试&#xff09;中处理相同的…

数字光端机和模拟视频光端机的六个不同点

说到模拟视频光端机与数字光端机的不同之处&#xff0c;在这里飞畅科技的小编要介绍六点不一样的地方来详细讲解模拟视频光端机与数字光端机的不同。模拟视频光端机与数字光端机究竟有何区别&#xff0c;这也是众多用户所关心的确问题&#xff0c;本文从以下几个方面进行如下论…

在硒中按TagName定位元素

硒定位器是处理网页上的元素时的关键。 从ID&#xff0c;名称&#xff0c;类&#xff0c;标记名&#xff0c;XPath&#xff0c;CSS选择器等定位器列表中&#xff0c;可以根据需要选择其中任何一种&#xff0c;然后在网页上找到Web元素。 由于与tagName或linktext相比&#xff0…

Java注解参数类型枚举值_EffectiveJava-5-枚举和注解

用enum代替int常量1. int枚举&#xff1a;引入枚举前&#xff0c;一般是声明一组具名的int常量&#xff0c;每个常量代表一个类型成员&#xff0c;这种方法叫做int枚举模式。int枚举模式是类型不安全的&#xff0c;例如下面两组常量&#xff1a;性别和动物种类&#xff0c;二者…

工业交换机:如果出现了物理性故障该怎么判断?

一般工业交换机出现故障大致可以分为&#xff1a;软性能故障和物理性故障。软性能故障一般是指工业交换机在研发设计方面出现的问题。今天就由飞畅科技的小编来给大家聊聊工业交换机物理性故障该怎么判断&#xff1f;一起来看看吧&#xff01; 物理层故障主要是指交换机本身的…

q7goodies事例_Java 8 Friday Goodies:精益并发

q7goodies事例在Data Geekery &#xff0c;我们喜欢Java。 而且&#xff0c;由于我们真的很喜欢jOOQ的流畅的API和查询DSL &#xff0c;我们对Java 8将为我们的生态系统带来什么感到非常兴奋。 我们已经写了一些关于Java 8好东西的博客 &#xff0c;现在我们觉得是时候开始一个…

工业交换机的外壳设计重要吗?

工业交换机一般使用在环境比较恶劣的地方&#xff0c;例如工厂等&#xff0c;为了让工业交换机更好的使用&#xff0c;工业交换机的外壳设计起到至关重要的作用&#xff0c;工业交换机的外壳可以更好的保护工业交换机的使用&#xff0c;那么如何才能选到实用的工业交换机外壳呢…

工业交换机和工业4G路由器的区分

工业交换机也称作工业以太网交换机&#xff0c;即应用于工业控制领域的以太网交换机设备&#xff0c;由于采用的网络标准&#xff0c;其开放性好、应用广泛以及价格低廉、使用的是透明而统一的TCP/IP协议&#xff0c;以太网已经成为工业控制领域的主要通信标准。工业交换机具有…

工业交换机和工业级光纤收发器的区别

工业交换机和工业级光纤收发器都是网络数据传输设备中的重要组成部分。工业交换机是进行网络内数据交换的一种以太网连接设备&#xff0c;而工业级光纤收发器是延长传输距离的一种光电转换设备。那么他们之间具体有哪些不同之处呢&#xff1f;接下来就由飞畅科技的小编来为大家…

工业交换机选择时需要注意什么?

工业以太网交换机是用于连接以太网的设备&#xff0c;应用十分广泛&#xff0c;主要应用于&#xff1a;煤矿安全、轨道交通、工厂自动化、水处理系统、城市安防等。现如今&#xff0c;市场上的工业交换机品牌厂家有很多&#xff0c;如何选购合适的以太网交换机是件令人困扰的事…

html如何与php,html页面怎么跟php文件连接

HTML页面调用PHP文件的方法是要通过JavaScript来实现&#xff0c;在生成静态页面时&#xff0c;可以根据数据库id给html页面生成一个对应的JavaScript文件来调用PHP文件。HTML页面调用PHP文件的方法是要通过JavaScript来实现&#xff0c;在生成静态页面时&#xff0c;可以根据数…

为使节构建控制平面的指南第3部分-特定于域的配置API

这是探索为Envoy Proxy构建控制平面的系列文章的第3部分。 在本博客系列中&#xff0c;我们将研究以下领域&#xff1a; 采用一种机制来动态更新Envoy的路由&#xff0c;服务发现和其他配置 确定哪些组件构成了控制平面&#xff0c;包括后备存储&#xff0c;服务发现API&…

工业交换机中:千兆级别和快速级别传输效率对比

目前市面上的工业交换机种类繁多&#xff0c;我们在采购工业交换机的时候&#xff0c;一般都是按照传输速率来进行挑选的。千兆工业交换机和快速以太网交换机都是传输速率比较快的工业交换机&#xff0c;那么&#xff0c;他们之间有哪些区别呢&#xff1f;接下来就由飞畅科技的…