java多线程 简述

创建线程

继承Thread类

1.定义一个子类继承Thread类,并重写run方法
2.创建Thread的子类对象
3.调用start方法启动线程(启动线程后,会自动执行run方法中的代码)

public class MyThread extends Thread{
    // 1、必须重写Thread类的run方法
    @Override
    public void run() {
        // 描述线程的执行任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程MyThread输出:" + i);
        }
    }
}

// 以下为测试类
// 3、创建MyThread线程类的对象代表一个线程
Thread t = new MyThread();
// 4、启动线程(自动执行run方法的)
t.start(); 
实现Runnable接口

1.先写一个Runnable接口的实现类,重写run方法(这里面就是线程要执行的代码)
2.再创建一个Runnable实现类的对象
3.创建一个Thread对象,把Runnable实现类的对象传递给Thread
4.调用Thread对象的start()方法启动线程(启动后会自动执行Runnable里面的run方法)

/**
 * 1、定义一个任务类,实现Runnable接口
 */
public class MyRunnable implements Runnable{
    // 2、重写runnable的run方法
    @Override
    public void run() {
        // 线程要执行的任务。
        for (int i = 1; i <= 5; i++) {
            System.out.println("子线程输出 ===》" + i);
        }
    }
}
// 以下为测试类
// 3、创建任务对象。
Runnable target = new MyRunnable();
// 4、把任务对象交给一个线程对象处理。
//  public Thread(Runnable target) 构造器
new Thread(target).start();

可使用内部类简化

实现Callable接口

1.先定义一个Callable接口的实现类,重写call方法
2.创建Callable实现类的对象
3.创建FutureTask类的对象,将Callable对象传递给FutureTask
4.创建Thread对象,将Future对象传递给Thread
5.调用Thread的start()方法启动线程(启动后会自动执行call方法)
等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中

6.调用FutrueTask对的get()方法获取返回结果

public class MyRunnable implements Callable<Integer>{
    Integer n;
    public CallableDemo(Integer n) {
        this.n = n;
    }
   // 先定义一个Callable接口的实现类,重写call方法
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i;
            System.out.println(sum);
        }
        return sum;
    }
}

// 以下为实现类
// 创建Callable实现类的对象
CallableDemo myRunnable = new MyRunnable(100);
//创建FutureTask类的对象,将Callable对象传递给FutureTask
FutureTask<Integer> task = new FutureTask<>(myRunnable);
// 创建Thread对象,将Future对象传递给Thread
Thread thread = new Thread(task);
// 调用Thread的start()方法启动线程(启动后会自动执行call方法) 等call()方法执行完之后,会自动将返回值结果封装到FutrueTask对象中
thread.start();
// 调用FutrueTask对的get()方法获取返回结果
System.out.println(task.get());
小结

继承Thread类 无法再继承其他类 无法获取返回值

实现Runnable接口 可继承其他类 无法获取返回值

实现Callable接口 可继承可获取返回值

多线程

多线程常用方法
public void start() // 启动线程
public String getName() // 获取当前线程名称
public void setName() // 设置当前线程名称
public static Thread currentThread() // 获取当前执行的线程对象
public static void sleep(long time) // 让线程休眠多少毫秒后再执行
public final void join() // 让调用这个方法的线程先执行完
Thread提供的常见构造器
public Thread(String name) // 为当前线程指定名称
public Thread(Runnable target) // 封装Runnable对象为线程对象
public Thread(Runnable target,String name) //封装Runnable对象为线程对象,并指定名称
public class MyThread extends Thread{
    public MyThread(String name){
        super(name); // 执行了父类的Thread(String name)构造器,设置名字
    }
    @Override
    public void run(){
        Thread t = Thread.currentThread();
       	System.out.println(t.getName()) // 打印线程名称
    }
}

// 测试类中
Thread t = new MyThread() //获取线程对象
Thread t1 = new MyThread() //获取线程对象
t.getName() // 获取线程名称
t.setName() // 设置线程名称
t.sleep(2000) // 线程休眠2秒
t.start() // 启动线程
t.join() // 先执行完t线程
t1.start() // 启动t1线程

线程安全问题

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

  • 创建一个共享的账户类 实现一个修改数据的方法
  • 创建一个线程类重写的run方法中调用修改数据的方法
  • 创建测试类 new多个线程对象进行修改

