《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.pswp.cn/news/273545.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/273545.shtml
英文地址,請注明出處:http://en.pswp.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…