线程

发布时间 2023-12-27 15:05:50作者: 小fei鱼学java

线程

java并发编程,多线程编程,能实现线程中多个任务同时并发的执行

程序:指令和数据的集合,编写代码

进程:将编写的程序,计算机指令集合启动(应用程序eg:工厂)

线程:一个进程中可以包含多个线程任务,cpu对线程的执行是在线程之间进行切换执行。线程为进程的逻辑单位(线程:工厂中每条流水线的工人)

线程执行需要获得CPU设备,os系统会在多个线程之间进行CPU的轮流切换,由os进行调度

一个程序启动形成进程后,一个进程可以存在多个执行路径

1.线程的实现:方式一

package Thread;

/**
 * Thread类的对象表示JVM中的线程
 * 自定义线程类需要继承Thread类,自定义类对象才能变为线程
 */
public class DemoTest01 extends Thread{

    //重写run方法,该方法中封装线程任务,每个线程需要完成的事
    @Override
    public void run() {
        for (int i = 1; i <=10 ; i++) {
            //获得当前线程对像
            System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
        }

    }

    public static void main(String[] args) {
        //创建3个线程,jvm为每个线程起一个默认的名称
        DemoTest01 d1 = new DemoTest01();
        DemoTest01 d2 = new DemoTest01();
        DemoTest01 d3 = new DemoTest01();
        /*启动线程,实现线程让线程变为就绪状态,排队等待os分配cpu
        * 当cpu分配给某个线程后,jvm会自动调用run方法完成线程任务*/
        d1.start();
        d2.start();
        d3.start();

    }
}

2.线程的实现:方式二

package Thread;

public class ThreadDemoTest02 implements Runnable {
    /*创建Runnable接口,重写run方法封装线程任务
     * 该类的对象只是封装了线程的任务,并不是线程对象*/
    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.println(Thread.currentThread().getName() + "移动" + i + "步");

        }
    }

    public static void main(String[] args) {
//创建封装线程任务的对象
        ThreadDemoTest02 y = new ThreadDemoTest02();
        ThreadDemoTest02 m = new ThreadDemoTest02();
        ThreadDemoTest02 s = new ThreadDemoTest02();
        /*创建Thread对象,分配线程任务,线程对象和线程任务是分离的
         * 同时指定线程名字,线程名字默认是Thread--数字*/
        Thread t1 = new Thread(y, "join");
        Thread t2 = new Thread(m, "lisa");
        Thread t3 = new Thread(s, "tom");
        t1.start();
        t2.start();
        t3.start();

    }


}

3.线程池

池化技术:在高并发,小任务的环境,如果存在对象的频繁创建和销毁,增加了系统堆内存的io负担,影响执行效率。--需要使用池化技术。

池化技术:将需要频繁创建和销毁的对象,创建好后放入一个池容器中,用的时候从容器中获得该对象,用完之后不需要是释放该对象,继续归还到池容器中。

池化技术的指标属性

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize:线程池的核心线程数。即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。
maximumPoolSize:最大线程数。超过此数量,会触发拒绝策略 如果对象不够使用的时候,可以创建的对象的最大值
keepAliveTime:线程的存活时间。当线程池里的线程数大于corePoolSize时,如果等了keepAliveTime时长还没有任务可执行,则线程退出。(线程在不处理任务的情况下,处于空闲状态,而且存活线程的数量大于核心数量,会销毁掉大于核心数量的线程)
unit:指定keepAliveTime的单位。比如:秒:TimeUnit.SECONDS。
workQueue:一个阻塞队列,提交的任务将会被放到这个队列里。(如果当前需要处理的任务大于线程对象最大数量,需要将不能及时处理的线程任务进行排队保存,保存到队列中。)
threadFactory:线程工厂,用来创建线程,主要是为了给线程起名字,默认工厂的线程名字:pool-1-thread-3。
handler:拒绝策略,当线程池里线程被耗尽,且队列也满了的时候会调用。
默认拒绝策略为AbortPolicy。即:不执行此任务,而且抛出一个运行时异常

4.线程池的实现一

Exceutors接口:提供绑定线程任务的方法,所有线程池都实现该接口

​ ExecutorService接口:提供关闭线程池,获得某个线程执行后的Future结果

