《Java 高并发》05 线程的基本操作

volatile 与 Java 内存模型

Java 内存模型都是围绕着原子性、有序性和可见性展开的。为了在适当的场合,确保线程间的原子性、有序性和可见性。Java 使用了一些特许的操作或者关键字来申明、告诉虚拟机,在这个地方,要尤其注意,不能随意变动优化目标指令。volatile 关键字就是其中之一。

当用 volatile 去申明一个变量是,就等于告诉虚拟机,这个变量极有可能会被某些程序或者线程修改。为了确保这个变量被修改后,应用程序范围内的所有线程都能“看到”这个改动,虚拟机就必须采用一些特殊的手段,保证这个变量的可见性等特点。

特别注意,volatile 并不能代替锁,它也无法保证一些符合操作的原子性。

使用示例:

public class MultiThreadLong {private static Long to = 0L;public static class ChangI implements Runnable {private Long to;public ChangI(Long to) {this.to = to;}@Overridepublic void run() {while (true) {MultiThreadLong.to = this.to;Thread.yield();}}}public static class ReadI implements Runnable {@Overridepublic void run() {while (true) {Long to = MultiThreadLong.to;if (to != 111 && to != -999 && to != 333 && to != 444) {System.out.println(to);Thread.yield();}}}}public static void main(String[] args) {new Thread(new ChangI(111L)).start();new Thread(new ChangI(-999L)).start();new Thread(new ChangI(333L)).start();new Thread(new ChangI(444L)).start();new Thread(new ReadI()).start();new Thread().stop();}}

线程组

在一个系统中,如果线程数量过多,而且功能分配比较明确,就可以将相同功能的线程放置在一个线程组中。

public class ThreadGroupName implements Runnable{@Overridepublic void run() {String groupAndName = Thread.currentThread().getThreadGroup().getName() +"-"+Thread.currentThread().getName();while (true){System.out.println("I am " + groupAndName);try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {ThreadGroup groupName1 = new ThreadGroup("groupName1");Thread thread1 = new Thread(groupName1,new ThreadGroupName(),"t1");Thread thread2 = new Thread(groupName1,new ThreadGroupName(),"t2");thread1.start();thread2.start();ThreadGroup groupName2 = new ThreadGroup("groupName2");Thread thread3 = new Thread(groupName2,new ThreadGroupName(),"t3");Thread thread4 = new Thread(groupName2,new ThreadGroupName(),"t4");thread3.start();thread4.start();}
}

上述创建了两个线程组 groupName1、groupName2,并创建了四个线程命名为 t1、t2、t3、t4,分别将 t1、t2 放入 groupName1中,t3、t4 放入 groupName2 中。日志打印如下:

I am groupName1-t1
I am groupName2-t4
I am groupName2-t3
I am groupName1-t2

注意:ThreadGroup 提供 stop() 方法,它会停止线程组内的所有线程。但它会出现和 Thread.stop() 相同的问题。

守护线程

守护线程是一种特殊的线程,就和它的名字一样,它是系统的守护者,在后台默默地完成一些系统性的服务,比如垃圾回收线程。与之相对应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。 如果用户线程全部结束,这也就意味着这个程序实际上无事可做了。守护线程要收的对象已经不存在了,那么整个应用程序就自然应该结束。因此,当一个 Java 应用内,只有守护线程时,Java 虚拟机就会自然退出。

想要创建一个守护线程只需要将它设置为守护线程即可,具体实现:

public class DaemonThread {public static class DaemonT implements Runnable{@Overridepublic void run() {while (true){System.out.println(System.currentTimeMillis() + "I am alive.");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}public static void main(String[] args) {try {DaemonT daemonT = new DaemonT();Thread thread = new Thread(daemonT);// 设置为守护线程thread.setDaemon(true);thread.start();Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}
}

线程优先级

Java 中的线程有自己的优先级。优先级高的线程在竞争资源师会更有有事,更可能抢占资源,当然,这个是一个概率问题。高优先级线程也有可能抢占失败。

在 Java 中,使用 1 到 10 表示线程优先级,数字越大优先级越高,默认优先级为 5。可以使用内置的三个静态变量来表示优先级:

