java 线程 总结
阅读原文时间:2023年07月08日阅读:4

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();  
        }

    }

}  

}

不设为守护线程的打印结果

设为守护线程的打印结果