Executors工具类:创建线程池的工具类,提供静态方法更具需要创建不同线程池

package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadDemoTest03 {
    public static void main(String[] args) {
        //创建线程池对象,存在3个线程
        ExecutorService service = Executors.newFixedThreadPool(3);
        //给线程池对象中线程绑定线程任务
        service.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <=10 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
                }
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <=10 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
                }
            }
        });

        service.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <=10 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
                }
            }
        });

        //该任务的处理时前三个任务那个线程处理完,由哪个线程处理
        service.execute(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <=10 ; i++) {
                    System.out.println(Thread.currentThread().getName()+"射击第"+i+"颗子弹。。。");
                }
            }
        });
    }
}

#创建线程池对象存在3个线程
#给线程池对象中每一个绑定线程任务,如果只创建3个线程对象,而绑定4个线程任务,则最先执行完成的处理第四个线程任务
pool-1-thread-1射击第1颗子弹。。。
pool-1-thread-2射击第1颗子弹。。。
pool-1-thread-3射击第1颗子弹。。。
pool-1-thread-2射击第2颗子弹。。。
pool-1-thread-1射击第2颗子弹。。。
pool-1-thread-2射击第3颗子弹。。。
pool-1-thread-3射击第2颗子弹。。。
pool-1-thread-2射击第4颗子弹。。。
pool-1-thread-1射击第3颗子弹。。。
pool-1-thread-2射击第5颗子弹。。。
pool-1-thread-3射击第3颗子弹。。。
pool-1-thread-2射击第6颗子弹。。。
pool-1-thread-1射击第4颗子弹。。。
pool-1-thread-2射击第7颗子弹。。。
pool-1-thread-3射击第4颗子弹。。。
pool-1-thread-2射击第8颗子弹。。。
pool-1-thread-1射击第5颗子弹。。。
pool-1-thread-2射击第9颗子弹。。。
pool-1-thread-3射击第5颗子弹。。。
pool-1-thread-2射击第10颗子弹。。。(2是最先执行完成的)
pool-1-thread-1射击第6颗子弹。。。
pool-1-thread-3射击第6颗子弹。。。
pool-1-thread-1射击第7颗子弹。。。
pool-1-thread-3射击第7颗子弹。。。
pool-1-thread-1射击第8颗子弹。。。
pool-1-thread-3射击第8颗子弹。。。
pool-1-thread-1射击第9颗子弹。。。
pool-1-thread-3射击第9颗子弹。。。
pool-1-thread-1射击第10颗子弹。。。
pool-1-thread-3射击第10颗子弹。。。
pool-1-thread-2射击第1颗子弹。。。
pool-1-thread-2射击第2颗子弹。。。
pool-1-thread-2射击第3颗子弹。。。
pool-1-thread-2射击第4颗子弹。。。
pool-1-thread-2射击第5颗子弹。。。
pool-1-thread-2射击第6颗子弹。。。
pool-1-thread-2射击第7颗子弹。。。
pool-1-thread-2射击第8颗子弹。。。
pool-1-thread-2射击第9颗子弹。。。
pool-1-thread-2射击第10颗子弹。。。

5.线程池的实现方式二

package Thread;

import java.util.concurrent.*;

public class ThreadDemoTest04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(3);
        //线程池绑定线程任务
        service.submit(new Runnable() {
            @Override
            public void run() {
                //重写run封装线程任务
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() +
                            "射击第" + i + "颗子弹、、、");
                }
            }
        });

        /*submit 第一个参数封装线程任务
         *         第二个参数封装线程任务执行后的返回值*/
        Future<Integer> future = service.submit(new Runnable() {
            @Override
            public void run() {
                //重写run封装线程任务
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() +
                            "射击第" + i + "颗子弹、、、");
                }
            }
        }, 8);
        Integer integer = future.get();
        System.out.println("integer" + integer);
        //绑定线程池任务
        Future<Integer> submit = service.submit(new Callable<Integer>() {

            @Override
            public Integer call() throws Exception {

                //重写run封装线程任务
                for (int i = 1; i <= 10; i++) {
                    System.out.println(Thread.currentThread().getName() +
                            "射击第" + i + "颗子弹、、、");
                }
                return 7;
            }
        });
        Integer integer1 = future.get();
        System.out.println("integer1" + integer1);
    }

}