    public final static int MIN_PRIORITY = 1;public final static int NORM_PRIORITY = 5;public final static int MAX_PRIORITY = 10;

线程优先级的使用:

public class PriorityDemo {public static class HightPriority extends Thread {static int count = 0;@Overridepublic void run() {while (true) {synchronized (HightPriority.class) {count++;if (count > 10000000){System.out.println("HightPriority is complate");break;}}}}}public static class LowPriority extends Thread {static int count = 0;@Overridepublic void run() {while (true) {synchronized (HightPriority.class) {count++;if (count > 10000000){System.out.println("LowPriority is complate");break;}}}}}public static void main(String[] args) {HightPriority hightPriority = new HightPriority();LowPriority lowPriority = new LowPriority();hightPriority.setPriority(Thread.MAX_PRIORITY);lowPriority.setPriority(Thread.MIN_PRIORITY);lowPriority.start();hightPriority.start();}}

上述代码创建了两个线程,分别是 HightPriority 设置为高优先级、LowPriority 设置为低优先级。让他们完成相同的工作,对 count 累加到 10000000,通过打印结果知道谁先完成工作。在对 count 累加前,使用 synchronized 产生一次资源竞争。目的是是的优先级的差异表现更为明显。

public class PriorityDemo {public static class HightPriority extends Thread {static int count = 0;@Overridepublic void run() {while (true) {synchronized (HightPriority.class) {count++;if (count > 10000000){System.out.println(System.currentTimeMillis() + " HightPriority is complate");break;}}}}}public static class LowPriority extends Thread {static int count = 0;@Overridepublic void run() {while (true) {synchronized (HightPriority.class) {count++;if (count > 10000000){System.out.println(System.currentTimeMillis() + " LowPriority is complate");break;}}}}}public static void main(String[] args) {HightPriority hightPriority = new HightPriority();LowPriority lowPriority = new LowPriority();hightPriority.setPriority(Thread.MAX_PRIORITY);lowPriority.setPriority(Thread.MIN_PRIORITY);lowPriority.start();hightPriority.start();}
}

synchronized

synchronized 的作用是实现线程间的同步。它的工作是对同步的代码加锁,是的每一次只能有一个线程进入同步块,从而保证线程间的安全性。

关键字 synchronized 使用方法:

  1. 同步代码块:同步代码块可以指定加锁对象,进入同步代码钱获得给定对象的锁。
  2. 作用于普通方法:相当于给当前实例加锁,进入同步代码前要获得当前实例的锁。
  3. 作用于静态方法:相当于对当前类加锁,进入同步代码钱要获得当前类的锁。

释放方式一:同步代码块

public class AccountingSync implements Runnable {static AccountingSync instance = new AccountingSync();static int num = 0;@Overridepublic void run() {for (int j = 0; j < 100000000; j++) {synchronized (instance) {num++;}}}public static void main(String[] args) {try {Thread t1 = new Thread(instance, "thread1");Thread t2 = new Thread(instance, "thread2");t1.start();t2.start();t1.join();t2.join();System.out.println(num);} catch (InterruptedException e) {e.printStackTrace();}}}

使用方式二:同步方法

public class AccountingSync2 implements Runnable {static AccountingSync2 instance = new AccountingSync2();static int num = 0;public synchronized void increase() {num++;}@Overridepublic void run() {for (int j = 0; j < 100000000; j++) {increase();}}public static void main(String[] args) {try {Thread t1 = new Thread(instance, "thread1");Thread t2 = new Thread(instance, "thread2");t1.start();t2.start();t1.join();t2.join();System.out.println(num);} catch (InterruptedException e) {e.printStackTrace();}}
}

基于上面两种凡是的错误加锁实现:

public class AccountingSync2 implements Runnable {static AccountingSync2 instance = new AccountingSync2();static int num = 0;public synchronized void increase() {num++;}@Overridepublic void run() {for (int j = 0; j < 100000000; j++) {increase();}}public static void main(String[] args) {try {Thread t1 = new Thread(new AccountingSync2(), "thread1");Thread t2 = new Thread(new AccountingSync2(), "thread2");t1.start();t2.start();t1.join();t2.join();System.out.println(num);} catch (InterruptedException e) {e.printStackTrace();}}}

上述锁对象是当前调用实例对象,但是在创建两个线程时都指向了不同的 AccountingSync2 锁对象。因此线程 t1 在进入同步方法前加锁 自己的 AccountingSunc2 实例,而线程 t2 也加锁自己 AccountingSync2 实例锁。因此,线程安全是无法保证的。

为解决上述请,synchronized 的第三种使用方式,将其作用在静态方法上。

public class AccountingSync3 implements Runnable {static AccountingSync3 instance = new AccountingSync3();static int num = 0;public synchronized static void increase() {num++;}@Overridepublic void run() {for (int j = 0; j < 100000000; j++) {increase();}}public static void main(String[] args) {try {Thread t1 = new Thread(new AccountingSync3(), "thread1");Thread t2 = new Thread(new AccountingSync3(), "thread2");t1.start();t2.start();t1.join();t2.join();System.out.println(num);} catch (InterruptedException e) {e.printStackTrace();}}}

静态同步方法的锁对象是当前类.class,尽管上诉两个线程创建了两个实例对象,但是他们使用的锁是同一个。

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

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

相关文章

mybatis 2 -常用数据操作

1、写入数据并获取自增ID XML配置&#xff1a; <!-- 写入数据获取自增ID --><insert id"insertLog" parameterType"com.mamaguwen.entity.sys_loginlog" useGeneratedKeys"true" keyProperty"logid">insert into sys_…

Spring常用的的注解对应xml配置详解

Component(value"")注解&#xff1a;组件 标记在类上&#xff0c;也可以放在接口上注解作用&#xff1a;把AccountDao实现类对象交由Spring IOC容器管理 相当于XML配置文件中的Bean标签 <bean id"userAnnonMapper" class"com.spring.mapper.User…

安卓模拟器bluestacks mac地址修改教程

http://szmars2008.blog.163.com/blog/static/118893702201373181349348/ 转载于:https://www.cnblogs.com/prayer521/p/4069037.html

Docker 搭建 ELK 日志系统,并通过 Kibana 查看日志

Docker 搭建 ELK 日志系统,并通过 Kibana 查看日志 docker-compose.yml version: 3 services:elasticsearch:image: elasticsearch:7.7.0 #镜像container_name: elasticsearch #定义容器名称restart: always #开机启动&#xff0c;失败也会一直重启environment:- "cl…

蟠桃记

Problem Description 喜欢西游记的同学肯定都知道悟空偷吃蟠桃的故事&#xff0c;你们一定都觉得这猴子太闹腾了&#xff0c;其实你们是有所不知&#xff1a;悟空是在研究一个数学问题&#xff01; 什么问题&#xff1f;他研究的问题是蟠桃一共有多少个&#xff01; 不过&#…

Spring 定时任务动态管理

管理 Spring 中定时任务 pom.xml <properties><hutool.version>5.6.6</hutool.version><lombok.version>1.18.20</lombok.version><spring-boot.web.version>2.2.10.RELEASE</spring-boot.web.version> </properties><de…

高效率Oracle SQL语句

1、Where子句中的连接顺序&#xff1a; ORACLE采用自下而上的顺序解析WHERE子句。 根据这个原理&#xff0c;表之间的连接必须写在其他WHERE条件之前&#xff0c; 那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。 举例&#xff1a; (低效) select ... from table1…

RabbitMQ Management:Management API returned status code 500

错误显示&#xff1a; 解决方案&#xff1a; 因为是使用docker 容器安装的&#xff0c;所有需要进入容器 docker exec -it rabbitmq /bin/bash进入目录 cd /etc/rabbitmq/conf.d/执行命令 echo management_agent.disable_metrics_collector false > management_agent.dis…

Android JNI和NDK学习(5)--JNI分析API

Java类型和本地类型对应 在如下情况下&#xff0c;需要在本地方法中应用java对象的引用&#xff0c;就会用到类型之间的转换&#xff1a; java方法里面将参数传入本地方法&#xff1b;在本地方法里面创建java对象&#xff1b;在本地方法里面return结果给java程序。Java基本类型…

RabbitMq 消费失败,重试机制

方案一&#xff1a; 本地消息表 定时任务 本地消息表&#xff1a;主要用于存储 业务数据、交换机、队列、路由、次数 定时任务&#xff1a;定时扫描本地消息表&#xff0c;重新给业务队列投递消息。 具体思路&#xff1a;业务队列消费失败时&#xff0c;把 业务数据、交换机、…

Android常用的工具类

主要介绍总结的Android开发中常用的工具类&#xff0c;大部分同样适用于Java。目前包括HttpUtils、DownloadManagerPro、ShellUtils、PackageUtils、 PreferencesUtils、JSONUtils、FileUtils、ResourceUtils、StringUtils、 ParcelUtils、RandomUtils、ArrayUtils、ImageUtils…

0. Spring 基础

BeanDefinition BeanDefinition 表示 Bean 定义&#xff1a; Spring根据BeanDefinition来创建Bean对象&#xff1b;BeanDefinition有很多的属性用来描述Bean&#xff1b;BeanDefiniton是Spring中非常核心的概念。BeanDefiniton中重要的属性&#xff1a; a. beanClass&#xf…

1. Spring 源码:Spring 解析XML 配置文件,获得 Bena 的定义信息

通过 Debug 运行 XmlBeanDefinitionReaderTests 类的 withFreshInputStream() 的方法&#xff0c;调试 Spring 解析 XML 配置文件&#xff0c;获得 Bean 的定义。 大体流程可根据序号查看&#xff0c;xml 配置文件随便看一眼&#xff0c;不用过多在意。 <?xml version&qu…

c++ 读取文件 最后一行读取了两次

用ifstream的eof()&#xff0c;竟然读到文件最后了&#xff0c;判断eof还为false。网上查找资料后&#xff0c;终于解决这个问题。 参照文件&#xff1a;http://tuhao.blogbus.com/logs/21306687.html 在使用C/C读文件的时候&#xff0c;一定都使用过eof&#xff08;&#xff0…

java中的io系统详解(转)

Java 流在处理上分为字符流和字节流。字符流处理的单元为 2 个字节的 Unicode 字符&#xff0c;分别操作字符、字符数组或字符串&#xff0c;而字节流处理单元为 1 个字节&#xff0c;操作字节和字节数组。 Java 内用 Unicode 编码存储字符&#xff0c;字符流处理类负责将外部的…

js获取字符串最后一个字符代码

方法一&#xff1a;运用String对象下的charAt方法 charAt() 方法可返回指定位置的字符。 代码如下 复制代码 str.charAt(str.length – 1) 请注意&#xff0c;JavaScript 并没有一种有别于字符串类型的字符数据类型&#xff0c;所以返回的字符是长度为 1 的字符串 方法二&#…

Unity3D Shader入门指南(二)

关于本系列 这是Unity3D Shader入门指南系列的第二篇&#xff0c;本系列面向的对象是新接触Shader开发的Unity3D使用者&#xff0c;因为我本身自己也是Shader初学者&#xff0c;因此可能会存在错误或者疏漏&#xff0c;如果您在Shader开发上有所心得&#xff0c;很欢迎并恳请您…

JVM:如何分析线程堆栈

英文原文&#xff1a;JVM: How to analyze Thread Dump 在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因。在我看来线程堆栈分析技术是Java EE产品支持工程师所必须掌握的一门技术。在线程堆栈中存储的信息&#xff0c;通常远超出你的想象&…

一个工科研究生毕业后的职业规划

http://blog.csdn.net/wojiushiwo987/article/details/8592359一个工科研究生毕业后的职业规划 [wojiushiwo987个人感触]:说的很诚恳&#xff0c;对于马上面临毕业的我很受用&#xff0c;很有启发。有了好的职业生涯规划&#xff0c;才有了前进的方向和动力&#xff0c;才能…

SQLSERVER中如何忽略索引提示

SQLSERVER中如何忽略索引提示 原文:SQLSERVER中如何忽略索引提示SQLSERVER中如何忽略索引提示 当我们想让某条查询语句利用某个索引的时候&#xff0c;我们一般会在查询语句里加索引提示&#xff0c;就像这样 SELECT id,name from TB with (index(IX_xttrace_bal)) where bal…