线程是系统调用的最基本单位,多个线程组成进程。电脑中真正执行的线程,我们看到的是进程。
一:定义
进程的定义:进程是由一个或多个线程组成。
线程的定义:CPU调度和分配的最基本单位。
二:多线程
定义:在同一个进程中,同时运行多个线程来完成不同的工作。
原理:CPU不能同时运行多个线程,在一个时间点系统只能运行一个线程。但是在CPU之中线程之间是高速切换的,人是感觉不到的。所以直观上的感觉是同步执行的。
三:多线程的优点
01.充分利用CPU的资源 02.提升用户的体验
四:代码实现多线程
实现步骤:必须继承Thread 或者实现Runnable接口,然后重写run() 方法,在main方法中实例化Thread对象,调用线程类的start()方法启动线程;
五:线程的分类
用户线程: User Thread :默认我们创建的线程就是!
守护线程: Daemon Thread: 为其他线程提供服务的! GC线程需要我们手动,setDaemon(true)方法设置。
注意事项:
01.默认我们创建的线程就是用户线程!通过Thread类中的setDaemon(true)可以转换成守护线程。
02.只要有用户线程存在,程序就不会终止,所有的用户线程都执行完毕了, 程序会结束所有的守护线程!JVM停止工作!
03.setDaemon(true)必须在start()之前
04.守护线程永远不要访问资源!!!因为守护线程随时都可能销毁!
1 public class DaemonThread implements Runnable { 2 3 @Override 4 public void run() { 5 BufferedWriter bw = null; 6 Writer out = null; 7 File file = new File("e:/heihei.txt"); 8 try { 9 System.out.println("进入了 run()"); 10 Thread.sleep(1000); // 保证main先执行完毕 11 // 获取输出流对象 12 out = new FileWriter(file); 13 bw = new BufferedWriter(out); 14 bw.write("守护线程不可能写进去内容!!看不到我!"); 15 } catch (IOException e) { 16 e.printStackTrace(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } finally { 20 try { 21 bw.close(); 22 out.close(); 23 } catch (IOException e) { 24 e.printStackTrace(); 25 } 26 } 27 } 28 29 public static void main(String[] args) { 30 Thread thread = new Thread(new DaemonThread(), "守护线程"); 31 // 变成 守护线程 32 // thread.setDaemon(true); 33 thread.start(); 34 System.out.println("用户线程===》main结束"); 35 } 36 }
01.继承Thread类
1 /** 2 * 自己定义的多线程类 3 * 4 * 01.必须继承Thread 5 * 02.然后重写run() ====>普通的方法 6 * 03.启动线程调用线程类的start() 7 */ 8 public class MyThread extends Thread { 9 10 /** 11 * 重写了Thread类中的 run() 12 */ 13 @Override 14 public void run() { 15 for (int i = 1; i <= 20; i++) { 16 System.out.println(Thread.currentThread().getName() + "在执行==》" + i); 17 } 18 } 19 20 public static void main(String[] args) { 21 // 创建出来了2个线程对象 然后分别执行 run() 22 MyThread t1 = new MyThread(); 23 MyThread t2 = new MyThread(); 24 // 分别给两个线程 设置名字 25 t1.setName("线程1"); 26 t2.setName("线程2"); 27 /* 28 * t1.run(); t2.run(); 直接调用run()其实就是执行普通方法,那么第一个run()没执行完毕,第二个无法执行 29 */ 30 // 真正的启动线程,但是线程没有执行! 明明是调用的start(),却是显示的run()方法内容! 31 // 线程的真正执行是run() 32 t1.start(); 33 t2.start(); 34 35 } 36 37 }
02.实现Runnable接口
1 public class MyRunnable implements Runnable { 2 /** 3 * 重写了Runnable类中的 run() 4 */ 5 @Override 6 public void run() { 7 for (int i = 1; i <= 20; i++) { 8 System.out.println(Thread.currentThread().getName() + "在执行==》" + i); 9 } 10 } 11 12 public static void main(String[] args) { 13 // 创建两个线程对象 14 Thread t1 = new Thread(new MyRunnable(), "线程1"); 15 Thread t2 = new Thread(new MyRunnable(), "线程2"); 16 // 启动线程 17 t1.start(); 18 t2.start(); 19 } 20 21 }
03.实现Callable接口
(一)线程的生命周期
01.新生状态 MyThread t1=new MyThread();
Thread t2 = new Thread(new RunnableImp());
02.就绪状态 调用start()方法,线程就绪,等待分配CPU资源。
03.运行状态 CPU分配时间片给run方法,线程开始执行run方法线程体代码
04.阻塞状态 sleep() , 休眠(不释放资源) wait() ,等待(释放系统资源) yield() , 礼让(静态方法,不一定成功。) join(),强制执行至结束线程体代码,才会释放资源。
05.死亡状态 run()执行完毕正常死亡 ,或者是run()执行过程中,出现了异常(java.lang.IllegalThreadStateException)终止运行,线程死亡。
(二)关于start()方法,与run()方法的区别:
01.start方法是是Thread中的方法,调用后会启动线程至就绪状态,底层会调用run方法。继承Runnable接口没有该方法。
02.run方法是普通的方法,调用run方法是调用普通方法,不会启动多线程。方法体称之为线程体,当CPU分配时间片给线程时,线程才处于运行状态。
(三)线程的调度方法
01.设置线程的优先级(有一定的概率调节线程的抢占资源的能力)
1 /** 2 * 3 * 线程的优先级 Priority 4 * 默认值是5 5 * 取值范围是 1-10 6 * 优先级越高 代表获取CPU资源的概率越高! 并不代表一定高或者一定执行! 7 * 8 */ 9 public class PriorityThread implements Runnable { 10 @Override 11 public void run() { 12 for (int i = 1; i <= 20; i++) { 13 System.out 14 .println(Thread.currentThread().getName() + "======>" + i); 15 } 16 } 17 18 public static void main(String[] args) { 19 // 创建两个线程类对象 20 Thread t1 = new Thread(new PriorityThread(), "线程1"); 21 Thread t2 = new Thread(new PriorityThread(), "线程2"); 22 System.out.println("t1默认的优先级===》" + t1.getPriority()); 23 System.out.println("t2默认的优先级===》" + t2.getPriority()); 24 // 设置t2的优先级为10 只是一种概率问题 并不能保证 每次都是t2先执行 25 t2.setPriority(10); 26 // 启动线程 27 t1.start(); 28 t2.start(); 29 } 30 }
02.强制加入join()方法(一定会先执行完毕调用方法的线程)
1 /** 2 * 2.Join 加入新线程 3 * 让新的线程加入进来,新线程执行完毕之后,其他线程才能执行! 4 */ 5 public class JoinThread implements Runnable { 6 7 @Override 8 public void run() { 9 for (int i = 1; i <= 50; i++) { 10 System.out 11 .println(Thread.currentThread().getName() + "======>" + i); 12 } 13 } 14 15 // 测试方法 16 public static void main(String[] args) { 17 18 Thread t1 = new Thread(new JoinThread(), "新线程1"); 19 // 启动新线程 20 t1.start(); 21 for (int i = 1; i <= 20; i++) { // 主线程的输出 22 System.out 23 .println(Thread.currentThread().getName() + "======>" + i); 24 if (i == 5) { 25 try { 26 System.out.println("T1线程 强制 加入"); 27 t1.join(); // 阻止main线程 28 } catch (InterruptedException e) { 29 e.printStackTrace(); 30 } 31 } 32 } 33 } 34 }
03.sleep(long ms) Thread类中静态方法,会抛出InterruptedException异常
1 /** 2 * Sleep 线程的休眠 不会释放资源 3 * 4 * sleep(long ms) Thread类中方法 5 01.线程的休眠,会占着CPU资源不放!其他的线程都无法获取CPU资源!必须等待! 6 02.long ms指的是在多长时间之后,进入 就绪状态 7 8 wait()是Object类中的方法! 9 线程wait()的时候,会释放占用的CPU资源! 10 01.其他的线程无需等待! 11 02.必须使用notify唤醒才能进入 就绪状态 12 */ 13 public class SleepThread implements Runnable { 14 @Override 15 public void run() { 16 for (int i = 1; i <= 20; i++) { 17 System.out 18 .println(Thread.currentThread().getName() + "======>" + i); 19 try { 20 Thread.sleep(1000);// 线程休眠一秒 21 } catch (InterruptedException e) { 22 e.printStackTrace(); 23 } 24 } 25 } 26 27 public static void main(String[] args) { 28 // 创建线程类对象 29 Thread t = new Thread(new SleepThread(), "休眠的线程"); 30 // 线程启动 31 t.start(); 32 } 33 }
04.sleep 与 wait 方法的区别
sleep(long ms) Thread类中方法 (ms表示等待的毫秒数)
01.线程的休眠,会占着CPU资源不放,其他的线程都无法获取CPU资源。必须等待!
02.long ms指的是在多长时间之后,进入就绪状态;
wait()是Object类中的方法! 线程wait()的时候,会释放占用的CPU资源!
01.其他的线程无需等待!
02.必须使用notify()唤醒才能进入就绪状态;
05.yield();礼让方法(不一定有效)
1 public class YieldThread implements Runnable { 2 3 @Override 4 public void run() { 5 for (int i = 1; i <= 50; i++) { 6 System.out 7 .println(Thread.currentThread().getName() + "======>" + i); 8 } 9 } 10 11 public static void main(String[] args) { 12 // 创建线程对象 13 Thread t = new Thread(new YieldThread(), "老幼病残线程"); 14 // 启动线程 15 t.start(); 16 for (int i = 1; i <= 50; i++) { 17 System.out 18 .println(Thread.currentThread().getName() + "======>" + i); 19 if (i % 5 == 0) { 20 System.out.println("======main开始礼让了======"); 21 Thread.currentThread().yield(); // 礼让 22 } 23 } 24 } 25 }
06.设置中断状态。(只是一种标签,不影响线程的运行)
1 /** 2 * 设置线程的 中断状态 Interrupt 3 01.interrupt : 只是设置线程的中断状态,不是终止线程! 线程还会继续执行! 4 02.isInterrupted:来判断线程是否是 中断状态! 5 03.interrupted:清除中断状态 interrupted(); 6 04.执行了wait或者sleep,会默认清除中断状态 7 */ 8 public class InterruptThread implements Runnable { 9 @Override 10 public void run() { 11 // 判断线程是否是中断状态 12 if (Thread.currentThread().isInterrupted()) { 13 System.out.println("当前线程是 中断状态!"); 14 } else { 15 for (int i = 1; i <= 50; i++) { 16 System.out.println("线程的状态是===》" 17 + Thread.currentThread().isInterrupted()); 18 System.out.println(Thread.currentThread().getName() + "======>" 19 + i); 20 if (i == 25) { 21 Thread.currentThread().interrupt(); // 线程的中断状态 22 } 23 if (i == 40) { 24 Thread.currentThread().interrupted(); // 清除 线程的中断状态 25 } 26 } 27 } 28 } 29 30 public static void main(String[] args) { 31 // 创建线程对象 32 Thread t = new Thread(new InterruptThread(), "中断线程"); 33 // 启动线程 34 t.start(); 35 } 36 37 }