多线程

本文围绕Java多线程展开,介绍了线程相关概念,如程序与进程、进程与线程、并行与并发。阐述了多线程的实现方式,包括继承和实现方式及其区别。还讲解了Thread常用方法、线程同步问题及解决办法,最后介绍了线程池技术,以解决频繁生成和销毁线程的效率问题。

多线程相关概念

1、线程三组概念
(1)程序与进程
程序:一组运行逻辑和数据的集合,是一个静态概念,一般存储在磁盘中。
进程:一个正在运行的程序,程序一次运行,是一个动态概念,一般加载在内存中。
(2)进程与线程
进程:正在运行的程序,该程序独立占用资源,是资源分配的一个单位。
线程:独立运行任务的路径。多线程:程序运行过程中,需要多个子任务协同执行,每个线程完成一个任务,任务之间不会相互依赖,独立执行。
进程和线程关系:一个进程最少由1个线程组成,一个进程中多个线程共享进程分配的资源。
在这里插入图片描述
(3)并行与并发
并行:多个线程同时执行,需要多核CPU,多个线程同时获取CPU资源,共同执行。
并发:多个线程同时发起,如单核CPU,在同一个CPU时间片中,只能有一个线程获取到CPU资源,其他线程只能等待获取。在多任务系统中,由于CPU分配给线程时间偏非常短,且CPU运行效率非常高,用户感觉是多个任务同时执行。
在这里插入图片描述

多线程的实现方式

1、继承方式实现


class MyThread extends Thread {

    @Override
    public void run() {
        Demo1_多线程实现方式.method();
    }
}

public class Demo1_多线程实现方式 {

    public static void method() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
    }

    public static void main(String[] args) {
        //method();
        //创建子线程
        MyThread th = new MyThread();
        //启动子线程
        th.start();

        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "...");
        }
    }

    //说明:Demo1_多线程实现方式是一个程序,当其运行时就变成了进程。
    //该进程为该程序分配了资源(CPU,内存),由于该程序具有两个子任务
    //问题:通过执行该程序,发现并没有子线程并发或并行两个子任务。
    //分析:该程序还是单线程程序,没有开启多线程服务。
    //问题:如何开启多线程服务呢?继承方式/实现方式
}

在这里插入图片描述
2、实现方式实现

class MyRunnable implements Runnable{

    @Override
    public void run() {
        Demo1_多线程实现方式.method();
    }
}


public class Demo1_多线程实现方式 {

    public static void method() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
    }

    public static void main(String[] args) {
        //实现方式开启子线程
        //创建任务类
        MyRunnable ru =  new MyRunnable();//任务类
        //创建线程类
        Thread th = new Thread(ru);
        //开启子线程
        th.start();



        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "...");
        }
    }

    //说明:Demo1_多线程实现方式是一个程序,当其运行时就变成了进程。
    //该进程为该程序分配了资源(CPU,内存),由于该程序具有两个子任务
    //问题:通过执行该程序,发现并没有子线程并发或并行两个子任务。
    //分析:该程序还是单线程程序,没有开启多线程服务。
    //问题:如何开启多线程服务呢?继承方式/实现方式
}


在这里插入图片描述
3、继承和实现多线程区别
(1)继承方式比实现方式代码更加简洁
(2)继承方式只能单继承名,实现方式是多实现,所以灵活性和可扩展性实现方式更强。
4、其他实现多线程方式

public class Demo2_其他多线程实现方式 {

    public static void method() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "...");
        }
    }

    public static void main(String[] args) {

        //其他实现方式(继承内部类)
        Thread th = new Thread(){
            @Override
            public void run() {
                Demo2_其他多线程实现方式.method();
            }
        };
        th.start();
        
        //(实现内部类)
        Thread th2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Demo2_其他多线程实现方式.method();
            }
        });
        th2.start();

        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
    }
}

Thread常用方法

1、获取/设置线程名称
在这里插入图片描述
在这里插入图片描述

public class Demo3_常用方法 {
    public static void main(String[] args) {

        //通过getName()获取线程名称

        Thread th = new Thread("ConsThread") {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    System.out.println(i + "...");
                }
                System.out.println("在任务内获取子线程名称:"+this.getName());;
            }
        };
        //setName(String) 设置子线程名称
        //th.setName("myThread");
        th.start();
        //getName() 获取子线程名称
        System.out.println("在任务外获取子线程名称:" + th.getName());//Thread-0
        //结论:子线程具有默认名称,名称格式:Thread-数字(子线程下标,以0开始算起)


        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
    }
}