pool-1-thread-1射击第1颗子弹、、、
pool-1-thread-2射击第1颗子弹、、、
pool-1-thread-1射击第2颗子弹、、、
pool-1-thread-2射击第2颗子弹、、、
pool-1-thread-1射击第3颗子弹、、、
pool-1-thread-2射击第3颗子弹、、、
pool-1-thread-1射击第4颗子弹、、、
pool-1-thread-2射击第4颗子弹、、、
pool-1-thread-1射击第5颗子弹、、、
pool-1-thread-2射击第5颗子弹、、、
pool-1-thread-1射击第6颗子弹、、、
pool-1-thread-2射击第6颗子弹、、、
pool-1-thread-1射击第7颗子弹、、、
pool-1-thread-2射击第7颗子弹、、、
pool-1-thread-1射击第8颗子弹、、、
pool-1-thread-2射击第8颗子弹、、、
pool-1-thread-1射击第9颗子弹、、、
pool-1-thread-2射击第9颗子弹、、、
pool-1-thread-1射击第10颗子弹、、、
pool-1-thread-2射击第10颗子弹、、、
integer8
integer18
pool-1-thread-3射击第1颗子弹、、、
pool-1-thread-3射击第2颗子弹、、、
pool-1-thread-3射击第3颗子弹、、、
pool-1-thread-3射击第4颗子弹、、、
pool-1-thread-3射击第5颗子弹、、、
pool-1-thread-3射击第6颗子弹、、、
pool-1-thread-3射击第7颗子弹、、、
pool-1-thread-3射击第8颗子弹、、、
pool-1-thread-3射击第9颗子弹、、、
pool-1-thread-3射击第10颗子弹、、、

Process finished with exit code -1

6.线程池的实现三

线程池实现定时任务和延迟任务:

延迟任务:在项目启动多长时间以后,执行某段代码

定时任务:在项目启动后,可以指定什么时间开始执行,指定间隔多长时间继续执行(频率)

package Thread;

import org.w3c.dom.ls.LSOutput;

import java.util.Date;
import java.util.concurrent.*;

public class ThreadPoolDemo04 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*定义任务--定时任务程序中某个代码在某个时刻启动,每隔多长时间执行一次
         * 延时任务--系统启动后延迟多少时间才执行某段代码*/
        //创建线程池对象
        ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
        //给线程池线程绑定线程任务
        service.schedule(new Runnable() {
            //封装线程任务
            @Override
            public void run() {
                System.out.println("---" + new Date());

            }
        }, 2, TimeUnit.SECONDS);
//主线程main方法中打印
        System.out.println("main:" + new Date());
//定时任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "--" + new Date());
            }
        }, 0, 1, TimeUnit.SECONDS);
        //主线程main方法打印
        System.out.println("main:" + new Date());

        //定时任务
        service.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"--"+new Date());
            }
        },0,1,TimeUnit.SECONDS);
        //给线程绑定线程任务,获得线程任务后结果
        ScheduledFuture<Integer> future = service.schedule(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println(Thread.currentThread().getName() + "--"+ new Date());
                return 100;
            }
        }, 5, TimeUnit.SECONDS);
        System.out.println("future="+future.get());
        //关闭线程池
        service.shutdown();
    }
}

main:Wed Dec 06 17:13:25 CST 2023
main:Wed Dec 06 17:13:25 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:25 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:25 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:26 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:26 CST 2023
---Wed Dec 06 17:13:27 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:27 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:27 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:28 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:28 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:29 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:29 CST 2023
pool-1-thread-3--Wed Dec 06 17:13:30 CST 2023
pool-1-thread-2--Wed Dec 06 17:13:30 CST 2023
pool-1-thread-1--Wed Dec 06 17:13:30 CST 2023
future=100

Process finished with exit code 0

7.线程的生命周期

WPS图片(1)

新建状态:当使用new关键字创建线程Thread类的对象,当前对象为线程的新建状态。

就绪状态:当通过某个线程对象调用了start方法,该线程处于就绪状态,等到os分配CPU。

​ 在没有得到CPU的情况下,进行排队,处于就绪队列中。

