Java多线程

进程:进程是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例。程序运行时系统就会创建一个进程,并为它分配资源,然后把该进程放入进程就绪队列,进程调度器选中它的时候就会为它分配CPU时间,程序开始真正运行。

线程:是一条执行路径,是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。

一个正在运行的软件(如迅雷)就是一个进程,一个进程可以同时运行多个任务( 迅雷软件可以同时下载多个文件,每个下载任务就是一个线程), 可以简单的认为进程是线程的集合。

线程是一条可以执行的路径。

对于单核CPU而言:多线程就是一个CPU在来回的切换,在交替执行。
对于多核CPU而言:多线程就是同时有多条执行路径在同时(并行)执行,每个核执行一个线程,多个核就有可能是一块同时执行的。

进程与线程的关系:

  • 一个程序就是一个进程,而一个程序中的多个任务则被称为线程。进程是表示资源分配的基本单位,又是调度运行的基本单位。,亦即执行处理机调度的基本单位。 进程和线程的关系:

  • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。

  • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量,即每个线程都有自己的堆栈和局部变量。

  • 处理机分给线程,即真正在处理机上运行的是线程。

  • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。

为什么要使用多线程:

生活案例:加入学生A扫完1间教室需要1个小时,校长要求学生A一小时之内扫完四间教室,学生A该怎么办,此时学生A可以叫上学生BCD一起打扫教室,就可以一小时内扫完四间教室。

多线程的应用场景:

1.顺序编程:程序从上往下执行,第一行代码没有执行完,第二行只能等待第一行代码执行完才能继续执行。

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class MyThread extends Thread {
    public static void main(String[] args) throws InterruptedException {
        eat();
        sleep();
    }

    public static void eat() throws InterruptedException {
        System.out.println("我先吃早餐");
        Thread.sleep(5000);
        System.out.println("我再吃晚餐");
    }

    public static void sleep() throws InterruptedException {
        System.out.println("我先睡一觉");
        Thread.sleep(5000);
        System.out.println("我醒了");
    }
}

并发编程

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


import java.util.Date;

public class Test {
    public static void main(String[] args){
        new Eat().start();
        new Sleep().start();
    }
}
class Eat extends Thread {
    @Override
    public void run() {
        System.out.println("开始吃饭?...\t" + new Date());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束吃饭?...\t" + new Date());
    }
}
class Sleep extends Thread {
    @Override
    public void run() {
        System.out.println("开始睡觉?...\t" + new Date());
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("结束睡觉?...\t" + new Date());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KHFomVzt-1634299373521)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015124758057.png)]

实现多线程的几种方式:

1.继承Thread类

Thread源码:

public class Thread implements Runnable{
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }

        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }

        sleep(millis);
    }
}

