一个进程可以包含多个线程,但至少要有一个线程。
多进程模式:每个进程只有一个线程
多线程模式:一个进程有多个线程
优缺点:
一个java程序就是一个JVM进程,JVM进程用一个主线程来执行main()方法,在main()方法内部又可以启动多个线程。多线程需要同步读取共享数据。
方法一:从Thread派生一个子类,并覆写run()方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.example; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.LinkedList; import java.util.List; public class Main { public static void main(String[] args) throws IOException { Thread t1 = new Thread(); t1.start(); } } class MyThread extends Thread { @Override public void run() { System.out.println("Hello World"); } }
方法二:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package org.example; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.LinkedList; import java.util.List; public class Main { public static void main(String[] args) throws IOException { Thread t1 = new Thread(new MyRunnable()); ti.start(); } } class MyRunnable implements Runnable { @Override public void run() { System.out.println("Hello World"); } }
方法三:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package org.example; import java.io.*; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.util.LinkedList; import java.util.List; public class Main { public static void main(String[] args) throws IOException { Thread t1 = new Thread(()->{ System.out.println("hello world"); }); t1.start(); } }
执行顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package org.example; import java.io.*; import java.nio.file.*; public class Main { public static void main(String[] args) throws IOException { System.out.println("main run..."); Thread t1 = new Thread(){ public void run(){ System.out.println("thread run..."); System.out.println("thread end..."); } }; t1.start(); System.out.println("main end..."); } }
main线程执行的代码有4行,首先会打印main run...,然后创建Thread对象,接着调用start()启动新线程。所以mian end...与新线程是同时执行的,程序本身无法确定线程的调度顺序。
可以使用Thread.sleep()强制当前线程暂停来控制哪个线程线执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package org.example; import java.io.*; import java.nio.file.*; public class Main { public static void main(String[] args) throws IOException { System.out.println("main run..."); Thread t1 = new Thread(){ public void run(){ System.out.println("thread run..."); try{Thread.sleep(10);}catch(Exception e){} System.out.println("thread end..."); } }; t1.start(); try{Thread.sleep(20);}catch(Exception e){} System.out.println("main end..."); } }
注意:
在mian线程内直接调用run()方法不会新启动线程,必须调用Thread实例的start()方法才能启动新线程。
一个线程对象只能调用一次start()方法。
也可以通过Thread.setPriority(int n)来设置线程的优先级(1~10,默认值是5),操作系统对高优先级线程可能调度更频繁,但不能保证一定会先执行。
线程状态
New:新创建的线程,尚未执行;
Runnable:运行中的线程,正在执行run()方法的Java代码;
Blocked:运行中的线程,因为某些操作被阻塞而挂起;
Waiting:运行中的线程,因为某些操作在等待中;
Timed Waiting:运行中的线程,因为执行sleep()方法正在计时等待;
Terminated:线程已终止,因为run()方法执行完毕。
线程终止的原因:
线程正常终止:run()方法执行到语句返回;
线程意外终止:run()因为未捕获的异常导致线程终止;
对某个线程的Thread实例调用stop()方法强制终止。
使用join()或join(long)可以让主线程等待子线程结束后再运行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package org.example; import java.io.*; import java.nio.file.*; public class Main { public static void main(String[] args) throws IOException, InterruptedException { Thread t=new Thread(()->{ System.out.println("hello"); }); System.out.println("start"); t.start(); t.join(); System.out.println("end"); } }
中断线程 Thread.interrupt():中断线程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package org.example; import java.io.*; import java.nio.file.*; public class Main { public static void main(String[] args) throws IOException, InterruptedException { Thread t=new MyThread(); t.start(); t.sleep(10); t.interrupt(); t.join(); System.out.println("end"); } } class MyThread extends Thread { @Override public void run() { int i=0; while(!isInterrupted()) { i++; System.out.println(i+"hello~"); } } }
interrupt方法只是修改线程中的中断标记位为true,并不会执行中断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package org.example;import java.io.*;import java.nio.file.*;public class Main { public static void main (String[] args) throws IOException, InterruptedException { Thread t=new MyThread (); t.start(); } } class MyThread extends Thread { @Override public void run () { int i=0 ,x=10 ; while (i<15 ) { if (i==x) { System.out.println("线程要被中断了:" +i); this .interrupt(); } i++; System.out.println("当前运行数字为:" +i); } } }
当一个线程因调用Object.wait()、Thread.sleep()或Thread.join()而进入等待/定时等待状态 时,若其他线程调用了该线程的interrupt()方法,则该线程会:
立即从等待方法中抛出 ******InterruptedException**(这是一个受检异常,必须被捕获或声明)。
中断状态标志被自动清除 (isInterrupted()会返回false)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package org.example;import java.io.*;import java.nio.file.*;public class Main { public static void main (String[] args) throws IOException, InterruptedException { Thread t=new Thread (()->{ try { Thread.sleep(5000 ); System.out.println("已过去" ); } catch (InterruptedException e) { System.out.println("中断被打断" ); } }); t.start(); Thread.sleep(1000 ); t.interrupt(); } }
也可以用volatile关键字来表示线程间共享变量,确保每个线程都能读取到更新后的变量值。
在JVM中,变量的值保存在主内存中,当线程访问变量时,会先获得一个副本并保存在自己的工作内存中。如果线程修改了变量的值,JVM会在某个时刻把修改后的值回写到主内存中,但时间是不确定的。
因此,volatile关键字的作用是:
每次访问变量时,总是获取主内存的最新值;
每次修改变量后,立刻回写到主内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package org.example;import java.io.*;import java.nio.file.*;public class Main { public static void main (String[] args) throws IOException, InterruptedException { MyThread t=new MyThread (); t.start(); Thread.sleep(1000 ); t.running=false ; } } class MyThread extends Thread { public volatile boolean running=true ; public void run () { int i=0 ; while (running) { i++; System.out.println(i); } } }