Java多线程

目录

一、什么是多线程?

二、多线程的实现方式

1、继承Thread,从写run方法

2、实现Runnable接口

3、实现Callable接口:带返回值

4、多线程常用方法

5、线程运行周期

三、线程池

线程池代码实现


 

一、什么是多线程?

多线程指的是在一个进程中同时执行多个线程的编程技术。每个线程可以独立执行不同的任务,共享进程的内存空间和系统资源。多线程可以提高程序的并发性和效率,因为在多线程程序中,多个线程可以同时执行不同的任务,从而提高程序的处理能力。

多线程的应用场景非常广泛,例如在Web服务器中,多线程可以同时处理多个客户端请求,提高服务器的并发处理能力;在图形界面程序中,多线程可以使程序界面更加流畅,避免因为一个任务的阻塞而导致整个程序的停顿。

在多线程编程中,需要注意线程安全和资源共享的问题,避免因为多个线程同时访问和修改同一份数据而导致的数据不一致或者程序崩溃的问题。同时,需要注意线程的调度和优先级问题,避免因为线程优先级不当而导致的任务饥饿和死锁问题。

多线程的作用:

多线程的主要作用是提高程序的并发性和效率,可以同时执行多个任务,从而提高程序的处理能力。

什么是进程什么是线程?

进程是计算机中正在运行的程序的实例。它包括了程序的代码、数据、内存空间、打开的文件和网络连接等资源。每个进程都有自己的独立内存空间和系统资源,进程之间是相互独立的。

线程是进程内的一个执行单元。一个进程可以包含多个线程,每个线程可以独立执行不同的任务。线程共享进程的内存空间和系统资源,因此线程之间可以相互访问和修改同一份数据。线程的创建和销毁比进程更加轻量级,因此线程的切换和调度开销比进程更小。

什么是并行什么是并发?

并行是指在同一时刻,多个任务同时进行,各自独立且互不影响。例如,多个线程同时执行不同的任务。

并发是指在同一时间段内,多个任务交替进行,通过快速切换的方式来实现同时进行的效果。例如,多个进程在同一台计算机上运行,每个进程交替执行,看起来就像是同时进行。

二、多线程的实现方式

1、继承Thread,从写run方法

public class MyThread extends Thread{

    /**
     * 继承Thread,从写run方法
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"helloWorld");
        }
    }

    public static void main(String[] args) {

        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        //设置线程名字
        thread1.setName("线程1:");
        thread2.setName("线程2:");
        thread1.start();
        thread2.start();
    }
}

2、实现Runnable接口

public class MyRunnable implements Runnable{

    /**
     * 实现Runnable接口
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            //获取当前线程对象
            Thread thread = Thread.currentThread();

            System.out.println(thread.getName() + "helloWorld");
        }
    }
}
public class RunnableTest {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread1.setName("线程1:");
        thread2.setName("线程2:");

        thread1.start();
        thread2.start();
    }
}

3、实现Callable接口:带返回值

public class MyCallable implements Callable {

    /**
     * 实现Callable接口
     * @return
     * @throws Exception
     */
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < 10; i++) {
            sum = sum + i;
        }
        return sum;
    }
}
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建MyCallable的对象(表示多线程要执行的任务〉(表示线程
        MyCallable myCallable1 = new MyCallable();
        //创建FutureTask的对象(作用管理多线程运行的结果
        FutureTask futureTask1 = new FutureTask<>(myCallable1);

        //创建Thread类的对象,并启动
        Thread thread1 = new Thread(futureTask1);
        Thread thread2 = new Thread(futureTask1);

        thread1.setName("线程1:");
        thread2.setName("线程2:");
        //启动线程
        thread1.start();
        thread2.start();
        //
        Integer integer1 = futureTask1.get();
        Integer integer2 = futureTask1.get();
        System.out.println(thread1.getName() + thread1.getId() +":"+ integer1);
        System.out.println(thread2.getName() + thread2.getId() +":" + integer2);
    }
}

4、多线程常用方法

方法名称 说明
string getName() 返回此线程的名称
void setName(string name) 设置线程的名字(构造方法也可以设置名字)
static Thread currentThread() 获取当前线程的对象
static void sleep( long time) 让线程休眠指定的时间,单位为毫秒
setPriority(int newPriority) 设置线程的优先级
final int getPriority() 获取线程的优先级
final void setDaemon(boolean on) 设置为守护线程
public static void yield() 出让线程/礼让线程
public static void join() 插入线程/插队线程
public class ThreadMethod extends Thread{
    //有参构造可以设置线程名称
    public ThreadMethod(String name) {
        super(name);
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+"helloWorld");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //创建对象
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        //设置线程名字
        thread1.setName("线程1:");
        thread2.setName("线程2:");
        
        //获取线程名字
        String name = thread1.getName();
        System.out.println(name);
        //获取当前线程对象
        Thread thread = Thread.currentThread();
        System.out.println(thread);
        //设置线程优先级
        thread1.setPriority(5);
        //获取线程优先级
        int priority = thread1.getPriority();
        System.out.println(priority);
        //线程休眠1秒钟
        Thread.sleep(1000);
        //开启线程
        thread1.start();
        thread2.start();
    }
}

5、线程运行周期

新建---就绪---运行---阻塞---死亡

Java多线程_第1张图片

线程的安全问题

指当多个线程同时访问共享资源时,可能会出现数据不一致、数据丢失、死锁等问题。

