java 多线程 学习笔记

多线程

DOS系统只支持单进程,一旦程序有问题,系统就不能运行其他程序,而Windows系统则支持了多进程,只是会消耗大量资源,程序执行性能下降。由此,又开发了出了多线程的概念,依附于指定的进程,又可以并发执行,快速启动。

例如,每次启动word都像开启了一个进程,而如拼写检查,联网服务都是一个个的线程。进程消失,线程肯定消失,但是线程消失,进程不一定消失

一、多线程的实现

Thread是线程的主要操作类

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        new MyThread("线程1").start(); // 启动线程,这里才是真正的启动线程,调用系统的start0方法
        new MyThread("线程2").start();
        new MyThread("线程3").start();
    }
}
class MyThread extends Thread{
    private String title;
    public MyThread(String title){
        this.title = title;
    }
    @Override
    public void run(){ //覆写run方法,这里提供的是资源的准备
        for (int x=0;x<10;x++){
            System.out.println(this.title+" x= "+x);
        }
    }
}

二、Runnable接口

Thread类只能单继承,局限性太大,因此开发了Runnable接口,定义如下

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

1. Runnable实现

这样通过集成Runnable就能实现run方法,并且这个接口还支持lambda函数式表达式

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        new Thread(new MyThread("线程1")).start(); // 此时MyThread不是Thread的子类,因此需要new一个
        new Thread(new MyThread("线程2")).start();
        new Thread(new MyThread("线程3")).start();
    }
}
class MyThread implements Runnable{
    private String title;
    public MyThread(String title){
        this.title = title;
    }
    @Override
    public void run(){
        for (int x=0;x<10;x++){
            System.out.println(this.title+" x= "+x);
        }
    }
}

2. lambda表达式实现

还能通过lambda表达式的方法,直接定义线程方法主体

public class MyThreadDemo {
    public static void main(String[] args) throws Exception {
        for (int x= 0;x<3;x++){
            String title = "线程对象-"+x;
            new Thread(()->{ // lambda函数实现run方法,直接生成三个线程对象和线程竞争资源
                for (int y=0;y<10;y++){
                    System.out.println(title+" y= "+y);
                }
            }).start();
        }
    }
}

3. Thread和Runnable的不同

实际开发中要使用Runnable避免单继承局限

public class Thread extents Objext implements Runnable{}

Thread类继承了Runnable接口,实际实现的还是Runnable中的run方法

此时Thread相当于一个Runnable的代理类,负责对接真实业务MyThread,直接进行内部转化资源调度

同时,Thread相当于new一个线程对象,而MyThread集成Runnable相当于new了一个线程资源,然后再把资源放到对象里,这样更加符合设计理念

public class MyThreadDemo {
    public static void main(String[] args) throws Exception {
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}
class MyThread implements Runnable{ // 生成了一个卖票资源,让线程来争抢
    int ticket = 5;
    @Override
    public void run(){
        for (int x=0;x<100;x++){
            if(this.ticket > 0){
                System.out.println("剩余票数:"+this.ticket--);
            }
        }
    }
}

4. Callable有返回接口

定义如下

@FunctionalInterface
public interface Callable<V>{ // 定义一个泛型,接受返回值
    public V call() throws Exception{};
}
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        FutureTask<String> task = new FutureTask<>(new MyThread()); // 包装Callable
        new Thread(task).start(); // 把task包装起来,FutrueTask是一个过渡类
        System.out.println("返回值: "+task.get());
    }
}

class MyThread implements Callable<String>{
    @Override
    public String call() throws Exception {
        for (int x=0;x<100;x++){
            System.out.println("线程: x="+x);
        }
        return "线程返回";
    }
}

