Java基础 Day24

发布于:2025-05-30 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、进程和线程

1、进程

(1)概念

进程 (Process) 是计算机中的程序关于某数据集合上的一次运行活动

是系统进行资源分配的基本单位

简单理解:程序的执行过程(正在运行的应用程序)

(2)特性

独立性:每一个进程都有自己的空间,在没有经过进程本身允许的情况下,一个进程不可以直接访问其它的的进程空间

动态性:进程是动态产生,动态消亡的

并发性:任何进程都可以同其它进程一起并发执行

Tips:

并行:在同一时刻,有多个指令在多个CPU上【同时】执行

并发:在同一时刻,有多个指令在单个CPU上【交替】执行

多进程同时工作:对于一个CPU(单核),它是在多个进程间轮换执行的

2、线程

(1)概念

线程(Thread):进程可以同时执行多个任务,每个任务就是线程

(2)多线程的意义

提高执行效率;同时处理多个任务

随着处理器上的核心数量越来越多,现在大多数计算机都比以往更加擅长并行计算

但是,一个线程,在一个时刻,只能运行在一个处理器核心上

Java程序也是一个进程,如果是一个单线程程序,则无法调动处理器的多个核心

二、Java中开启线程的方式

Tips:Java程序默认是多线程的,一条主线程,一条垃圾回收线程

1、法一:继承Thread类

步骤:

(1)编写一个类继承Thread类

(2)重写run方法

(3)将线程任务写在run方法中

(4)创建线程对象

(5)调用start方法开启线程

注意:直接调用run方法并不能开启线程

2、法二:实现Runnable接口

(扩展性更好)

步骤:

(1)编写一个类实现Runnable接口

(2)重写run方法

(3)将线程任务写在run方法中

(4)创建线程任务资源对象

(5)创建线程对象,将资源传入

(6)使用线程对象调用start方法开启线程

public class ThreadDemo2 {
    public static void main(String[] args) {
//        (4)创建线程任务资源对象
        MyRunnable mr = new MyRunnable();
//        (5)创建线程对象,将资源传入
        Thread t1 = new Thread(mr);
//        (6)使用线程对象调用start方法开启线程
        t1.start();

        for (int i = 0; i < 100; i++) {
            System.out.println("main" + i);
        }
    }
}

//(1)编写一个类实现Runnable接口
class MyRunnable implements Runnable {
//    (2)重写run方法
    @Override
    public void run() {
//        (3)将线程任务写在run方法中
        for (int i = 0; i < 100; i++) {
            System.out.println("MyRunnable" + i);
        }
    }
}

3、法三:实现Callable接口

(线程任务有返回值)

步骤:

(1)编写一个类实现Callable接口

(2)重写call方法

(3)将线程任务写在call方法中

(4)创建线程任务资源对象

(5)创建线程任务对象,封装线程资源

(6)创建线程对象,传入线程任务

(7)使用线程对象调用start方法开启线程

public class ThreadDemo3 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        MyCallable mc = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(mc);
        Thread thread = new Thread(task);
        thread.start();
        Integer result = task.get(); // 获取线程任务的返回值
        System.out.println(result);
    }
}

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}

三、线程的相关方法

String getName​()

返回此线程的名称

void setName​(String name)

设置线程的名字(构造方法也可以设置名字)

static Thread currentThread()

获取当前线程的对象

static void sleep(long time)

让线程休眠指定的时间,单位为毫秒

setPriority(int newPriority)

设置线程的优先级,从1到10,默认为5

final int getPriority()

获取线程的优先级

final void setDaemon(boolean on)

设置为守护线程

Tips:线程的调度方式分为抢占式调度(随机)和非抢占式调度(轮流)

Java 采用的方式是抢占式调度

提高线程的优先级可以提高该线程抢到CPU的概率

四、线程安全和同步

1、安全问题出现的条件

是多线程环境

有共享数据

有多条语句操作共享数据

2、同步技术

将多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程可以执行

(1)同步代码块

格式:
synchronized(锁对象) {
    多条语句操作共享数据的代码
}

示例:
public class TicketDemo {
    public static void main(String[] args) {
        // 只new了一个TicketTask对象,三个线程共享一份数据
        TicketTask ticket = new TicketTask();
        Thread t1 = new Thread(ticket);
        Thread t2 = new Thread(ticket);
        Thread t3 = new Thread(ticket);
        t1.start();
        t2.start();
        t3.start();
    }
}

class TicketTask implements Runnable {
    private int tickets = 2000;

    @Override
    public void run() {
        while (true) {
            // 建议使用字节码文件作为锁对象
            synchronized (TicketTask.class) {
                if (tickets <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " sold " + tickets);
                tickets--;
            }
        }
    }
}   

Tips:锁对象可以是任意对象,但是需要保证多条线程的锁对象,是同一把锁

同步可以解决多线程的数据安全问题,但是也会降低程序的运行效率

(2)同步方法

在方法的返回值类型前面加入 synchronized 关键字

该方法里的代码就变成同步的

静态方法的锁对象是字节码对象,非静态方法的锁对象是 this

(3)Lock 锁

使用 Lock 锁,可以更清晰地看到哪里加了锁,哪里释放了锁

Lock 是接口,无法直接创建对象

public ReentrantLock()

构造方法:创建一个 ReentrantLock 的实例互斥锁

void lock()

加锁

void unlock();

释放锁

3、死锁

两个或者多个线程互相持有对方所需要的资源

导致这些线程处于等待状态,无法前往执行

产生死锁的情况:同步嵌套


网站公告

今日签到

点亮在社区的每一天
去签到