实现代码:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class MyThread extends Thread {
    public static void main(String[] args) throws InterruptedException {
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
        new MyThread().start();
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
    
}

2.实现Runnabl接口

Runnable接口源码:

/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.lang;

/**
 * The <code>Runnable</code> interface should be implemented by any
 * class whose instances are intended to be executed by a thread. The
 * class must define a method of no arguments called <code>run</code>.
 * <p>
 * This interface is designed to provide a common protocol for objects that
 * wish to execute code while they are active. For example,
 * <code>Runnable</code> is implemented by class <code>Thread</code>.
 * Being active simply means that a thread has been started and has not
 * yet been stopped.
 * <p>
 * In addition, <code>Runnable</code> provides the means for a class to be
 * active while not subclassing <code>Thread</code>. A class that implements
 * <code>Runnable</code> can run without subclassing <code>Thread</code>
 * by instantiating a <code>Thread</code> instance and passing itself in
 * as the target.  In most cases, the <code>Runnable</code> interface should
 * be used if you are only planning to override the <code>run()</code>
 * method and no other <code>Thread</code> methods.
 * This is important because classes should not be subclassed
 * unless the programmer intends on modifying or enhancing the fundamental
 * behavior of the class.
 *
 * @author  Arthur van Hoff
 * @see     java.lang.Thread
 * @see     java.util.concurrent.Callable
 * @since   1.0
 */
@FunctionalInterface
public interface Runnable {
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

实现源码:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class MyThread02 implements Runnable{
    int num=100;
    public static void main(String[] args){
        MyThread02 myThread01 = new MyThread02();
        MyThread02 myThread02 = new MyThread02();
        new Thread(myThread01).start();
        new Thread(myThread02).start();
    }

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

    }
}

3.实现Callable接口

public class Main {
    public static void main(String[] args) throws Exception {
    	 // 将Callable包装成FutureTask,FutureTask也是一种Runnable
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();

        // get方法会阻塞调用的线程
        Integer sum = futureTask.get();
        System.out.println(Thread.currentThread().getName() + Thread.currentThread().getId() + "=" + sum);
    }
}


class MyCallable implements Callable<Integer> {

    @Override
    public Integer call() throws Exception {
        System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tstarting...");

        int sum = 0;
        for (int i = 0; i <= 100000; i++) {
            sum += i;
        }
        Thread.sleep(5000);

        System.out.println(Thread.currentThread().getName() + "\t" + Thread.currentThread().getId() + "\t" + new Date() + " \tover...");
        return sum;
    }
}

线程的状态:

  • 创建

  • 就绪:调用了start方法,等待CPU调度

  • 运行:调用run方法

  • 阻塞:暂时停止线程,wait,sleep,join

  • 死亡:线程销毁(线程正常执行完毕,发生异常或者被打断interrupt导致线程终止)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YiXoEGwc-1634299373531)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015170905000.png)]

线程的常用方法:

public class Thread implements Runnable {
    // 线程名字
    private volatile String name;
    // 线程优先级(1~10)
    private int priority;
    // 守护线程
    private boolean daemon = false;
    // 线程id
    private long tid;
    // 线程组
    private ThreadGroup group;
    
    // 预定义3个优先级
    public final static int MIN_PRIORITY = 1;
    public final static int NORM_PRIORITY = 5;
    public final static int MAX_PRIORITY = 10;
    
    
    // 构造函数
    public Thread();
    public Thread(String name);
    public Thread(Runnable target);
    public Thread(Runnable target, String name);
    // 线程组
    public Thread(ThreadGroup group, Runnable target);
    
    
    // 返回当前正在执行线程对象的引用
    public static native Thread currentThread();
    
    // 启动一个新线程
    public synchronized void start();
    // 线程的方法体,和启动线程没毛关系
    public void run();
    
    // 让线程睡眠一会,由活跃状态改为挂起状态
    public static native void sleep(long millis) throws InterruptedException;
    public static void sleep(long millis, int nanos) throws InterruptedException;
    
    // 打断线程 中断线程 用于停止线程
    // 调用该方法时并不需要获取Thread实例的锁。无论何时,任何线程都可以调用其它线程的interruptf方法
    public void interrupt();
    public boolean isInterrupted()
    
    // 线程是否处于活动状态
    public final native boolean isAlive();
    
    // 交出CPU的使用权,从运行状态改为挂起状态
    public static native void yield();
    
    public final void join() throws InterruptedException
    public final synchronized void join(long millis)
    public final synchronized void join(long millis, int nanos) throws InterruptedException
    
    
    // 设置线程优先级
    public final void setPriority(int newPriority);
    // 设置是否守护线程
    public final void setDaemon(boolean on);
    // 线程id
    public long getId() { return this.tid; }
    
    
    // 线程状态
    public enum State {
        // new 创建
        NEW,

        // runnable 就绪
        RUNNABLE,

        // blocked 阻塞
        BLOCKED,

        // waiting 等待
        WAITING,

        // timed_waiting
        TIMED_WAITING,

        // terminated 结束
        TERMINATED;
    }
}

具体的方法可以查看java开发文档