5. 线程运行状态

  1. 创建状态:new Thread后,线程的对象和空间就已经分配好了,但是不可运行
  2. 就绪状态:调用start方法就可以启动线程,此时等待CPU调度服务
  3. 运行状态:获得处理器资源后,开始调用该线程的对象的run方法
  4. 阻塞状态:认为挂起,或等待某项任务执行,就会让CPU暂时中止自己的行为,进入阻塞状态
  5. 中止状态:run结束后,终止状态,线程不再运行

三、多线程常用操作方法

1. 获取线程名字

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt,"线程a").start();
        new Thread(mt).start();
        new Thread(mt,"线程b").start();
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int x=0;x<10;x++){
            System.out.println(Thread.currentThread().getName());
        }
    }
}

主线程获取

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt,"线程a").start();
        mt.run(); // 直接调用实例化对象的run方法
    }
}

class MyThread implements Runnable{
    @Override
    public void run() {
        for (int x=0;x<10;x++){
            System.out.println(Thread.currentThread().getName());
        }
    }
}

进程是由多个线程构成的,使用java命令执行一个类的时候,就已经启动了一个JVM进程,主方法则是这个进程上的一个线程而已,类执行完毕后,线程就消失了

实际开发过程中,所有的线程都是通过主线程来创建的

利用子线程,处理一下复杂的逻辑的同时,不影响别的任务的执行,如下

public class MyThreadDemo {
    public static void main(String[] args) throws Exception{
        new Thread(()->{
            try{
                Thread.sleep(100);
            }catch (Exception e){
                e.printStackTrace();
            }
        }).start(); // 耗时任务
        System.out.println("任务A");// 简单任务不需要等待耗时任务完成后才能执行
        System.out.println("任务B");
    }
}

2. 线程休眠

No.方法类型描述
1public static void sleep(long millis) throws InterruptedException普通设置线程休眠的毫秒数,时间一到自动唤醒
2public static void sleep(long millis, int nanos) throws InterruptedException普通设置毫秒数和纳秒数
public class MyThreadDemo{
    public static void main(String[] args) {
        Runnable run = ()->{
            for (int x=0;x<10;x++){
                System.out.println(Thread.currentThread().getName()+"、x= "+x);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        };
        for (int i=0;i<5;i++){
            new Thread(run,"线程对象-"+i).start();
        }
    }
}

3. 线程中断

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        Thread thread = new Thread(()->{
                System.out.println("准备睡觉");
                try {
                    Thread.sleep(10000);
                    System.out.println("正在睡觉,美滋滋!");
                } catch (InterruptedException e){
                    System.out.println("被吵醒了,生气!");
                }
        });
        thread.start();
        Thread.sleep(1000); // 子进程先运行一秒
        if (!thread.isInterrupted()){ // 打断进程
            System.out.println("一声炮响");
            thread.interrupt();
        }
    }
}

4. 线程强制执行

5. 线程礼让

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        Runnable run = ()->{
            for(int i=0;i<100;i++){
                if (i%3==0){
                    Thread.yield();
                    System.out.println("太君,这边请!"+ Thread.currentThread().getName());
                }
                try {
                    Thread.sleep(100);
                }catch (Exception e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" i= "+i);
            }
        };
        new Thread(run,"A").start();

        for (int x=0;x<100;x++){
            Thread.sleep(100);
            System.out.println("呦西,你滴大大滴良民!");
        }
    }
}

6. 线程优先级

No.方法类型描述
1public static final int MAX_PRIORITY常量最高优先级,10
2public static final int NORM_PRIORITY常量中等优先级,5
3public static final int MIN_PRIORITY常量中等优先级,5
4public final void setPriority(int newPriority)普通设置线程优先级
5public final int getPriority()普通取得线程优先级
public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        Runnable run = ()->{
            for(int i=0;i<10;i++){
                System.out.println(Thread.currentThread().getName()+"i= "+i);
            }
        };
        Thread threadA = new Thread(run,"A");
        Thread threadB = new Thread(run,"B");
        Thread threadC = new Thread(run,"C");
        threadA.setPriority(Thread.MAX_PRIORITY);
        threadC.setPriority(Thread.MIN_PRIORITY);
        System.out.println(threadB.getPriority());
        threadA.start();
        threadB.start();
        threadC.start();
    }
}

