多线程的应用

一、进程与线程的概念

进程: 进程是应用程序的执行实例,有独立的内存空间,占用独立的系统资源。

线程:包含在进程之内的,有主线程与子线程的区分。是CPU调度和分派的基本单位,每个子线程都可以独立的完成一个功能。

什么是多线程?

如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为“多线程”,多个线程交替占用CPU资源,而非真正的并行执行

多线程好处:充分利用CPU的资源,简化编程模型,带来良好的用户体验

并行就是两个任务同时运行,就是甲任务进行的同时,乙任务也在进行。(需要多核CPU)

并发是指两个任务都请求运行,而处理器只能按受一个任务,就把这两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。

二、主线程

Thread类 Java提供了java.lang.Thread类支持多线程编程

主线程

main()方法即为主线程入口

产生其他子线程的线程

必须最后完成执行,因为它执行各种关闭动作

public static void main(String[] args) {
    Thread thread = Thread.currentThread(); //线程对象
    String name = thread.getName();
    System.out.println("线程名称为:"+name); //主线程
    thread.setName("mythread");
    System.out.println("线程名称为:"+thread.getName());
}

三、创建线程

在Java中创建线程的三种方式:

  • 继承java.lang.Thread类

  • 实现java.lang.Runnable接口

  • 实现Callable接口和Future接口

使用线程的步骤:

1、定义线程

2、创建线程对象

3、启动线程

4、终止线程

3.1 继承Thread类实现创建线程

步骤:

  • 定义MyThread类继承Thread类

  • 重写run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

示例

public class MyThread extends Thread {

    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        switch (name){
            case "xc1":
                for (int i = 0; i < 100; i++) {
                    System.out.println(Thread.currentThread().getName() + ":" + i);
                }
                break;
            case "xc2":
                for (int i = 0; i < 10; i++) {
                    System.out.println("好好学习java"+i);
                }
                break;
        }
    }
}

创建线程对象:

public static void main(String[] args) {
    MyThread myThread1=new MyThread(); //创建线程对象
    myThread1.setName("xc1");
    myThread1.start(); //启动线程

    MyThread myThread2=new MyThread(); //创建线程对象
    myThread2.setName("xc2");
    myThread2.start(); //启动线程

}

多个线程交替执行,不是真正的“并行”,线程每次执行时长由分配的CPU时间片长度决定

3.2 实现Runnbale接口创建线程

步骤:

  • 定义MyRunnable类实现Runnable接口

  • 实现run()方法,编写线程执行体

  • 创建线程对象,调用start()方法启动线程

示例

public class MyRunable implements  Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName()+":"+i);
        }
    }
}

创建线程对象

public class Test1 {
    public static void main(String[] args) {
        Runnable myRunable=new MyRunable(); //1、实例化对象

        Thread thread=new Thread(myRunable); //2、构建一个线程对象
        thread.setName("线程1");
        thread.start(); //3、启动线程

        Thread thread1=new Thread(myRunable);
        thread1.setName("线程2");
        thread1.start();

    }
}
3.3 实现Callable接口和Future接口

定义线程

步骤:

1、创建Callable接口的实现类,并重写call()方法,该方法具有返回值

2、实例化实现类对象,通过实现类对象进行入参来构建一个FutureTask对象,FutureTask对象实现了Runable接口,Future也是Runable和Future接口的实现类

3、通过FutureTask对象入参构建一个线程对象,并调用start()方法来启动执行线程

4、适用FutureTask对象的get()方法来获得线程执行的结果

import java.util.concurrent.Callable;

/**
@author:mengshujun
@createTime: 2024-03-20 10:38:41 星期三
*/
public class MyThread3 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int num1=11;
        int num2=22;
        int sum=num1+num2;
        return sum;
    }
}

创建线程并调用

