Java 高级复习(多线程)

多线程编程

进程:

单进程:在同一时间段内,只允许一个程序在执行。

多进程:在同一时间段内,可以运行多个程序,并且这些程序将进行资源的轮流抢占。所以在同一时间段上会有多个程序依次执行,但是在同一个时间点上只会有一个进程在执行。

什么是线程?

线程是在进程的基础上划分的更小的运行单元,线程是在进程基础上创建并且使用的,所以线程依赖于进程的支持。但是线程的启动速度要比进程快许多,所以当使用多线程进行并发处理的时候,其执行性能要高于进程。

线程和进程的区别?

  • 进程是运行中的程序,线程是进程的内部的一个执行序列
  • 进程是资源分配的单元,线程是执行行单元
  • 进程间切换代价大,线程间切换代价小
  • 进程拥有资源多,线程拥有资源少
  • 多个线程共享进程的资源

线程实现方式

  1. 继承Thread类,重写Thread类中的run()方法,直接创建线程。
  2. 实现Runnable接口,再通过Thread类好Runnable的实现类间接创建一个进程。
  3. 使用Callable和Future接口间接创建进程。(最大的优势在于可以在线程执行完任务之后获取执行结果)

线程的生命周期

在线程的生命周期中,线程要经过五种状态:

  1. 新建
  2. 就绪
  3. 运行
  4. 阻塞
  5. 死亡

多线程的实现

public class Demo16 {
    public static void main(String[] args) {
        // 调用Thread类的currentThread()方法获取当前线程
        Thread thread = Thread.currentThread();
        // 设置线程名
        thread.setName("线程一");
        // 输出线程信息
        System.out.println(thread);

        // 获取线程id
        System.out.println("线程ID"+thread.getId());
        // 获取线程名
        System.out.println("线程名"+thread.getName());
    }
}

继承Thread类实现多线程

继承Thread类,重写Thread类中的run()方法,直接创建线程。

  1. 定义一个子类继承Thread类,重写run()方法。
  2. 创建子类的实例,即实例化线程对象。
  3. 调用线程对象的strat()方法启动该线程。
// 1.继承Thread类
public class Demo17 extends Thread {
    // 重写run()方法,线程的任务方法,即执行体

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            // 继承Thread类时,直接使用this即可获得当前线程对象
            // 调用getName()方法放回当前线程的名字
            System.out.println(this.getName() + ":" + i);
        }
    }

    public static void main(String[] args) {
        // 调用线程对象
        Demo17 demo17 = new Demo17();
        // 调用start()方法
        demo17.start();
        // 主线程任务
        for (int i = 1000; i < 1100; i++) {
            // 使用Thread.currentThread().getName()获取主线程名字
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

实现Runnable接口

  1. 定义一个类实现Runnable接口,并实现该接口中的run()方法。
  2. 创建一个Thread类的实例,并将Runnable接口实现类所创建的对象作为参数传入Thread类的构造方法中。
  3. 调用Thread对象的start()方法启动该线程。
// 1.线程任务类实现Runnable接口
class ThreadTask implements Runnable {
    // 重写run方法

    @Override
    public void run() {
        // 获取当前进程的名字
        for (int i = 0; i < 100; i++) {
            // 实现Runnable接口时,只能使用Thread.currentThread()获取当前线程
            // 再调用getName()方法返回当前线程名
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

public class Demo18 {
    public static void main(String[] args) {
        // 2.创建一个Thread类的实例,其参数是ThreadTask类的对象
        Thread thread = new Thread(new ThreadTask());
        // 3.调用start()方法启动该进程
        thread.start();
        for (int i = 1000; i < 1100; i++) {
            // 使用Thread.currentThread().getName()获取主线程名字
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}

实现Callable接口和Future接口

使用Callable接口和Future接口的最大优势在于可以在线程执行完成任务之后获取任务的执行结果。

  1. 创建Callable接口的实现类,并实现call()方法,该方法作为线程的执行体,并具有返回值;然后创建Callable实现类的实例。
  2. 使用FutureTask类来包装Callable对象,在FutureTask对象中封装里Callable对象的call()方法的返回值。
  3. 调用FutureTask对象作为Thread对象的target,创建并启动新线程。
  4. 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

// 1.创建Callable接口的实现类
// 接口对应的泛型限制为<Integer>
class Task implements Callable<Integer> {
    //实现call()方法,作为线程的执行体

    @Override
    public Integer call() throws Exception {
        int i = 0;
        for (; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + ": " + i);
        }
        // call()方法可以有返回值
        return i;
    }
}


public class Demo19 {
    public static void main(String[] args) {
        // 2.使用FutureTask类包装Callable实现类的实例
        FutureTask<Integer> task = new FutureTask<Integer>(new Task());
        // 3.创建线程,使用FutureTask对象task作为Thread对象的target,并调用start()方法启动线程
        new Thread(task, "子线程").start();
        // 4.调用FutureTask对象task的get()方法获取子线程执行结束后的返回值
        try {
            System.out.println("子线程的返回值" + task.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        // 主线程
        for (int i = 1000; i < 1100; i++) {
            // 使用Thread.currentThread().getName()获取主线程名字
            System.out.println(Thread.currentThread().getName() + ":" + i);
        }
    }
}
子线程: 0
子线程: 1
子线程: 2
……
子线程: 99
子线程的返回值100
main:1000
main:1001
……
main:1099

线程的同步

同步代码块

public class Demo20 {
    // synchronized为同步关键字,object为同步监视器(线程开始执行同步代码之前,必须完成对同步代码的锁定)
    synchronized (object){
        // 需要同步的代码
    }
}

非同步代码块

public void run() {
        double d = this.account.getBalance();
        if (money < 0 && d < -money) {
            System.out.println("余额不足");
        } else {
            d += money;
            this.account.setBalance(d);
        }
    }
}

同步代码块

public void run() {
        synchronized (this.account) {
            double d = this.account.getBalance();
            if (money < 0 && d < -money) {
                System.out.println("余额不足");
            } else {
                d += money;
                this.account.setBalance(d);
            }
        }
    }
}

同步方法

[访问修饰符] synchronized 返回类型 方法名([参数列表]){
    // 方法体
}
// 同步方法
    public synchronized void access(double money) {
        if (money < 0 && balance < -money) {
            System.out.println("余额不足");
        } else {
            balance += money;
        }
    }
public void run() {
    // 调用同步方法
    this.account.access(money);
}

同步锁

import java.util.concurrent.locks.ReentrantLock;

public class Demo21 {
    // 定义锁对象
    private final ReentrantLock lock = new ReentrantLock();

    public void myMethod() {
        // 加锁
        lock.lock();
        try {
            // 需要安全执行的代码
        } finally {
            // 释放锁
            lock.unlock();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_29537269/article/details/106971026