同步代码块

把操作共享数据的代码锁起来

格式:

synchronized(锁){
    操作共享数据的代码
}

特点:

1、 锁默认打开,有一个线程进去了,锁自动关闭
2、里面的代码全部执行完毕,线程出来,锁自动打开

示例:

public class StaticThread extends Thread{
    //共享数据
    static int number = 0;
    //锁对象,一定要保证唯一
    static Object object = new Object();

    @Override
    public void run() {
        while (true){
            synchronized (object){
                if (number < 100){
                   try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    number++;
                    System.out.println(getName() + "增在买第" + number + "张票");
                }else {
                    break;
                }
            }
        }
    }

    public static void main(String[] args) {
        //创建线程对象
        StaticThread thread1 = new StaticThread();
        StaticThread thread2 = new StaticThread();
        StaticThread thread3 = new StaticThread();
        //起名字
        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        //开启线程
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

同步方法:

就是把synchronized关键字加到方法上

格式

修饰符synchronized返回值类型方法名(方法参数){...}
public class MyRunable implements Runnable{
    int number = 0;
    @Override
    public void run() {
        while (true){
            if (method()){
                break;
            }
        }
    }
    private synchronized boolean method(){
        if (number <100){
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            number++;
            System.out.println(Thread.currentThread().getName() + "正在卖第" + number + "张票");
        }else {
            return true;
        }
        return false;
    }
}
public class ThreadDemo {
    public static void main(String[] args) {
        MyRunable myRunable = new MyRunable();

        Thread thread1 = new Thread(myRunable);
        Thread thread2 = new Thread(myRunable);
        Thread thread3 = new Thread(myRunable);

        thread1.setName("窗口1");
        thread2.setName("窗口2");
        thread3.setName("窗口3");

        thread1.start();
        thread2.start();
        thread3.start();
    }
}

死锁

死锁是指两个或多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法继续执行下去。

死锁产生的原因通常是两个或多个线程相互持有对方需要的资源,同时又不释放自己持有的资源,导致两个或多个线程都无法继续执行下去,从而形成死锁。

线程等待

方法名称 说明
void wait() 当前线程等待,直到被其他线程唤醒
void notify() 随机唤醒单个线程
void notifyAll() 唤醒所有线程


三、线程池

线程池是一种常见的线程管理机制,它可以在程序启动时创建一定数量的线程,并将它们放入线程池中,以便在需要时重复使用。线程池可以有效地控制线程的数量,避免线程频繁创建和销毁的开销,提高程序的性能和稳定性。

Java中的线程池主要由以下几个组件组成:

  1. 线程池管理器:用于创建和管理线程池,包括创建线程池、提交任务、销毁线程池等操作。

  2. 工作线程:线程池中的线程,用于执行任务。

  3. 任务队列:用于存放等待执行的任务,通常采用阻塞队列实现。

  4. 任务接口:用于定义需要执行的任务,通常采用Runnable或Callable接口。

核心原理

1、创建一个池子,池子中是空的
2、提交任务时,池子会创建新的线程对象,任务执行完毕,线程归还给池子下回再次提交任务时,不需要创建新的线程,直接复用已有的线程即可
3、但是如果提交任务时,池子中没有空闲线程,也无法创建新的线程,任务就会排队等待

线程池代码实现

Executors:线程池的工具类通过调用方法返回不同类型的线程池对象。

方法名称 说明

public static Executorservice newCachedThreadPool()

创建一个没有上限的线程池
public static ExecutorService newFixedThreadPool(int nThreads) 创建有上限的线程池

方式1

public class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + "------" + i);
        }
    }
}
public class ExecutorsTest {
    public static void main(String[] args) {
        //获取线程池对象
        ExecutorService pool = Executors.newCachedThreadPool();

        //提交人物
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //销毁线程池
        //pool.shutdown();

    }
}

方式2

public class ExecutorsTest {
    public static void main(String[] args) {
        //获取线程池对象
        ExecutorService pool = Executors.newFixedThreadPool(3);

        //提交人物
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());
        pool.submit(new MyRunnable());

        //销毁线程池
        //pool.shutdown();

    }
}

自定义线程池

public class CustomThread {
    public static void main(String[] args) {

        ThreadPoolExecutor pool = new ThreadPoolExecutor(
                3,//核心线程数量,能小于0
                6,//最大线程数,不能小于0,最大数量>=核心线程数量
                60,//空闲线程最大存活时间
                TimeUnit.SECONDS,//时间单位
                new ArrayBlockingQueue<>(3),//任务队列
                Executors.defaultThreadFactory(),//创建线程工厂
                new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略

        );
        //提交任务
        pool.submit(new MyRunnable());
    }
}
任务拒绝策略 说明
ThreadPoolExecutor.AbortPolicy
 
默认策略:丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy 丢弃任务,但是不抛出异常这是不推荐的做法
ThreadPoolExecutor.Discard0ldestPolicy 抛弃队列中等待最久的任务然后把当前任务加入队列中
ThreadPoolExecutor.callerRunsPolicy 调用任务的run()方法绕过线程池直接执行

不断的提交任务,会有以下三个临界点:
1、当核心线程满时,再提交任务就会排队

2、当核心线程满,队伍满时,会创建临时线程

3、当核心线程满,队伍满,临时线程满时,会触发任务拒绝策略





 

你可能感兴趣的:(#,Java,java,jvm,开发语言)