2、获取执行任务的线程对象
在这里插入图片描述

class Test{
    @Override
    protected void finalize() throws Throwable {
        System.out.println("执行垃圾回收的线程名称:" + Thread.currentThread().getName());//Finalizer
        super.finalize();
    }
}

public class Demo3_常用方法 {
    public static void main(String[] args) {

        Test test = new Test();
        test = null;
        System.gc();

        System.out.println("测试垃圾回收线程...");

        //method2();
        //method1();
    }

    private static void method2() {
        Thread th = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    System.out.println(i + "...");
                }
                //this.getName();报错
                //原因:由于匿名内部类实现Runnable接口,所以该类是一个任务类,任务类没有getName()方法。
                System.out.println("任务内获取子线程名称:" + Thread.currentThread().getName());//Thread-0s
            }
        });
        th.start();
        System.out.println("任务外获取子线程名称:" + th.getName());//Thread-0


        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
        //面试题1:main方法被哪个线程执行呢?
        System.out.println("获取执行mian方法的线程名称:" + Thread.currentThread().getName());//main
        //面试题2:对象垃圾回收机制,回收垃圾对象的线程是什么线程?
    }
}

3、线程休眠
在这里插入图片描述

public class Demo3_常用方法 {
    public static void main(String[] args) {

        //休眠方法
        Thread th = new Thread() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    System.out.println(i + "...");
                    try {
                        //.sleep(long) 使执行该方法的线程进入多少毫秒休眠状态
                        //注意1:使用该方法时必须进行异常处理。
                        //注意2:被休眠线程,会释放占用资源,但还保留争取资源的权利
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        th.start();

        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
        }
   }
}

4、守护线程
在这里插入图片描述

