《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 使用方法:
釋放方式一:同步代碼塊
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,盡管上訴兩個線程創建了兩個實例對象,但是他們使用的鎖是同一個。
總結
以上是生活随笔為你收集整理的《Java 高并发》05 线程的基本操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第十八期:闲鱼上哪些商品抢手?Pytho
- 下一篇: 计算机在数据处理方面的论文,数据挖掘论文