线程同步方案

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

  • 同步代码块
  • 同步方法
  • Lock锁

在操作数据的时候加锁

同步代码块
// 小明 小红线程同时过来的
public void drawMoney(double money) {
    // 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    // this正好代表共享资源!
    synchronized (this) {
        if(this.money >= money){
            System.out.println(name + "来取钱" + money + "成功!");
            this.money -= money;
            System.out.println(name + "来取钱后,余额剩余:" + this.money);
        }else {
            System.out.println(name + "来取钱:余额不足~");
        }
    }
}

1.建议把共享资源作为锁对象, 不要将随便无关的对象当做锁对象
2.对于实例方法,建议使用this作为锁对象
3.对于静态方法,建议把类的字节码(类名.class)当做锁对象

同步方法

同步方法就是给整个方法上锁,同时只能有一个线程调用该方法

// 同步方法
public synchronized void drawMoney(double money) {
    // 先搞清楚是谁来取钱?
    String name = Thread.currentThread().getName();
    // 1、判断余额是否足够
    if(this.money >= money){
        System.out.println(name + "来取钱" + money + "成功!");
        this.money -= money;
        System.out.println(name + "来取钱后,余额剩余:" + this.money);
    }else {
        System.out.println(name + "来取钱:余额不足~");
    }
}
Lock锁

Lock锁是JDK5版本专门提供的一种锁对象,通过这个锁对象的方法来达到加锁,和释放锁的目的,使用起来更加灵活

1.在成员变量,创建一个Lock接口的实现类对象

private final Lock lk = new ReentrantLock();

2.在需要上锁的地方加入下面的代码

lk.lock(); // 加锁
//...中间是被锁住的代码...
lk.unlock(); // 解锁

线程通信(了解)

notify() // 唤醒任意一线程
notifyAll() // 唤醒所有线程
wait(); // 让当前线程等待

线程池

创建线程池

new ThreadPoolExecutor(核心线程数,最大线程数,临时线程存活时间,存活时间单位,线程队列,线程工厂,拒绝策略)

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

创建临时线程的时间

新任务提交时,发现核心线程都在忙、任务队列满了、并且还可以创建临时线程,此时会创建临时线程。

什么时候开始拒绝新的任务

核心线程和临时线程都在忙、任务队列也满了、新任务过来时才会开始拒绝任务。

线程池执行Runnable任务

使用execute()执行

ExecutorService pool = new ThreadPoolExecutor(
    3,	//核心线程数有3个
    5,  //最大线程数有5个。   临时线程数=最大线程数-核心线程数=5-3=2
    8,	//临时线程存活的时间8秒。 意思是临时线程8秒没有任务执行,就会被销毁掉。
    TimeUnit.SECONDS,//时间单位(秒)
    new ArrayBlockingQueue<>(4), //任务阻塞队列,没有来得及执行的任务在,任务队列中等待
    Executors.defaultThreadFactory(), //用于创建线程的工厂对象
    new ThreadPoolExecutor.CallerRunsPolicy() //拒绝策略
);

Runnable target = new MyRunnable();
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
pool.execute(target); // 线程池会自动创建一个新线程,自动处理这个任务,自动执行的!
线程池执行Callable任务

使用submit()执行

// 1、通过ThreadPoolExecutor创建一个线程池对象。
        ExecutorService pool = new ThreadPoolExecutor(
            3,
            5,
            8,
            TimeUnit.SECONDS, 
            new ArrayBlockingQueue<>(4),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy());

        // 2、使用线程处理Callable任务。
        Future<String> f1 = pool.submit(new MyCallable(100));
        Future<String> f2 = pool.submit(new MyCallable(200));

并发并行

并发

并发就是多条线程交替执行

并行

多个线程同时被CPU调度执行(CPU多核心)

线程的声明周期

NEW: 新建状态,线程还没有启动
RUNNABLE: 可以运行状态,线程调用了start()方法后处于这个状态
BLOCKED: 锁阻塞状态,没有获取到锁处于这个状态
WAITING: 无限等待状态,线程执行时被调用了wait方法处于这个状态
TIMED_WAITING: 计时等待状态,线程执行时被调用了sleep(毫秒)或者wait(毫秒)方法处于这个状态
TERMINATED: 终止状态, 线程执行完毕或者遇到异常时,处于这个状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

绝知芳誉亘千乡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值