四、线程同步与死锁

现在有三个线程,每个线程中都运行着一个run方法,调用start方法开始运行。例如下面的卖票程序

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt).start();
        new Thread(mt).start();
        new Thread(mt).start();
    }
}
class MyThread implements Runnable{
    private int ticket = 3;
    @Override
    public void run() {
        while (true){
            if (this.ticket>0){
                try{
                    Thread.sleep(100);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println("余票 = "+this.ticket--);
            } else {
                System.out.println("无票");
                break;
            }
        }
    }
}
// 输出
//余票 = 2
//余票 = 3
//余票 = 3
//余票 = 1
//无票
//余票 = -1
//无票
//余票 = 0
//无票

每个线程中都在执行run方法中的程序,并且通过共同的变量ticket来判断是否执行,但是当判断ticket大于0到ticket–的中间,需要执行时间,这就造成了线程的不同步,可能第一个线程得知余票=3,开始执行程序,但是尚未输出余票减一,第二线程此时也得知了余票=3,开始执行,这就造成了不同步。当第一个程序运行出来时,ticket=2,第二个程序

1. 同步代码块

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt, "A").start();
        new Thread(mt, "B").start();
        new Thread(mt, "C").start();
    }
}
class MyThread implements Runnable{
    private int ticket = 3;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (this.ticket>0){
                    try{
                        Thread.sleep(100);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
                } else {
                    System.out.println("无票");
                    break;
                }
            }
        }
    }
}

2. 同步方法

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt, "A").start();
        new Thread(mt,"B").start();
        new Thread(mt,"C").start();
    }
}

class MyThread implements Runnable{
    private int ticket = 3;
    @Override
    public void run() {
        while (this.sale()){}
    }
    
    public synchronized boolean sale(){
        if (this.ticket>0){
            try{
                Thread.sleep(100);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
            return true;
        } else {
            System.out.println("无票");
            return false;
        }

    }
}

3. 线程死锁

4. 生产者和消费者

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        Message msg= new Message();
        new Thread(new Producer(msg)).start();
        new Thread(new Consumer(msg)).start();
    }
}
class Producer implements Runnable{
    private Message msg = null;

    public Producer(Message msg){
        this.msg = msg;
    }

    public void run(){
        for (int x=0;x<50;x++){
            if (x%2==0){
                msg.setTitle("煎饼果子");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                msg.setContent("加个鸡蛋");
            } else{
                msg.setTitle("烙馍卷菜");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                msg.setContent("不要鸡蛋");
            }
        }
    }
}
class Consumer implements Runnable{
    private Message msg = null;
    public Consumer(Message msg){
        this.msg=msg;
    }

    @Override
    public void run() {
        for (int x=0;x<50;x++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(msg.getTitle()+"  "+msg.getContent());
        }
    }
}
class Message{
    private String title;
    private String content;

    public void setTitle(String title){
        this.title = title;
    }
    public void setContent(String content){
        this.content = content;
    }
    public String getTitle(){
        return title;
    }
    public String getContent(){
        return content;
    }
}

线程同步

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        Message msg= new Message();
        new Thread(new Producer(msg)).start();
        new Thread(new Consumer(msg)).start();
    }
}
class Producer implements Runnable{
    private Message msg = null;

    public Producer(Message msg){
        this.msg = msg;
    }

    public void run(){
        for (int x=0;x<50;x++){
            if (x%2==0){
                msg.set("煎饼果子","加个鸡蛋");
            } else{
                msg.set("烙馍卷菜", "不要鸡蛋");
            }
        }
    }
}
class Consumer implements Runnable{
    private Message msg = null;
    public Consumer(Message msg){
        this.msg=msg;
    }