执行状态:就绪状态的线程在获得CPU的情况下,开始处理线程任务 ,执行run方法。

死亡状态:当执行状态的线程,处理完线程任务后,自动销毁。内存没有该线程对象。

阻塞状态:当前正在执行的线程,失去CPU资源(被动,主动,OS调度),处于阻塞状态

阻塞状态的线程可以直接获得CPU变为执行状态,也可以变为就绪状态

8.java线程通信

sleep();

join();

yield();

setDeamon();

setPriority();

9.线程安全问题

介绍:当多个线程访问共享资源(同一资源)的时候出现程序执行结果和预期结果不一致的情况--线程安全。

共享资源(临界资源)。

控制对共享资源按照业务规则,实现对共享资源访问的原子性,怎么实现共享资源访问的原子性--需要对访问共享资源的代码加锁。

所实现了计算机线程指令执行的原子性,保证了数据的安全。

会将程序执行的效率,并发性。是一种排他说,悲观锁。同时只能有一个线程获得锁。

10.synchronized实现代码枷锁

使用synchronized修饰方法

package Thread;

public class Ticket implements Runnable {
    /*实现售票,是每个窗口的任务,线程任务
    * 定义同步代码块
    * synchronized (JAVA对象)*/
    private static  int num = 100;
    private static Object obj = new Object();
    @Override
    public void run() {
        for (int i = 0; i <=100 ; i++) {
            synchronized (obj){//同步代码快
                if (num>0){//判断是否有余票
                    System.out.println(Thread.currentThread().getName()+
                            "卖出的票号:"+num+"的票");
                    num--;
                }
            }
        }
    }

    public static void main(String[] args) {
        //创建三个线程对象,表示三个窗口。封装线程任务
        Ticket t = new Ticket();
        for (int i = 0; i <=30 ; i++) {
            Thread t1 = new Thread(t);
            //启动线程
            t1.start();
        }
    }
}

定义同步代码块

package Thread;

public class Ticket implements Runnable {
    /*实现售票,是每个窗口的任务,线程任务
    * 定义同步代码块
    * synchronized (JAVA对象)*/
    private static  int num = 100;
    private static Object obj = new Object();
    @Override
    public void run() {
        for (int i = 0; i <=100 ; i++) {
            synchronized (obj){//同步代码快
                if (num>0){//判断是否有余票
                    System.out.println(Thread.currentThread().getName()+
                            "卖出的票号:"+num+"的票");
                    num--;
                }
            }
        }
    }

    public static void main(String[] args) {
        //创建三个线程对象,表示三个窗口。封装线程任务
        Ticket t = new Ticket();
        for (int i = 0; i <=30 ; i++) {
            Thread t1 = new Thread(t);
            //启动线程
            t1.start();
        }
    }
}

11.lock锁的使用

12. 生产者和消费模型

生产者和消费者模型--主要是先多个线程执行不同任务的同步。

比如:公交车司机和售票员--公交车司机没进站,停车。售票员不能开门。

公交车司机和售票员--售票员没有关门,公交车司机不能开出和出栈。

1702039869527

Producer:不能实现数据的重复生产,如果容器Buffer已经存在数据,Producer不能生产,直到Consumer消费完成数据。

Consumer:已经消费了,不能实现数据重复消费,消费者不能继续消费,直到Producer生产数据。

创建容器类

package Thread;

/**
 * 容器类
 */
public class Buffer {
    private int num = 0;//容器中保存的数据
    //true 有  false 表示没有
    private boolean flag = false;//表示容器中是否已经生成数据
    /*定义方法给容器中存放数据*/
    public synchronized  void  put(int num){
        try {
            if(flag){//已经存在保存的数据,不能生产
                //当前线程阻塞,生产者等待,直到其他线程调用notifyAll方法
                //this.wait();
            }
            //开始生产
            this.num = num;
            this.notifyAll();//唤醒消费者
            //将flag赋值为
            flag= true;
        }catch (Exception ex){
            ex.printStackTrace();
        }
    }
    //定义方法获得容器中的数据
    public synchronized  int pop(){
        try {
            if(!flag){
                //消费者等待,直到其他线程调用notifyAll方法
                this.wait();
            }
        }catch (InterruptedException e) {
            e.printStackTrace();
        }
        //开始消费
        flag =false;
        this.notifyAll();//唤醒生产者
        return this.num;
    }
}