public class Test2 {
    public static void main(String[] args) {
        MyThread3 m3=new MyThread3(); //实例化对象
        FutureTask<Integer> futureTask=new FutureTask(m3); //Runable对象
        //创建线程
        Thread t1=new Thread(futureTask);
        t1.setName("线程一");
        t1.start();
        //new Thread(futureTask,"线程一").start();
        try {
            System.out.println("线程的执行结果为:"+futureTask.get()); //获得线程的返回值
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

适用于线程有返回值的情况,可以适用这种方式来创建线程,接收结果。

三种创建线程的区别:

1、继承Thread类,编写简单,可直接操作线程,适用于单继承

2、实现Runnable接口,避免单继承局限性,便于共享资源。

3、实现Callable接口,可以接收线程运行的结果。

推荐使用实现Runnable接口方式创建线程

四、线程的五种状态

创建类

package com;

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        String name = Thread.currentThread().getName();
        System.out.println("线程" + name + ",现在处于就绪状态");
        for (int i = 0; i < 30; i++) {
            System.out.println("线程" + name + ",现在处于运行状态");
            try {
                Thread.sleep(3);
                System.out.println("线程" + name + ",现在处于阻塞状态");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("线程" + name + ",现在处于死亡状态");
    }
}

创建线程

public static void main(String[] args) {
    MyRunnable myRunnable=new MyRunnable();
    Thread thread=new Thread(myRunnable);
    thread.setName("线程一");
    System.out.println("线程"+thread.getName()+",现在处于创建状态");
    thread.start();
}

五、线程调度

线程调度指按照特定机制为多个线程分配CPU的使用权

线程优先级:线程优先级由1~10表示,1最低,默认优先级为5,优先级高的线程获得CPU资源的概率较大

线程休眠:让线程暂时睡眠指定时长,线程进入阻塞状态,睡眠时间过后线程会再进入可运行状态

线程礼让:暂停当前线程,允许其他具有相同优先级的线程获得运行机会,该线程处于就绪状态,不转为阻塞状态

线程中断:使当前线程暂停执行,等待其他线程结束后再继续执行本线程

public final void join()
public final void join(long mills)
public final void join(long mills,int nanos)

millis:以毫秒为单位的等待时长

nanos:要等待的附加纳秒时长

需处理InterruptedException异常

六、线程使用同一资源的数据安全问题

存在问题的代码

package net;

/**
 * 抢票线程类
 */
public class BuyThread implements Runnable {
    public int num = 10; //总票数
    public int count = 0; //记录当前的用户(线程)抢到了第几张票

    @Override
    public void run() {
        while (true) {
            if (num == 0) {
                System.out.println("票已被抢完,抢票活动结束!");
                break;
            }
            num--;
            count++;
            System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张");

        }
    }
}

创建线程类

public static void main(String[] args) {
    BuyThread buyThread = new BuyThread();
    Thread t1 = new Thread(buyThread, "徐磊");
    Thread t2 = new Thread(buyThread, "贾恒广");
    Thread t3 = new Thread(buyThread, "袁晓波");

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

七、解决线程安全问题有两种解决方案

7.1、使用synchronized (this) 代码块来解决

同步代码块锁 ,this就代表的是当前进来的线程对象,进行锁定,本线程执行完操作后才会释放锁。

package net;

/**
 * 抢票线程类
 */
public class BuyThread implements Runnable {
    public int num = 10; //总票数
    public int count = 0; //记录当前的用户(线程)抢到了第几张票

    @Override
    public void run() {
        //同步代码块锁 ,this就代表的是当前进来的线程对象
        //1
        while (true) {
            synchronized (this) {
                if (num == 0) {
                    System.out.println("票已被抢完,抢票活动结束!");
                    break;
                }
                num--;
                count++;
                System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张");
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }

    }
}
7.2、定义synchronized方法来解决
package net;

/**
 * 抢票线程类
 */
public class BuyThread implements Runnable {
    public int num = 10; //总票数
    public int count = 0; //记录当前的用户(线程)抢到了第几张票


    @Override
    public void run() {
        while (true) {
            if (!cale()) {
                break;
            }
        }  
    }

    //同步方法
    public synchronized boolean cale() {
        boolean isFlag = true;
        if (num == 0) {
            isFlag = false;
            System.out.println("票已售完!");
            return isFlag;
        }
        num--;
        count++;
        System.out.println("当前用户(线程)," + Thread.currentThread().getName() + "抢到了第" + count + "张票,总票数还剩余" + num + "张");

        try {
            Thread.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return isFlag;
    }
}

八、线程安全的类型

ArrayList list=new ArrayList(); //动态的数组
list.add(1);

Vector vector=new Vector();  //线程安全
vector.add(100);
HashMap map1=new HashMap();
map1.put("id","111");

Hashtable table1=new Hashtable();  //线程安全
table1.put("id","222");
StringBuilder sb1=new StringBuilder();
sb1.append("ddd");

StringBuffer sb=new StringBuffer(); //线程安全
sb.append("abc");

  • 16
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
LoadingCache是Guava库中的一个缓存工具类,用于高效地缓存数据。在多线应用中,LoadingCache可以提供良好的并发支持。 首先,LoadingCache的内部实现是线程安全的。它使用了并发集合ConcurrentMap作为缓存的存储结构,并使用了同步机制来保证多线程并发的正确性。因此,在多线程环境下,不需要额外的加锁操作,不需要开发人员手动处理并发访问问题,而是由LoadingCache自身来处理。 其次,LoadingCache提供了get和getAll方法来获取缓存中的数据。在多线应用中,当多个线程同时调用get方法来获取缓存中的数据时,LoadingCache可以保证只有一个线程去加载数据,其他线程会等待加载完成后直接从缓存中获取数据,避免了重复的数据加载操作。 另外,LoadingCache还提供了refresh方法,用于定时刷新缓存中的数据。在多线应用中,可以通过设置定时任务或者使用定时调度框架,定期调用refresh方法来刷新缓存中的数据。这样可以保证在多线程并发操作下,缓存的数据始终是最新的。 需要注意的是,在多线应用中,如果缓存的数据更新频繁,而且有多个线程在同时操作缓存,可能会导致缓存的命中率下降。这时可以考虑使用更加高级的缓存方案,如分布式缓存,来进一步提升缓存的并发性能和命中率。 综上所述,LoadingCache在多线应用中可以提供良好的并发支持。它的线程安全的内部实现、合理的并发控制机制,以及定时刷新的功能,使得在多线程并发访问下,能够高效地缓存和获取数据,并保证数据的一致性和可靠性。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值