线程安全问题:多个线程操作同一资源的时候会出现线程安全的问题

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class MyThread02 implements Runnable{
    int tickets=100;
    public static void main(String[] args){
        MyThread02 myThread01 = new MyThread02();
        new Thread(myThread01).start();
        new Thread(myThread01).start();
    }

    @Override
    public void run() {
        while(true){
            if(tickets>0){
                System.out.println(Thread.currentThread().getName()+":"+tickets--);
            }else {
                break;
            }
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QIiLfSs7-1634299373534)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015180449264.png)]
在这里插入图片描述

会出现重复操作资源的现象。

如何解决:

同步代码块:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 11:49
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class MyThread02 implements Runnable{
    int tickets=100;
    public static void main(String[] args){
        MyThread02 myThread = new MyThread02();
        new Thread(myThread).start();
        new Thread(myThread).start();
    }

    @Override
    public void run() {
        while(true){
            synchronized (this){
                if(tickets>0){
                    System.out.println(Thread.currentThread().getName()+":"+tickets);
                    tickets--;
                }
            }
        }
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yRxHjiyy-1634299373537)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015182033801.png)]

同步方法:

未使用同步方法:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 18:32
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class SynchronizedTest02 {
    public  void method1() {
        System.out.println("Method 1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 1 end");
    }

    public  void method2() {
        System.out.println("Method 2 start");
        try {
            System.out.println("Method 2 execute");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 2 end");
    }


    public static void main(String[] args) {
        final SynchronizedTest02 test = new SynchronizedTest02();
        new Thread(test::method1).start();
        new Thread(test::method2).start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vutTQzmF-1634299373538)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015185239471.png)]

使用同步方法:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 18:32
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class SynchronizedTest {
    public synchronized void method1() {
        System.out.println("Method 1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 1 end");
    }

    public synchronized void method2() {
        System.out.println("Method 2 start");
        try {
            System.out.println("Method 2 execute");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 2 end");
    }


    public static void main(String[] args) {
        final SynchronizedTest test = new SynchronizedTest();

        new Thread(test::method1).start();

        new Thread(test::method2).start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CZvnXKjc-1634299373539)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015185411550.png)]

当两个线程去操作同一方法时:

/*
    PACKAGE_NAME:PACKAGE_NAME
    USER:18413
    DATE:2021/10/15 18:32
    PROJECT_NAME:untitled
    面向代码面向君,不负代码不负卿————蒋明辉 */


public class SynchronizedTest {
    public synchronized void method1() {
        System.out.println("Method 1 start");
        try {
            System.out.println("Method 1 execute");
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 1 end");
    }

    public synchronized void method2() {
        System.out.println("Method 2 start");
        try {
            System.out.println("Method 2 execute");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Method 2 end");
    }


    public static void main(String[] args) {
        final SynchronizedTest test1 = new SynchronizedTest();
        final SynchronizedTest test2 = new SynchronizedTest();
        new Thread(test1::method1).start();
        new Thread(test2::method2).start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5DCpU2o3-1634299373540)(C:\Users\18413\AppData\Roaming\Typora\typora-user-images\image-20211015191320417.png)]

结果:两个线程执行时不会互相干扰执行,互不影响

死锁:图中的小明和小王,小王想要手里的遥控飞机,小明想要小王手里的遥控汽车,但是小王和小明拿着手里的东西不肯放手,造成了生活中常见的死锁问题。

死锁即是多个线程执行时,互相持有对方线程所需的资源,且保持自己的资源不释放。

img

死锁产生的条件:

  • 互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。

  • 请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。

  • 不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。

  • 循环等待条件:在发生死锁时,线程之间存在着循环等待,等待对方释放资源。

解决死锁的基本方法:

  • 破坏请求条件:资源一次性分配,一次性分配所有资源
  • 破坏请求保持条件:自要有一个资源得不到分配,也不会个这个线程分配其他资源
  • 破坏不剥夺条件:当前线程获取了部分资源,但是得不到其他资源的时候,线程释放其占有的资源
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值