声明容器属性

package Thread;

public class Producer  implements  Runnable{
    //声明容器属性对象
    private Buffer buffer;

    public Producer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 1; i <=10 ; i++) {
         //生产者实现生产任务
         buffer.put(i+100);
            System.out.println(Thread.currentThread().getName()+
                    "生产数据是:"+(i+100));
            //sleep阻塞
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

创建Conusmer类

package Thread;

public class Conusmer implements  Runnable {
    //声明容器属性对象
    private Buffer buffer;

    public Conusmer(Buffer buffer) {
        this.buffer = buffer;
    }

    @Override
    public void run() {
        for (int i = 1; i <=10 ; i++) {
            //生产者实现生产任务
            int pop = buffer.pop();
            System.out.println(Thread.currentThread().getName()+
                    "消费数据"+pop);
            //sleep阻塞
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
            }
        }
    }
}

创建ProducerConsumerTet类

package Thread;

public class ProducerConsumerTet {
    public static void main(String[] args) {
        //创建容器对象
        Buffer buffer = new Buffer();
        //创建封装生产者任务的线程
        Producer producer = new Producer(buffer);
        //创建消费者任务的线程
        Conusmer conusmer = new Conusmer(buffer);

        /*创建生产者和消费者线程*/
        Thread t1 = new Thread(producer);
        Thread t2 = new Thread(conusmer);

        t1.start();
        t2.start();
    }
}

13.线程总结

Java多线程编程,线程的实现方式

继承Thread类,该类子类的对象就是一个线程对象。

重写run方法,重写run方法指定线程任务

实现Runnable接口,重写run方法封装线程任务

创建Thread对象,传入Runnable接口实现,指定线程任务

线程池实现,创建多个Thread对象保存到一个集合中,

给线程池中某个线程池对象绑定线程任务,处理完成线程任务后,该线程对象不会销毁,

归还到线程池。建立程序在高并发,小任务环境中线程对象的频繁创建和销毁,减少对

内存的IO操作,提高程序执行的效率。

Executor接口:提供给线程池中线程绑定任务的方法

​ |--ExecutorService接口:

​ 绑定线程任务

​ Runnable接口绑定任务,方法没有返回值

​ Callable接口绑定任务,方法存在返回值,可以获得任务执行后的返回值关闭线程池

Executors工具类:提供静态方法,创建线程池

线程池核心指标:

​ 线程的核心数量

​ 线程的最大数量

​ 线程存活时间(当线程的最大数量>线程的核心数量,如果在一段时间内,线程没有参

​ 数任务的处理,可以将线程对象销毁)。

Java线程通信的方法

新建状态

就绪状态

运行状态

消亡状态

阻塞状态

sleep(2000);

join();

yield();

setPriority()

setDeamon()

Java线程安全问题

​ 当多个线程池并发访问共享资源,出现运行结果和预期结果不一致---出现线程安全问题。

解决线程安全问题--给访问共享资源的代码加锁,利用Java对象的锁。

实现代码执行的原子性---在计算机中某些代码语句不能拆分执行,必须看作一个整体执

行。使用synchronized加锁

synchronized 修饰方法,定义同步方法,利用的是调用该方法的this对象的锁,如果是static

方法,利用的是Class对象的锁。

synchronized修饰代码块,定义同步代码块,需要指定对象。

线程实现方式:

继承Thread

1 需要创建多个线程,就创建多个Thread类的子类对象,如果需要实现多个线程共享

资源,就必须将子类的属性修饰为静态的

​ 2 Java的继承是单根继承,所以为了合理利用继承资源,不建议使用继承Thread实现

实现Runable接口

1 实现线程任务和线程的分离,创建实现类的对象,只是创建了封装线程任务的对象。

2 创建Thread对象,指定线程任务,传入封装线程任务的对象

3 可以实现多个线程对资源的共享。

线程池

线程安全:

线程加锁,利用Java对象锁

加锁的目的是为了实现操作的原子性,锁是排他锁(悲观锁)。

synchronized 隐式锁,可以修饰同步方法,可以修饰同步代码块

lock 显式锁,需要集合try-finally,手动加锁和释放锁。