栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

「不得不看」线程的那些知识

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

「不得不看」线程的那些知识

线程 1.进程和线程

(1)进程:即正在运行的程序,是操作系统进行资源分配的基本单位。单核CPU情况下,同一时刻只能运行一个进程;宏观上并行,微观上串行。
(2)线程:又称为轻量级进程,是CPU的基本调度单位。
(3)二者的区别:

  • 进程是操作系统进行资源分配的基本单位,线程是CPU调度的基本单位。
  • 程序是静态的,一般表现为一个或一组文件,进程是动态的,是操作系统内部进行资源申请的最小个体。
  • 进程间无法进行数据共享,但是线程之间可以。
2.线程的组成
  • CPU时间片:操作系统会为每个线程分配执行时间。
  • 运行数据:
    堆空间:存储引用类型数据,多个线程可以共享;
    栈空间:存储局部变量,每个线程都有独立的栈空间。
3.线程的创建
  • 继承Thread类
//方式一:继承 Thread,重写run() 方法
public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i <50 ; i++) {
            System.out.println("子线程正在执行==="+i);
        }
    }
}
--------------------------------------------------------------------
测试:
public class TestThread {
    public static void main(String[] args) {
        //创建线程对象
        MyThread t1=new MyThread();
        //启动线程
        t1.start();
        for (int i = 0; i <50 ; i++) {
            System.out.println("主线程正在执行==="+i);
        }
    }
}
  • 实现Runnable接口
//方式二:实现Runnable接口,重写 run() 方法
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i=1;i<=50;i++){
            System.out.println("子线程正在执行==="+i);
        }
    }
}
--------------------------------------------------------------------
测试:
public class TestRunnable {
    public static void main(String[] args) {
        //1.创建MyRunnable对象,表示线程要执行的功能
        MyRunnable myRunnable =new MyRunnable();
        //2.创建线程对象
        Thread t1=new Thread(myRunnable,"我的线程");
        //3.启动线程
        t1.start();
        //main线程
        for (int i = 1; i <=50 ; i++) {
            System.out.println("主线程正在执行==="+i);
        }
    }
}
4.线程的静态方法
  • 线程休眠
