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

Java多线程

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

Java多线程

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

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

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

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

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

进程与线程的关系:

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

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

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

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

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

为什么要使用多线程:

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

多线程的应用场景:

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



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("我醒了");
    }
}

并发编程



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());
    }
}

实现多线程的几种方式:

1.继承Thread类

Thread源码:

public class Thread implements Runnable{
    public synchronized void start() {
        
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                
            }
        }
    }
    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);
    }
}

实现代码:



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接口源码:


package java.lang;


@FunctionalInterface
public interface Runnable {
    
    public abstract void run();
}

实现源码:



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 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 {

    @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导致线程终止)

线程的常用方法:

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开发文档

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



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;
            }
        }
    }
}


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

如何解决:

同步代码块:



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:Users18413AppDataRoamingTyporatypora-user-imagesimage-20211015182033801.png)]

同步方法:

未使用同步方法:



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();
    }
}

使用同步方法:



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();
    }
}

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



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();
    }
}

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

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

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

死锁产生的条件:

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

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

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

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

解决死锁的基本方法:

  • 破坏请求条件:资源一次性分配,一次性分配所有资源
  • 破坏请求保持条件:自要有一个资源得不到分配,也不会个这个线程分配其他资源
  • 破坏不剥夺条件:当前线程获取了部分资源,但是得不到其他资源的时候,线程释放其占有的资源
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/327807.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

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

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