Java 多线程编程与单例模式

1. 线程的创建方式

在 Java 中,创建线程的方式主要有两种:继承 Thread实现 Runnable 接口

1.1 继承 Thread

通过继承 Thread 类并重写 run() 方法来创建线程。run() 方法是线程执行的逻辑体。

java 复制代码
public class MyThread extends Thread {

    private String name;

    public MyThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + "下载了" + i + "%");
        }
    }
}

测试代码:

java 复制代码
public class ThreadTest {
    public static void main(String[] args) {
        MyThread mt = new MyThread("肖申克的救赎");
        mt.start();

        MyThread mt1 = new MyThread("当幸福来敲门");
        mt1.start();
    }
}
1.2 实现 Runnable 接口

通过实现 Runnable 接口并实现 run() 方法来创建线程。这种方式更灵活,因为 Java 不支持多继承,但可以实现多个接口。

java 复制代码
public class DownLoad implements Runnable {

    private String name;

    public DownLoad(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + "下载了" + i + "%");
        }
    }
}

测试代码:

java 复制代码
public class ThreadTest {
    public static void main(String[] args) {
        Thread t = new Thread(new DownLoad("肖申克的救赎"));
        Thread t1 = new Thread(new DownLoad("当幸福来敲门"));
        t.start();
        t1.start();
    }
}

1.3实现 Callable 接口

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * 使用 Callable 创建线程
 */
public class DownloadCallable implements Callable<String> {

    private String name; // 电影名称

    public DownloadCallable(String name) {
        this.name = name;
    }

    @Override
    public String call() throws Exception {
        for (int i = 1; i <= 100; i++) {
            System.out.println(name + " 下载进度:" + i + "%");
            Thread.sleep(50); // 模拟下载耗时
        }
        return name + " 下载完成!";
    }
}

/**
 * 测试代码
 */
