Java多线程与线程安全

本文详细介绍了Java中多线程的三种创建方式(继承Thread类、实现Runnable接口和Callable接口),以及线程安全问题、线程同步(包括同步代码块、同步方法和Lock锁)和线程间的通信机制。还讨论了线程生命周期的基本概念。
摘要由CSDN通过智能技术生成

目录

多线程

一、创建线程

        1、继承Thread类

        2、实现Runnable接口

        3、实现Callable接口

二、API

线程安全

一、线程安全问题

        什么是线程安全问题?

        线程安全问题出现的原因

         解决方案

二、线程同步

        线程同步的思想

        线程同步的常见方案

        同步代码块

        同步方法

        Lock锁

线程通信

线程生命周期


多线程

一、创建线程

        1、继承Thread类

        Java是通过java.lang.Thread 类的对象来代表线程的。可以通过继承Thread类后,再重写Thread类中的run()方法。

// 1、定义一个线程类继承Thread类。
public class MyThread extends Thread{
    public MyThread(String name) {
        super(name);
    }

    // 2、重写Thread类提供的run方法(线程执行任务的方法)
    @Override
    public void run() {
    }
}

        创建MyThread对象

// 创建Thread对象 并通过MyThread的有参构造器为线程起名字
Thread t1 = new MyThread("1号->");
// t1.setName("1号");

        调用线程对象的start()方法,启动线程

t1.start();

优点:编码简单

缺点:线程类已经继承Thread,无法继承其他类,不利于功能的扩展。

        2、实现Runnable接口

        定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法

class RunnableThread implements Runnable{

    @Override
    public void run() {
        
    }
}

        创建MyRunnable任务对象

Runnable target = new RunnableThread();

        把MyRunnable任务对象交给Thread处理。

Thread thread = new Thread(target);
thread.start();

优点:任务类只是实现接口,可以继续继承其他类、实现其他接口,扩展性强。

           可以使用匿名内部类。

缺点:需要多一个Runnable对象。

        3、实现Callable接口

        通过前两种方式,假如线程执行完毕后有一些数据需要返回,他们重写的run方法均不能直接返回结果。为了解决这个问题,JDK 5.0提供了Callable接口和FutureTask类来实现(多线程的第三种创建方式)这种方式最大的优点:可以返回线程执行完毕后的结果。

创建任务对象

        定义一个类实现Callable接口,重写call方法,封装要做的事情,和要返回的数据。

        把Callable类型的对象封装成FutureTask(线程任务对象)。

class MyCallable implements Callable<Integer>{
    private int res;

    public MyCallable(int res) {
        this.res = res;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 1; i <= res; i++) {
            sum += i;
        }
        return sum;
    }
}
FutureTask<Integer> futureTask = new FutureTask<>(callable);

        把线程任务对象交给Thread对象。

Thread thread = new Thread(futureTask);

        调用Thread对象的start方法启动线程。

thread.start();

        线程执行完毕后、通过FutureTask对象的的get方法去获取线程任务执行的结果。

Integer integer = futureTask.get();

优点:线程任务类只是实现接口,可以继续继承类和实现接口,扩展性强;可以在线程执行完毕后去获取线程执行的结果。

缺点:编码复杂一点。

二、API

        Thread

线程安全

一、线程安全问题

        什么是线程安全问题?

                多个线程,同时操作同一个共享资源的时候,可能会出现业务安全问题。

        线程安全问题出现的原因

  •         存在多个线程在同时执行
  •         同时访问一个共享资源
  •         存在修改该共享资源

         解决方案

        在Java中,为了解决线程安全问题,引入了线程同步

二、线程同步

        线程同步的思想

让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

        线程同步的常见方案

加锁:每次只允许一个线程加锁,加锁后才能进入访问,访问完毕后自动解锁,然后其他线程才能再加锁进来

           让多个线程实现先后依次访问共享资源,这样就解决了安全问题。

        同步代码块

        作用:把访问共享资源的核心代码给上锁,以此保证线程安全。

synchronized(同步锁) {
          访问共享资源的核心代码
}

        原理:每次只允许一个线程加锁后进入,执行完毕后自动解锁,其他线程才可以进来执行

        注意事项:对于当前同时执行的线程来说,同步锁必须是同一把(同一个对象),否则会出bug。

        锁对象的使用规范:

建议使用共享资源作为锁对象

        对于实例方法建议使用this作为锁对象。

        对于静态方法建议使用字节码(类名.class)对象作为锁对象。

        同步方法

        作用:把访问共享资源的核心方法给上锁,以此保证线程安全。

修饰符 synchronized 返回值类型 方法名称(形参列表) {
	操作共享资源的代码
}

        原理:每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行

        同步方法的底层原理:

同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。

        如果方法是实例方法:同步方法默认用this作为的锁对象。

        如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

        两者对比:

是同步代码块好还是同步方法好一点?

        范围上:同步代码块锁的范围更小,同步方法锁的范围更大。

        可读性:同步方法更好。

        Lock锁

        Lock锁是JDK5开始提供的一个新的锁定操作,通过它可以创建出锁对象进行加锁和解锁,更灵活、更方便、更强大。

        Lock是接口,不能直接实例化,可以采用它的实现类ReentrantLock来构建Lock锁对象。

        Lock锁的常用方法:

线程通信

        当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,并避免无效的资源争夺。

线程生命周期

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值