    @Override
    public void run() {
        for (int x=0;x<50;x++){
            try {
                Thread.sleep(100);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(msg.get());
        }
    }
}
class Message{
    private String title;
    private String content;

    public synchronized void set(String title, String content){
        this.title= title;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e){
            e.printStackTrace();
        }
        this.content = content;
    }
    public synchronized String get(){
        return this.title+"--->"+this.content;
    }
}

五、Object线程等待与唤醒

No.方法类型描述
1public final void wait() throws InterruptedException普通线程等待
2public final void notify()普通按照等待的次序唤醒一个进程
3public final void notifyAll()普通唤醒全部进程,并按优先级启动

六、优雅的停止线程

public class MyThreadDemo{
    private static boolean flag = true;
    public static void main(String[] args) throws Exception{
        new Thread(()->{
            long num=0;
            while (flag){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" num="+num++);
            }
        },"执行线程").start();
        Thread.sleep(300); // 300毫秒之后,flag变为false,线程中获取到,终止while循环
        flag = false;
        System.out.println("终止线程");
    }
}

七、后台守护线程

public class MyThreadDemo{
    private static boolean flag = true;
    public static void main(String[] args) throws Exception{
        Thread UserThread = new Thread(()->{
            long num=0;
            while (flag){
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+" num="+num++);
            }
        },"亲爱的");

        Thread DaemonThread = new Thread(()->{
            for (int i=0;i<Integer.MAX_VALUE;i++){
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }

                System.out.println(Thread.currentThread().getName()+" n= "+ i);
            }
        }, "守护者");

        DaemonThread.setDaemon(true);
        UserThread.start();
        DaemonThread.start();

        Thread.sleep(3000);
        flag = false;
        System.out.println("终止线程");
    }
}

八、volatile关键字

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        new Thread(mt, "A").start();
        new Thread(mt, "B").start();
        new Thread(mt, "C").start();
    }
}
class MyThread implements Runnable{
    private volatile int ticket = 3;
    @Override
    public void run() {
        while (true){
            synchronized (this){
                if (this.ticket>0){
                    try{
                        Thread.sleep(100);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"余票 = "+this.ticket--);
                } else {
                    System.out.println("无票");
                    break;
                }
            }
        }
    }
}

volatile关键字是一种直接对内存的处理,主要在属性上使用,进程直接修改原始变量,而不是进行副本操作,synchronized是实现同步操作的关键字。

九、总结

  1. 线程是指程序的运行流程,多线程机制可以同时运行多个代码块,提高效率
  2. 实现多线程必须要继承Thread类或者实现Runnable接口,并且必须覆写run方法
  3. 每一个线程都会经历以下五种状态:创建、就绪、运行、阻塞、终止
  4. 可以使用synchronized关键字进行资源的同步处理,同时应防范死锁的产生
  5. Object类中提供有wait方法和唤醒方法notify(), notifyAll()
  6. 线程开发分为两种,用户线程和守护线程,守护线程依附于用户线程存在
  7. volatile关键字是对原始变量的定义,可以直接访问变量内存修改,而不是进行副本操作和数据同步

十、习题

问题抢答

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class MyThreadDemo{
    public static void main(String[] args) throws Exception{
        MyThread mt = new MyThread();
        FutureTask<String> taskA = new FutureTask<>(mt);
        FutureTask<String> taskB = new FutureTask<>(mt);
        FutureTask<String> taskC = new FutureTask<>(mt);
        new Thread(taskA,"A").start();
        new Thread(taskB,"B").start();
        new Thread(taskC,"C").start();
        System.out.println(taskA.get());
        System.out.println(taskB.get());
        System.out.println(taskC.get());
    }
}
class MyThread implements Callable<String> {
    private boolean flag = true;
    @Override
    public String call() {
        synchronized (this){
            if (this.flag){
                this.flag=false;
                return Thread.currentThread().getName()+" 抢答成功";
            } else{
                return Thread.currentThread().getName()+" 抢答失败";
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值