public class ThreadTest {
    public static void main(String[] args) {
        // 创建 Callable 任务
        DownloadCallable task1 = new DownloadCallable("肖申克的救赎");
        DownloadCallable task2 = new DownloadCallable("当幸福来敲门");

        // 使用 FutureTask 包装 Callable 任务
        FutureTask<String> futureTask1 = new FutureTask<>(task1);
        FutureTask<String> futureTask2 = new FutureTask<>(task2);

        // 创建线程并启动
        Thread thread1 = new Thread(futureTask1);
        Thread thread2 = new Thread(futureTask2);
        thread1.start();
        thread2.start();

        // 获取线程执行结果
        try {
            String result1 = futureTask1.get(); // 阻塞,直到线程1执行完毕
            String result2 = futureTask2.get(); // 阻塞,直到线程2执行完毕
            System.out.println(result1);
            System.out.println(result2);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
2. 线程的执行原理

线程的并发执行是通过多个线程不断切换 CPU 资源来实现的。由于切换速度非常快,我们感知到的是多个线程在并发执行。


3. 线程的生命周期

线程的生命周期包括以下几个状态:

  1. 新建(New):线程被创建,但尚未启动。

  2. 准备就绪(Runnable) :线程调用了 start() 方法,具备执行的资格,但尚未获得 CPU 资源。

  3. 运行(Running):线程获得 CPU 资源,正在执行。

  4. 阻塞(Blocked):线程因某些原因(如等待锁、I/O 操作)暂时停止执行。

  5. 销毁(Terminated):线程执行完毕或被强制终止。


4. 并发与同步

在多线程编程中,共享资源可能会导致数据不一致的问题。为了解决这个问题,可以使用 synchronized 关键字来实现同步。

1.同步代码块

java 复制代码
public class SaleTicketThread extends Thread {

    private String name;
    static int tickets = 100; // 共享资源
    static Object obj = new Object(); // 锁对象

    public SaleTicketThread(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (obj) {
                if (tickets > 0) {
                    System.out.println(name + "卖出座位是" + (tickets--) + "号");
                } else {
                    break;
                }
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println(name + "卖票结束");
    }
}

测试代码:

java 复制代码
public class ThreadTest {
    public static void main(String[] args) {
        SaleTicketThread t1 = new SaleTicketThread("窗口1");
        SaleTicketThread t2 = new SaleTicketThread("窗口2");
        SaleTicketThread t3 = new SaleTicketThread("窗口3");
        SaleTicketThread t4 = new SaleTicketThread("窗口4");

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

2.同步方法对象上

java 复制代码
public class SaleTicket implements Runnable {
    /**
     * 多个线程共享的100张票
     */
    int tickets = 100;

    //创建一个锁对象,这个对象是多个线程对象共享的数据
    Object obj = new Object();

    @Override
    public void run() {
        //卖票是持续的
        while (true){
            if(saleTickets()){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        System.out.println(Thread.currentThread().getName()+"卖票结束");
    }


    /*public boolean saleTickets(){
        synchronized (obj){
            boolean isFinish = false;
            if(tickets > 0){
                System.out.println(Thread.currentThread().getName()+"卖出座位是"+(tickets--)+"号");
            }else{
                isFinish = true;
            }
            return isFinish;
        }
    }*/

    /**
     *
     * @return     如果一个对象方法上有synchronized的话那么锁的对象就是this
     */
    public synchronized boolean saleTickets(){
        //synchronized (obj){
        boolean isFinish = false;
        if(tickets > 0){
            System.out.println(Thread.currentThread().getName()+"卖出座位是"+(tickets--)+"号");
        }else{
            isFinish = true;
        }
        return isFinish;
        //}
    }
}

3.同步类方法上,那么锁对象就是类的类对象

java 复制代码
public class SaleTicketThread extends Thread {

    private String name;

    /**
     * 定义共享的数据100张票
     */
    static int tickets = 100;

    //创建一个锁对象,这个对象是多个线程对象共享的数据
    static Object obj = new Object();


    public SaleTicketThread(String name) {
        super(name);
        this.name = name;


    }

    @Override
    public void run() {

        //卖票是持续的
        while (true){
            if(saleTickets()){
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
        System.out.println(name+"卖票结束");
    }


    public static synchronized boolean saleTickets(){

        boolean isFinish = false;
        if(tickets > 0){
            System.out.println(Thread.currentThread().getName()+"卖出座位是"+(tickets--)+"号");
        }else{
            isFinish = true;
        }
        return isFinish;

    }
}


//测试代码:
public class ThreadTest {

    public static void main(String[] args) {
        SaleTicketThread t1 = new SaleTicketThread("窗口1");
        SaleTicketThread t2 = new SaleTicketThread("窗口2");
        SaleTicketThread t3 = new SaleTicketThread("窗口3");
        SaleTicketThread t4 = new SaleTicketThread("窗口4");

        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }
}
5. 单例模式

单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。

单例模式的好处:

  1. 确保系统中只有一个实例,避免重复创建对象。

  2. 提供对唯一实例的受控访问。

  3. 节约系统资源,提高性能。

6 休眠

复制代码
在做服务器端的程序的时候都需要给一个休眠的时间,在没有synchronized代码块里面会让出cpu的资源。

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| public static void main(String[] args) { while (true ){ System.out .println(new Date()); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } |

休眠在同步代码块内不会让出cpu

|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| synchronized (ojb ){ try { // 我们休眠如果在 synchronized 内部就不会让出 cpu 的资源 Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } |

7.使用继承Thread,实现Runnable,实现Callable接口3种方法创建线程的区别

继承性

  • 继承 Thread 类:Java 是单继承语言,一个类继承了 Thread 类就无法再继承其他类,限制了类的扩展性。
  • 实现 Runnable 接口:实现 Runnable 接口的类还可以继承其他类,更符合面向对象中的 "聚合" 思想,能让多个线程共享一个 Runnable 实例,资源共享性更好。
  • 实现 Callable 接口:同样实现 Callable 接口的类也可以继承其他类,在实现多任务处理的同时保持了类的继承灵活性。

任务执行结果返回

  • 继承 Thread 类和实现 Runnable 接口:这两种方式中的 run () 方法没有返回值,无法直接获取线程执行后的结果,如果需要获取结果,需通过共享变量等间接方式实现。
  • 实现 Callable 接口:其 call () 方法允许有返回值,能更方便地获取线程执行的结果,可用于需要获取线程计算结果并进行后续处理的场景。

异常处理

  • 继承 Thread 类和实现 Runnable 接口:在 run () 方法中只能通过 try-catch 捕获处理内部异常,无法向外抛出。
  • 实现 Callable 接口:call () 方法可以声明抛出异常,由调用者进行处理,让异常处理更灵活、更可控。

启动方式

  • 继承 Thread 类:直接创建子类实例后调用 start () 方法启动线程。
  • 实现 Runnable 接口和 Callable 接口:需要将实现类的实例作为参数传入 Thread 类的构造函数,再调用 start () 方法启动线程。
相关推荐
Ljugg1 分钟前
把doi直接插入word中,然后直接生成参考文献
开发语言·c#·word
长流小哥2 分钟前
可视化开发:用Qt实现Excel级动态柱状图
开发语言·c++·qt·ui
Python测试之道10 分钟前
Deepseek API+Python 测试用例一键生成与导出 V1.0.6(加入分块策略,返回更完整可靠)
开发语言·python·测试用例
SRC_BLUE_1713 分钟前
Python GUI 编程 | QObject 控件基类详解 — 定时器
开发语言·数据库·python
啊阿狸不会拉杆21 分钟前
第二十一章:Python-Plotly库实现数据动态可视化
开发语言·python·plotly
okok__TXF26 分钟前
Mybatis源码分析
java·后端·mybatis
滴答滴答嗒嗒滴32 分钟前
Python小练习系列 Vol.12:学生信息排序(sorted + key函数)
开发语言·python
白云如幻1 小时前
【Java】Hibernate的一级缓存
java·hibernate
eternal__day1 小时前
Spring Boot 快速入手
java·spring boot·后端·spring·java-ee·maven
愚润求学1 小时前
【C++】vector常用方法总结
开发语言·c++·vector