Java多线程

目录

什么是线程

作用及应用场景

多线程实现

并发和并行

三种方式创建线程

线程优先级

守护线程

礼让线程

插入线程

线程的生命周期

线程安全

加锁方式

同步代码块

synchronized关键字加锁,自动锁

同步方法 

lock锁,手动锁

死锁

线程的等待唤醒

 通过队列实现(方式二)

线程池


什么是线程

线程是计算机最小的执行单元,是进程内的一个独立执行的流,可以让程序同时做多见事情

作用及应用场景

可以实现并行和并发的需求,可以在多任务和多用户环境下提高程序的效率,或者拷贝迁移大文件使用提高效率

多线程实现

并发和并行

并发:同一时刻,单个CPU交替执行多个线程

并行:同一时刻,多个CPU同时执行多个线程

6核12线程配置能同时执行12个线程。

三种方式创建线程

1.继承Thread类

2.实现Runnable接口

3.利用Callable接口和Future接口

class Work1 extends Thread{
    @Override
    public void run() {
        System.out.println(getName()+"启动。。。。");
    }
}

/**
 * 线程的实现方式1
 */
@Test
public void test1(){
    Thread work = new Work1();
    work.setName("1");
    work.start();
}

class Work2 implements Runnable{
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"启动。。。。");
    }
}

/**
 * 线程的实现方式2
 */
@Test
public void test2(){
    /**
     * 注意区别work2的方式,该写法只是在当前线程调用work2类中的run方法,不会创建新的线程执行
     * Runnable接口作用是定义一个线程任务,要多线程的去执行还是需要new Thread。
     */
    Work2 work2 = new Work2();
    work2.run();
    // 开启2个相同任务的线程,只需要创建一个线程任务对象
    Thread thread = new Thread(work2);
    Thread thread2 = new Thread(work2);
    thread.start();
    thread2.start();
}


class Work3 implements Callable<String>{
    @Override
    public String call() {
        Thread thread = Thread.currentThread();
        return thread.getName();
    }
}

/**
 * 创建线程方式3,该方式优点:可以返回线程执行的结果
 */
@Test
public void test3() throws ExecutionException, InterruptedException {
    Work3 work3 = new Work3();
    // FutureTask 管理多线程运行的结果
    FutureTask futureTask = new FutureTask(work3);
    Thread thread = new Thread(futureTask);
    thread.setName("3");
    thread.start();
    System.out.println(futureTask.get());
}

线程常用方法

线程优先级

线程调度方法:Java中使用抢占式调度方式。线程优先级越高,CPU执行它的概率越高,但并不会100%被执行。

抢占式调度:CPU随机执行线程

非抢占式调度:CPU按顺序执行线程

守护线程

Java中的守护线程(Daemon Thread)是一种特殊类型的线程,其作用是在后台提供服务和支持,通常用于执行一些低优先级的任务,例如垃圾回收、定时任务、后台数据同步等,这些任务不需要用户干预。一旦所有的非守护线程结束,守护线程将立即终止。

当聊连和传输文件2个线程同时执行时,关闭聊天窗口就没必要继续传输文件,此时可以将传输文件的线程设置为守护线程。

礼让线程

线程主动让出本次CPU执行时间,以允许其他线程执行。但是该线程可能再次获取到CPU。

插入线程

将start的线程插入到当前执行线程前执行。

线程的生命周期

线程安全

产生的原因:当前线程代码在执行时,随时会失去执行权。

解决办法:给线程代码加锁,执行完代码后释放锁。

加锁方式

同步代码块
synchronized关键字加锁,自动锁
static class Shopping extends Thread{

    static int food = 100;
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
           // 锁的对象必须要是唯一的
           synchronized (Shopping.class){
              if(food > 0){
                  food--;
                  System.out.println(Thread.currentThread().getName()+"正在卖第"+(100-food)+"件物品");
               }
            }
         }
    }
}


/**
 * 同步代码快,synchronized 
 */
