深入理解Java多線程(一)

  關於java多線程的概念以及基本用法:java多線程基礎

  1,停止線程

  停止線程意味着在線程執行完之前停止正在做的操作,即立刻放棄當前的操作,這並不容易。停止線程可以用Thread.stop()方法,但是這個方法不安全,所以不建議使用,還有一個方法就是Thread.interrupt()方法,但是這個方法不會終止一個正在運行的線程,需要添加一個判斷纔可以完成線程的停止。

  1.1,停不下來的線程

  public void interrupt()

  將調用者線程的中斷狀態設爲true

  Thread.interrupt()方法只是在當前線程中打了一個停止的標記,並不是直接將線程停止

  class MyThread_1 extends Thread{

  @Override

  public void run() {

  super.run();

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

  System.out.println("i="+(i+1));

  }

  }

  }

  public class Demo1_7_1 {

  public static void main(String[] args) {

  try {

  MyThread_1 thread = new MyThread_1();

  thread.start();

  Thread.sleep(200);

  thread.interrupt();

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  結果:

  從結果可以看到,使用thread.interrupt()線程並未停止

  1.2,判斷線程是否是停止狀態

  public static boolean interrupted

  只能通過Thread.interrupted()調用

  class MyThread_2 extends Thread{

  @Override

  public void run() {

  super.run();

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

  System.out.println("i="+(i+1));

  }

  }

  }

  public class Demo1_7_2 {

  public static void main(String[] args) {

  try {

  MyThread_2 thread = new MyThread_2();

  thread.start();

  Thread.sleep(2000);

  thread.interrupt();

  System.out.println("是否停止1?="+thread.interrupted());

  System.out.println("是否停止2?="+thread.interrupted());

  } catch (Exception e) {

  e.printStackTrace();

  }

  System.out.println("end");

  }

  }

  運行結果:

  當前線程是main並沒有中斷過,所以打印結果都是false

  將main函數代碼改爲

  Thread.currentThread().interrupt();

  System.out.println("是否停止1?="+Thread.interrupted());

  System.out.println("是否停止2?="+Thread.interrupted());

  System.out.println("end");

  結果爲

  是否停止1?=true

  是否停止2?=false

  end

  爲什麼第二個值是false?這是因爲第一次調用Thread.interrupted()返回當前線程中斷狀態,然後會將線程的中斷狀態設爲false

  public boolean isInterrupted()

  判斷調用者線程的中斷狀態

  講main函數修改

  try {

  MyThread_2 thread = new MyThread_2();

  thread.start();

  Thread.sleep(2000);

  thread.interrupt();

  System.out.println("是否停止1?="+thread.isInterrupted());

  System.out.println("是否停止2?="+thread.isInterrupted());

  } catch (Exception e) {

  e.printStackTrace();

  }

  System.out.println("end");

  運行結果是:

  。。。

  。。。。

  i=9997

  i=9998

  i=9999

  i=10000

  是否停止1?=false

  是否停止2?=false

  end

  很神奇這裏返回結果都是false,想了半天,後來在網上查了資料明白是sleep函數的原因Thread.sleep(2000);休眠的是主線程,這裏休眠的就是main函數,在這段時間裏thread可能已經執行完了,所以thread.interrupt();也就起不到作用了,修改sleep函數時間,返回結果就都是true了

  此方法作用:測試線程是否已經是中斷狀態,但是不會執行一次後將中斷狀態設置爲false

  1.3,停止線程–異常法

  使用if語句判斷線程是否是停止狀態來控制後面的代碼是否繼續執行,但是無論是否停止線程,for循環外run方法裏的代碼都會執行,這裏用throw new InterruptedException();來產生異常,從而結束線程,線程其餘代碼也不會執行了

  class MyThread_3 extends Thread{

  @Override

  public void run() {

  super.run();

  try {

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

  if(this.isInterrupted()) {

  System.out.println("已經是停止狀態了,我要退出");

  throw new InterruptedException();

  }

  System.out.println("i="+(i+1));

  }

  System.out.println("for下面");

  }catch(InterruptedException e) {

  System.out.println("進Mythread類run方法中的catch");

  e.printStackTrace();

  }

  }

  }

  public class Demo1_7_3 {

  public static void main(String[] args) {

  try {

  MyThread_3 thread = new MyThread_3();

  thread.start();

  Thread.sleep(2000);

  thread.interrupt();

  } catch (Exception e) {

  e.printStackTrace();

  }

  System.out.println("end");

  }

  }

  結果:

  。。。。

  。。。。。

  i=366068

  i=366069

  i=366070

  end

  已經是停止狀態了,我要退出

  進Mythread類run方法中的catch

  java.lang.InterruptedException

  at chapter_1.MyThread_3.run(Demo1_7_3.java:12)

  1.4,停止線程–在Sleep狀態

  在sleep狀態下停止某一線程會進入catch語句,並且清除停止狀態值,使之變成false

  class MyThread_4 extends Thread{

  @Override

  public void run() {

  super.run();

  try {

  System.out.println("run begin");

  Thread.sleep(200000);

  System.out.println("run end");

  } catch (Exception e) {

  // TODO: handle exception

  System.out.println("在沉睡中被停止進入catch"+this.isInterrupted());

  e.printStackTrace();

  }

  }

  }

  public class Demo1_7_4 {

  public static void main(String[] args) {

  try {

  MyThread_4 thread = new MyThread_4();

  thread.start();

  Thread.sleep(200);

  thread.interrupt();

  } catch (Exception e) {

  // TODO: handle exception

  System.out.println("main catch");

  e.printStackTrace();

  }

  System.out.println("end");

  }

  }

  結果:

  run begin

  end

  在沉睡中被停止進入catchfalse

  java.lang.InterruptedException: sleep interrupted

  at java.lang.Thread.sleep(Native Method)

  at chapter_1.MyThread_4.run(Demo1_7_4.java:10)

  1.5,停止線程–暴力停止

  所謂暴力停止就是最直接的方法了–stop(),這種方法直接讓線程停止,但是這也會引來一些問題,比如線程的安全問題,不建議使用

  1.6,停止線程–使用return,鄭州不孕不育好醫院:http://www.zzchyy110.com/

  將方法interrupt()與return結合使用也可以實現停止線程

  class MyThread_8 extends Thread{

  @Override

  public void run() {

  super.run();

  while(true) {

  if(this.isInterrupted()) {

  System.out.println("停止了!");

  return;

  }

  System.out.println("timer="+System.currentTimeMillis());

  }

  }

  }

  public class Demo1_7_8 {

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

  MyThread_8 t = new MyThread_8();

  t.start();

  Thread.sleep(2000);

  t.interrupt();

  }

  }

  結果:

  。。。

  。。。

  timer=1533907137644

  timer=1533907137644

  timer=1533907137644

  timer=1533907137644

  timer=1533907137644

  timer=1533907137644

  停止了!

  1.7,yield方法

  yield()方法作用是放棄當前CPU資源,讓給其他線程去使用,但是放棄時間不確定

  1.8,線程優先級

  操作系統會對線程進行劃分優先級,優先級高的線程會優先分配到資源,類似於現實中的VIP,Java將線程從1到10劃分十個等級,默認優先級爲NORM_PRIORITY,設置優先級用setPriority()

  setPriority()源碼:

  public final void setPriority(int i)

  {

  checkAccess();

  if(i > 10 || i < 1)

  throw new IllegalArgumentException();

  ThreadGroup threadgroup;

  if((threadgroup = getThreadGroup()) != null)

  {

  if(i > threadgroup.getMaxPriority())

  i = threadgroup.getMaxPriority();

  setPriority0(priority = i);

  }

  }

  public final static int MIN_PRIORITY=1;

  public final static int NORM_PRIORITY=5;

  public final static int MAX_PRIORITY=10;

  此外線程還具有繼承性,若線程A啓動線程B,則B具有A的優先級

  1.9,守護線程

  在Java線程中有兩種線程,一種是用戶線程一種是守護線程,我們常說的操作系統中並沒有守護線程這一說,原因是Java是構建在JVM之上的。顧名思義,守護線程具有陪伴的意思,當進程中不存在非守護線程時,守護線程就會自動銷燬。

  Daemon作用是爲了其他線程的運行提供便利服務,只有當最後一個非守護線程結束時,守護線程纔會隨着JVM一同結束工作,守護線程典型的例子就是GC

  class MyThread_11 extends Thread{

  private int i=0;

  @Override

  public void run() {

  super.run();

  try {

  while(true) {

  i++;

  System.out.println("i="+(i));

  Thread.sleep(1000);

  }

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  public class Demo1_11 {

  public static void main(String[] args) {

  try {

  MyThread_11 thread = new MyThread_11();

  thread.setDaemon(true);

  thread.start();

  Thread.sleep(5000);

  System.out.println("守護線程離開了!");

  } catch (Exception e) {

  e.printStackTrace();

  }

  }

  }

  結果:

  i=1

  i=2

  i=3

  i=4

  i=5

  守護線程離開了!

  2018.9.9補充:

  突然對this與Thread.currentThread()產生了疑問,下面來對這個區別來介紹一下:

  創建一個MyObject類:

  public class MyThread extends Thread{

  public MyThread() {

  }

  @Override

  public void run() {

  System.out.println("Thread.currentThread.getName= "

  +Thread.currentThread().getName());

  System.out.println("this.getName= "+this.getName());

  }

  }

  測試類:

  public class Run {

  public static void main(String[] args) {

  MyThread myThread = new MyThread();

  Thread t = new Thread(myThread);

  t.start();

  }

  }

  結果:

  Thread.currentThread.getName= Thread-1

  this.getName= Thread-0

  顯然這裏this和Thread.currentThread()指向的不是同一個線程,看一下Thread源碼:

  Thread t = new Thread(myThread)這句傳入的對象MyThread就是源碼裏的target對象,Thread類的構造方法會給線程一個默認的名字,從”Thread-0”開始

  this.getName()其實就是target.getName(),返回的是執行MyThread myThread = new MyThread()的線程名,而Thread.currentThread().getName()返回的是t.getName(),返回的是Thread t = new Thread(myThread)這句創建的線程


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章