1.前言
(1)线程的上一级是进程,进程是程序的一次执行过程,是系统运行程序的基本单位,因此进程是动态的。 (2)线程与进程相似,但线程是一个比进程更小的执行单位,也被称为轻量级进程。一个进程在其执行的过程中可以产生多个线程。 (3)多线程就是多个线程同时运行或交替运行。 (4)提倡多线程而不是多进程 的原因是 线程间的切换和调度的成本远远小于进程。 (5)自定义线程可以继承Thread类 ,或者 实现Runnable接口 。 (6)多进程共享数据导致脏数据,可以使用同步锁 synchronized 解决。 (7)使用 interrupt() 和 return 配合 停止线程。 (8)不是优先级低的线程就一定在优先级高的线程后面运行,而是运行的机率会低一些,,反之亦然。 (9)线程分为用户线程和守护线程 ,当 用户线程 结束后,守护线程跟着结束 ,对于自定义线程可使用方法 setDaemon(true); 设置该线程为守护线程 。 常见的 守护线程是 垃圾回收线程
(10)开启线程的方式一共三种:Thread类、Runnable接口、callable接口
2.自定义线程
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; public class T1 { @Test public void t1(){ //调用方法一 // Mythread m = new Mythread(); // m.start(); //调用方法二 MyRunnable mr = new MyRunnable(); Thread thread=new Thread(mr); thread.start(); System.out.println("结束"); } } //方法一: class Mythread extends Thread { @Override public void run(){ super.run(); System.out.println("我的线程"); } } //方法二: class MyRunnable implements Runnable{ @Override public void run() { System.out.println("使用接口实现我的线程"); } }
3.多个线程之间不共享变量线程安全的情况
每个线程都有自己的实例变量count ,互不影响
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; /** * 多个线程之间不共享变量线程安全的情况 */ public class T2 { @Test public void t2(){ MyThread2 m1 = new MyThread2("1"); MyThread2 m2 = new MyThread2("2"); MyThread2 m3 = new MyThread2("3"); m1.start(); m2.start(); m3.start(); } } class MyThread2 extends Thread { private int count = 5; MyThread2(String name) { // super(); //给父类的name属性赋值 this.setName(name); } //每次new MyThread2只会执行一次run(); @Override public void run() { super.run(); while (count>0){ System.out.println("由 线程 " + MyThread2.currentThread().getName() + " 计算,剩余count=" + count); count--; } } } /* 由 线程 1 计算,剩余count=5 由 线程 1 计算,剩余count=4 由 线程 1 计算,剩余count=3 由 线程 1 计算,剩余count=2 由 线程 1 计算,剩余count=1 由 线程 3 计算,剩余count=5 由 线程 2 计算,剩余count=5 由 线程 3 计算,剩余count=4 由 线程 3 计算,剩余count=3 由 线程 3 计算,剩余count=2 由 线程 3 计算,剩余count=1 由 线程 2 计算,剩余count=4 由 线程 2 计算,剩余count=3 由 线程 2 计算,剩余count=2 由 线程 2 计算,剩余count=1 */ /* 每个线程都有自己的实例变量count ,互不影响 */
4.共享数据的情况 [使用 同步锁 可解决脏数据问题]
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; /** * 共享数据的情况 [使用 同步锁 可解决脏数据问题] */ public class T3 { @Test public void t3() { MyThread3 myThread3 = new MyThread3(); //参数分别是 自定义线程 ,线程名字 Thread m1 = new Thread(myThread3, "1"); Thread m2 = new Thread(myThread3, "2"); Thread m3 = new Thread(myThread3, "3"); Thread m4 = new Thread(myThread3, "4"); Thread m5 = new Thread(myThread3, "5"); m1.start(); m2.start(); m3.start(); m4.start(); m5.start(); } } //class MyThread3 extends Thread { // //公共数据 // private int count = 5; // //每次new MyThread3只会执行一次run(); // @Override // public void run() { // super.run(); // int yu = count--; // System.out.println("由 线程 " + MyThread3.currentThread().getName() + " 计算,剩余count=" + yu); // // } //} /* 由 线程 3 计算,剩余count=2 由 线程 5 计算,剩余count=3 由 线程 1 计算,剩余count=5 由 线程 4 计算,剩余count=1 由 线程 2 计算,剩余count=4 */ /* 本应该剩余count是依次递减的 ,可是显示是乱数据, 因为在大多数jvm中,count–-的操作分为如下三步: 取得原有count值 计算 -1 的结果 进行赋值 并发线程获取count的值显然是无法避免脏数据 */ //使用锁 synchronized,即可解决线程脏数据 class MyThread3 extends Thread { //公共数据 private int count = 5; //每次new MyThread3只会执行一次run(); //添加了同步锁 synchronized @Override public synchronized void run() { super.run(); int yu = count--; System.out.println("由 线程 " + MyThread3.currentThread().getName() + " 计算,剩余count=" + yu); } } /* 由 线程 1 计算,剩余count=5 由 线程 5 计算,剩余count=4 由 线程 4 计算,剩余count=3 由 线程 3 计算,剩余count=2 由 线程 2 计算,剩余count=1 */
5.使用 interrupt() 和 return 配合 停止线程
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; /** * 使用 interrupt() 和 return 配合 停止线程 */ public class T4 { @Test public void t() throws InterruptedException { MyThread4 m = new MyThread4(); m.start(); //主线程休眠0.01秒后执行 Thread.sleep(10); m.interrupt(); Thread.sleep(1000); System.out.println("停了么?"); } } class MyThread4 extends Thread { //每次new MyThread4只会执行一次run(); @Override public void run() { super.run(); int i = 0; while (true) { //判断该线程是否被执行了interrupt() if (this.isInterrupted()) { System.out.println("线程被停止"); return; } System.out.println(i++); } } }
打印结果:
6.线程优先级对比
setPriority不一定起作用的,在不同的操作系统不同的jvm上,效果也可能不同
数字越大优先级越高,但不是优先级低就一定在优先级高的线程后面运行,而是运行的机率会低一些,,反之亦然。
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; /** * 线程优先级对比 * setPriority不一定起作用的,在不同的操作系统不同的jvm上,效果也可能不同 * 数字越大优先级越高,但不是优先级低就一定在优先级高的线程后面运行,而是运行的机率会低一些,,反之亦然。 * */ public class T5 { @Test public void t() { MyThread5 a = new MyThread5("A"); MyThread5 b = new MyThread5("B"); // a.setPriority(10); // b.setPriority(1); a.setPriority(1); b.setPriority(10); a.start(); b.start(); } } class MyThread5 extends Thread { MyThread5(String name) { super(); this.setName(name); } @Override public void run() { super.run(); System.out.println("我是线程:" + Thread.currentThread().getName()); } }
7.守护线程 ,当 用户线程 结束后,守护线程跟着结束
注意:
不可使用@Test测试 ,否则所有线程都会随着用户线程结束而结束,看不到非守护线程运行效果
package com.example.javabaisc.myThread; import org.junit.jupiter.api.Test; /** * 守护线程 ,当 用户线程 结束后,守护线程跟着结束 * * 不可使用@Test测试 ,否则所有线程都会随着用户线程结束而结束,看不到非守护线程运行效果 * * */ public class T6 { public static void main(String[] args) throws InterruptedException { MyThread6 a = new MyThread6(); //设置该线程为守护线程 a.setDaemon(true); a.start(); Thread.sleep(1000); System.out.println("用户线程结束,退出,守护线程也跟着退出"); } } class MyThread6 extends Thread { private int i = 0; @Override public void run() { while (true) { System.out.println(i++); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }
不设为守护线程的打印结果
设为守护线程的打印结果