public class Demo3_常用方法 {
    public static void main(String[] args) {
        //守护线程:子线程保存父线程,如果父线程中断,该子线程也会同时中断。这子线程就是守护线程。
        Thread th = new Thread() {
            @Override
            public void run() {
                for (int i = 1; i <= 100; i++) {
                    System.out.println(i + "...");
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        //setDaemon(boolean),参数为true,则该线程被设置守护线程。
        th.setDaemon(true);
        //isDaemon() 判断是否是守护线程,如果是返回true,否则返回false
        System.out.println("子线程是否是守护线程:" + th.isDaemon());//false
        //应用:软件后台更新线程,是守护线程。
        
        th.start();

        for (int i = 1; i <= 100; i++) {
            System.out.println(i + "%%%");
            int i1 = i / 0;//程序中断
            //注意:异常出现后,只会影响该异常出现的线程中断,其他子线程不会收到影响。
        }
}
}

线程同步问题

1、同步代码块
概念:
多线程的程序,线程1未完成自己的任务便释放了资源,线程2后去该资源后执行其任务,导致结果出现错误,该错误就是由于线程安全问题。
2、同步代码块
synchronized (锁对象) {
需要同步的代码
}
synchronized 是关键字,表示同步
锁对象:用于管理线程,线程获取锁对象后才能进入同步代码块,否则已在外等候,直到其他线程释放锁对象,它获取才能进入。

import java.io.InputStream;

public class Demo4_线程同步问题 {

    public static void main(String[] args) {

        //锁对象:可以是任意对象
        Object obj = new Object();
        //InputStream obj = System.in;

        Thread th = new Thread(){
            @Override
            public void run() {
                while(true){
                    synchronized (obj) {
                        System.out.println("中");
                        System.out.println("公");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("教");
                        System.out.println("育");
                    }
                }
            }
        };

        Thread th2 = new Thread(){
            @Override
            public void run() {
                while(true){
                    synchronized (obj) {
                        System.out.println("优");
                        System.out.println("就");
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("业");
                    }
                }
            }
        };

        th.start();
        th2.start();

        //线程同步问题的注意点
        //1、多线程的程序,才会出现线程安全问题。
        //2、线程安全问题体现在:线程1未完成自己的任务便释放了资源,线程2后去该资源后执行其任务,导致结果出现错误,该错误就是由于线程安全导致的。

        //如何解决同步问题呢?
        //1、同步代码块 2、同步函数

        //同步代码块:
        //定义位置:定义在函数(方法)中
        //语法:
        // synchronized (锁对象) {
        //     需要同步的代码
        // }
        //synchronized 是关键字,表示同步
        //锁对象:用于管理线程,线程获取锁对象后才能进入同步代码块,否则已在外等候,直到其他线程释放锁对象,它获取才能进入。
    }
}

2、同步函数(方法)
访问修饰符 synchronized 返回值类型 方法名(参数列表){
方法体
}
同步方法作用:对方法体内容添加锁,线程进入方法体便获取了锁,离开方法便释放锁。

import java.io.InputStream;

public class Demo4_线程同步问题 {

    public synchronized static void method(){
        System.out.println("中");
        System.out.println("公");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教");
        System.out.println("育");
    }

    public synchronized static void function(){
        System.out.println("优");
        System.out.println("就");
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("业");
    }

    public static void main(String[] args) {

        //锁对象:可以是任意对象
        Object obj = new Object();
        //InputStream obj = System.in;

        Thread th = new Thread(){
            @Override
            public void run() {
                while(true){
                    Demo4_线程同步问题.method();
                }
            }
            //属于th的同步方法
//            public synchronized void method(){
//                System.out.println("中");
//                System.out.println("公");
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println("教");
//                System.out.println("育");
//            }
        };

        Thread th2 = new Thread(){
            @Override
            public void run() {
                while(true){
                    Demo4_线程同步问题.function();
                }
            }
            //属于th2同步方法
//            public synchronized void method(){
//                System.out.println("优");
//                System.out.println("就");
//                try {
//                    Thread.sleep(100);
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//                System.out.println("业");
//            }
        };

        th.start();
        th2.start();

        //问题:加了同步方法,未能解决同步问题?
        //答案:两个同步方法的锁不一样。
        //注意:非静态同步方法的锁 是 对象本身。
        //     静态同步方法的锁 是类对象本身。
    }
}

3、同步在API中的应用
(1)StringBuilder和StringBuffer
(2)集合工具类中方法使用集合同步
在这里插入图片描述

import java.util.*;

public class Demo5_集合中的同步 {

    public static void main(String[] args) {
        Collection<String> col = new ArrayList();
        Collection<String> coll = Collections.synchronizedCollection(col);

        List<String> list = new ArrayList();
        List<String> list2 = Collections.synchronizedList(list);

        Set<String> set = new HashSet<>();
        Set<String> set2 = Collections.synchronizedSet(set);

        Map<String,String> map = new HashMap<>();
        Map<String, String> map2 = Collections.synchronizedMap(map);
    }
}

4、线程生命周期
(1)线程生命周期中的状态
在这里插入图片描述
(2)线程生命周期中状态的转换
在这里插入图片描述
在这里插入图片描述

public class Demo6_线程生命周期 {

    public static void main(String[] args) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                System.out.println("子线程线程状态为:" +this.getState());//RUNNABLE
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        System.out.println("子线程线程状态为:" + thread.getState());//NEW

        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("子线程线程状态为:" +thread.getState());//TIMED_WAITTING

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("子线程线程状态为:" +thread.getState());//TERMINATED

    }
}

线程池技术

1、概念:
用于解决频繁生成和销毁线程问题,在开发中子任务功能简单,但是需要线程来执行,线程执行完毕又要销毁,而该任务需要多个线程并发执行,从而导致频繁生成销毁线程,会大大降低软件运行效率。所以需要使用线程池技术解决。
2、运行原理:
创建线程池时,先初始化多个线程,接收到子任务派遣(无需生成)线程去执行,执行完毕后重新回收(无需销毁)线程。热昂线程等待下一次派遣。
3、代码示例

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class MyRunnable3 implements Runnable {

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(Thread.currentThread().getName() + "..." + i);//pool-1-thread-1
        }
    }
}

public class Demo7_线程池技术 {

    public static void main(String[] args) {

        MyRunnable3 mr1 = new MyRunnable3();
        MyRunnable3 mr2 = new MyRunnable3();
        MyRunnable3 mr3 = new MyRunnable3();
        MyRunnable3 mr4 = new MyRunnable3();

        /*生成带一个线程的线程池
        //.newSingleThreadExecutor() 生成带一个线程的线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        //.submit(Runnable) 接收并派遣线程执行该任务
        executorService.submit(mr);
        //.shutdown() 释放线程池资源
        executorService.shutdown();*/

        //自定义多个线程的线程池
        //.newFixedThreadPool(3) 自定义带3个线程的线程池
        ExecutorService executorService = Executors.newFixedThreadPool(3);
        executorService.submit(mr1);
        executorService.submit(mr2);
        executorService.submit(mr3);
        executorService.submit(mr4);
        executorService.shutdown();

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值