@Test
public void test4(){
    Shopping shopping1 = new Shopping();
    Shopping shopping2 = new Shopping();
    Shopping shopping3 = new Shopping();
 
    Thread thread1 = new Thread(shopping1);
    Thread thread2 = new Thread(shopping2);
    Thread thread3 = new Thread(shopping3);

    thread1.setName("窗口1");
    thread2.setName("窗口2");
    thread3.setName("窗口3");

    thread1.start();
    thread2.start();
    thread3.start();
}
同步方法 

直接将synchronized修饰符加在方法上,会锁整个方法。

lock锁,手动锁
static class ShoppingLock implements Runnable{
    // 同一资源
    static int food = 100;
    // 同一锁
    static ReentrantLock lock = new ReentrantLock();

    @Override
    public void run() {

        for (int i = 0; i < 100; i++) {
            lock.lock();
            try {
                if(food > 0){
                    food--;
                    System.out.println(Thread.currentThread().getName()+":"+food);
                }else {
                    break;
                }
            } finally {
                // finally方式释放锁
                lock.unlock();
            }
        }

    }
}

死锁

 一般锁的嵌套使用引起。

线程的等待唤醒

object.wait()和notify()与synchronized隐式锁一起使用。

如果想用手动(显示锁)锁lock,线程等待与唤醒要用new ReentrantLock().newCondition()的await()和signal()。

static class Scz extends Thread{

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                // 线程结束标识
                if(Desk.count == 0){
                    break;
                }else {
                    if(Desk.space == 0){
                        Desk.space = 1;
                        System.out.println("生产了一个");
                        Desk.lock.notifyAll();
                    }else {
                        Desk.lock.wait();
                    }
                }
            }
        }
    }
}

static class Xfz extends Thread{

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            synchronized (Desk.lock){
                // 线程结束标识
                if(Desk.count == 0){
                    break;
                }else {
                    if(Desk.space == 0){
                        Desk.lock.wait();
                    }else {
                        Desk.space = 0;
                        Desk.count--;
                        System.out.println("消费了一个,还要消费"+Desk.count+"个。");
                        Desk.lock.notifyAll();
                    }
                }
            }
        }
    }
}

static class Desk{
    // 总数,线程停止的标识
    private static int count = 10;
    // 等待与唤醒的标识
    private static int space = 0;
    // 锁对象,用于将绑定的线程设为等待或唤醒
    private static Object lock = new Object();
}


@Test
public void test5(){

    Thread scz = new Scz();
    Thread xfz = new Xfz();

    scz.start();
    xfz.start();

}

 wait()让线程等待后,并不会再参与CPU的争夺并释放锁。直到被唤醒。

如果代码未加锁,就不会受保护,另一个线程可以等待获取锁的同时执行未加锁的代码。

 通过队列实现(方式二)

class SczQue extends Thread{

    ArrayBlockingQueue arrayBlockingQueue;

    public SczQue(ArrayBlockingQueue arrayBlockingQueue){
        this.arrayBlockingQueue = arrayBlockingQueue;
    }

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            // put方法底层自己有锁和等待方法
            arrayBlockingQueue.put("食物");
        }
    }
}

class XfzQue extends Thread{
    ArrayBlockingQueue arrayBlockingQueue;

    public XfzQue(ArrayBlockingQueue arrayBlockingQueue){
        this.arrayBlockingQueue = arrayBlockingQueue;
    }

    @SneakyThrows
    @Override
    public void run() {
        while (true){
            Object take = arrayBlockingQueue.take();
            System.out.println(take);
        }
    }
}

@Test
public void test6(){
    ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(1);
    SczQue sczQue = new SczQue(arrayBlockingQueue);
    XfzQue xfzQue = new XfzQue(arrayBlockingQueue);

    sczQue.start();
    xfzQue.start();

}

线程池

Executors:执行器工具类,可以用来创建线程池和添加任务。

自定义线程池:参考Executors创建线程池的源码。

Executor创建线程池源码
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>(),
                                  threadFactory);
}
各个参数意义:
核心线程数,最大线程数
空闲线程最大存活时间,时间单位(第三个参数的单位)
线程队列,线程工厂
任务拒绝策略

拒绝策略默认是丢弃任务并抛出异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值