public class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <=10 ; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
            try {
                //线程休眠1000ms
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 线程join
public class SleepThread extends Thread{
    @Override
    public void run() {
        for (int i = 1; i <=5 ; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
            try {
                //线程休眠1000ms
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
--------------------------------------------------------------------
测试:
public class TestJoin {
    public static void main(String[] args) throws InterruptedException {
        JoinThread j1 =new JoinThread();
        j1.start();
        //join()方法会阻塞主线程,直至 j1 执行完毕,再执行main线程
        j1.join();
        //主线程
        for (int i = 1; i <=5; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
        }
    }
}
---------------------------------------------------------------------
运行结果:
Thread-0---1
Thread-0---2
Thread-0---3
Thread-0---4
Thread-0---5
main---1
main---2
main---3
main---4
main---5
5.守护线程

线程分为两类:用户线程(前台线程)、守护线程(后台线程),如果程序中所有的前台线程都执行完毕了,后台线程会自动结束。(Java中的垃圾回回收器就i属于守护线程)

  • 示例代码-
public class DaemonThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+"---"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
------------------------------------------------------------------
//测试
public class Test {
    public static void main(String[] args) throws InterruptedException {
        //创建线程,默认情况下是前台线程
        DaemonThread t1=new DaemonThread();
        //设置为守护线程
        t1.setDaemon(true);
        //启动线程
        t1.start();
        //主线程
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程"+"---"+i);
            Thread.sleep(500);
        }
    }
}

执行结果:

6.系统

(1)并行和并发:

  • 并行:进程“真”同时在执行,微观角度的同一时刻,有多个指令在执行,只会出现在多CPU多核的场景下。
  • 并发:进程“假”同时执行,微观上,表现为一次执行一个进程,但宏观上,多个进程在“同时”执行。

(2)用户态和内核态

  • CPU正在执行的是OS的指令时,就进入到内核态。反之,执行的是普通进程的指令时,就是用户态。
  • 内核态的指令权限高,所有硬件资源都能访问,用户态的指令权限低,只能访问OS规定的资源。通过系统调用,切换用户态和内核态。
  • 用户态的性能较好,内核态的性能较差。

(3)进程状态的转移


进程各状态之间转移的条件:

  • 初始状态->就绪状态:进程的初始化工作完成,由操作系统的指令完成。
  • 就绪状态->运行状态:进程被OS选中,分配CPU资源后;
  • 运行状态->结束状态:进程的最后一条指令执行结束;
  • 运行状态->就绪状态:
    1.CPU资源被优先级更高的进程抢占;
    2.时间片耗尽;
    3.进程执行操作系统提供的系统调用,主动放弃;
  • 运行状态->阻塞状态:
    1.等待一些外部条件,如IO设备的输入等;
    2.进程进入休眠状态;
    …
  • 阻塞状态->就绪状态:外部条件满足时,IO数据输入;休眠时间到了等
6.线程的安全问题*

(1)同步代码块

**
 * 同步代码块解决线程安全
 * 保证线程的原子性
 */
public class Test {
    private static int index=0;
    public static void main(String[] args) throws InterruptedException {
        String[] s =new String[5];

        //创建线程
        Runnable runnableA =new Runnable() {
            @Override
            public void run() {
                //对临界资源上锁,保证原子性
               synchronized (s){
                   s[index]="hello";
                   index++;
               }
            }
        };

        Runnable runnableB=new Runnable() {
            @Override
            public void run() {
               synchronized (s){
                   s[index]="world";
                   index++;
               }
            }
        };

        Thread t1 =new Thread(runnableA,"A");
        Thread t2 =new Thread(runnableB,"B");
        t1.start();
        t2.start();

        //保证两个线程都执行完毕
        t1.join();
        t2.join();

        System.out.println(Arrays.toString(s));
    }
}

(2)同步方法

public class TicketThread implements Runnable{
   private int ticket=100;
   //创建一个锁
   private Object object =new Object();
    @Override
    public void run() {
        while (true){
            if ( !sale()){
                break;
            }
        }
    }
    //非静态方法
    //当前锁 this
    public  synchronized boolean sale(){
        if (ticket<=0){
            return false;
        }
        System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
        ticket--;
        return true;
    }
	//静态方法
	//当前锁 类对象 TicketThread.class
 	public static boolean sale(){
       synchronized(TicketThread.class){
       if (ticket<=0){
            return false;
        }
        System.out.println(Thread.currentThread().getName()+"卖了第"+ticket+"张票");
        ticket--;
        return true;
       } 
    }
}
7.多线程的下对[0-40000000]数组排序案例
  1. 产生随机数组
import java.util.Random;

public class ArrayHelper {
    
    public static  long[]  generateArray(int n){
        Random random =new Random(20220620);
        long[] array =new long[n];
        for (int i = 0; i < n; i++) {
            array[i] =random.nextInt();
        }
        return array;
    }
}
  1. 单线程下对[0-40000000]排序
public class SingleSort {
    public static void main(String[] args) {

        //计时
        long start =System.currentTimeMillis();
        //生成一个40000000的数组
        long[] array =ArrayHelper.generateArray(4_000_0000);

        //[0,1000_0000]
        Arrays.sort(array,0,1000_0000);

        //[1000_0001,2000_0000]
        Arrays.sort(array,1000_0001,2000_0000);

        //[2000_0001,3000_0000]
        Arrays.sort(array,2000_0001,3000_0000);

        //[3000_0001,4000_0000]
        Arrays.sort(array,3000_0001,4000_0000);

        long end =System.currentTimeMillis();
        long elapsed =end-start;
        System.out.println(elapsed+"ms");
    }
    }

排序耗时:4786ms

  1. 多线程下对[0-40000000]排序
import java.util.Arrays;


public class ConcurrentSort {

    //排序的线程
    static class SortWork  extends Thread{
        private  long[] array;
        private  int fromIndex;
        private  int toIndex;
        public SortWork(long[] array,int fromIndex,int toIndex){
            this.array =array;
            this.fromIndex =fromIndex;
            this.toIndex =toIndex;
        }

        //排序
        @Override
        public void run() {
            //左闭右开区间上排序
            Arrays.sort(array,fromIndex,toIndex);
        }
    }

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

        //计时
        long start =System.currentTimeMillis();
        //生成一个40000000的数组
        long[] array =ArrayHelper.generateArray(4_000_0000);

        //四个线程进行同时排序
        //[0,1000_0000]
        Thread t1 =new SortWork(array,0,1000_0000);
        t1.start();
        //[1000_0001,2000_0000]
        Thread t2 =new SortWork(array,1000_0001,2000_0000);
        t2.start();
        //[2000_0001,3000_0000]
        Thread t3 =new SortWork(array,2000_0001,3000_0000);
        t3.start();
        //[3000_0001,4000_0000]
        Thread t4 =new SortWork(array,3000_0001,4000_0000);
        t4.start();

        t1.join();
        t2.join();
        t3.join();
        t4.join();
        long end =System.currentTimeMillis();
        long elapsed =end-start;
        System.out.println(elapsed+"ms");
    }
}

排序耗时:2684ms

总结,虽然多线程下程序的执行效率要比单线程的执行效率要高,但并带代表线程越多越好,其一,创建线程本身也需要消耗资源,线程数越多,它消耗的cpu资源也越多,其二,线程调度也需要耗时,OS从100个线程中挑一个的时间和从10000个线程中挑一个的时间也是不同的。最后,cpu是公共资源,在设计程序时,也应想到OS中的其他程序。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/993407.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号