JAVA高级:线程

1. 什么是线程

线程,又称轻量级进程。进程中的一条执行路径,也是CPU的基本调度单位。一个进程由一个或多个线程组成,彼此间完成不同的工作,同时执行,成为多线程。

1.2 进程和线程的区别

(1)进程是操作系统资源分配的基本单位,而线程是CPU的基本调度单位。
(2)一个程序运行后至少有一个进程。
(3)一个进程可以包含多个线程,但是至少需要有一个线程,否则这个进程是没有意义的。
(4)进程间不能共享数据段地址,但是同进程的线程之间可以。

1.3 获取和修改线程ID和线程名称:

获取线程ID和线程名称:
a. 在Thread的子类中调用this.getId()或this,getName()
b. 使用Thread,currentThread().getId()和Thread.currentThread.getName()
修改线程名称:
a. 调用线程对象的setName()方法
b. 使用线程子类的构造方法赋值

2. 线程的创建方式

(1)继承Thread类,重写run方法


public class MyThread extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println("线程---------------"+i);
        }
    }
}


public class Test01 {
    public static void main(String[] args) {
        //创建一个线程对象
        MyThread m1 = new MyThread();
        //开启线程  执行时会调用run方法
        m1.start();
 
        for (int i = 0; i < 10; i++) {
            System.out.println("主线程**********"+i);
        }
    }

(2)实现Runnable接口

public class TicketSell implements Runnable{
    private static int total = 100;
    private Object object = new Object();
    @Override
    public void run() {
        while (true){
            synchronized (this.object){
                if (total>0){
                    System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+total--+"张票");
                }
            }
        }
    }


public class Test03 {
    public static void main(String[] args) {
        //创建任务对象
        TicketSell ticketSell = new TicketSell();
        //将任务对象放入创建的线程中,并设置线程名
        Thread t1 = new Thread(ticketSell,"售票窗口1");
        Thread t2 = new Thread(ticketSell,"售票窗口2");
        Thread t3 = new Thread(ticketSell,"售票窗口3");
        Thread t4 = new Thread(ticketSell,"售票窗口4");
        //启动线程
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

3. 常见方法

休眠: public static void sleep(long millis)
当前线程主动休眠millis毫秒

放弃: public static void yield()
当前线程主动放弃时间片,回到就绪状态,竞争下一次时间片

加入: public final void join()
允许其他线程加入到当前线程中

优先级: 线程对象.setPriority()
线程优先级1-10,默认为5,优先级越高,表示获取CPU的概率越高。

守护线程:线程对象.setDaemon(true);设置为守护线程。
线程有两类:用户线程(前台线程)和守护线程(后台线程)
如果程序中所有前台线程都执行完毕了,后台线程也会自动结束。
垃圾回收线程属于守护线程。

4. 多线程安全问题

使用synchronized保证线程的安全性:

//同步代码块:

//对临街资源对象加锁

synchronized(临界资源对象){

        //代码(原子操作)

}

synchronized也可以用在方法上(同步方法,只有拥有对象互斥锁标记的线程,才能进入该对象加锁的同步方法中)

同步方法:

synchronized 返回值类型 方法名称(形参列表){
        //代码(原子操作)
}

注意:每个对象都有一个互斥锁标记,用来分配给线程的。
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。
只有在调用包含同步代码块的方法,或者同步方法时,才需要对象的所标记。如调用
不包含同步代码块的方法,或普通方法时,则不需要锁标记,可直接调用。

5.线程死锁

当A线程拥有锁资源a时,这时A线程需要锁资源b, 而B线程拥有锁资源b,这时B线程需要锁资源a, 这样会导致A等待B线程释放资源b, B线程等待A线程释放锁资源a。 从而二个处于永久等待。从而操作死锁。
例子: 情人节—两个情人去餐厅吃饭—必须具有两个筷子。
男方拥有一根筷子,女方拥有另一个筷子。
操作死锁得原因:1. 锁与锁之间有嵌套导致。
如何解决死锁:1. 尽量减少锁得嵌套。
2. 可以使用一些安全类。
3. 可以使用Lock中得枷锁,设置枷锁时间。

6.线程通信

在这里插入图片描述
线程通信中使用得方法有哪些?
在这里插入图片描述
例子: 存钱和取钱。

package com.hzk.demo6;

/**
 * @author hzk
 * @date 2022/7/20 16:27
 */
public class BankCard {
    private double balance;//余额
    //true表示有钱   false表示没钱
    private boolean flag=false;

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        this.balance = balance;
    }

    public synchronized void save(double money){
        if (flag==true){
            //进入等待队列
            try {
                this.wait();
                System.out.println("nan");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //存钱
        this.balance=this.balance+money;
        System.out.println(Thread.currentThread().getName()+"向卡中存入"+money+"卡中余额为"+this.balance);
        flag=true;
        //唤醒等待队列
        this.notify();
    }
    public synchronized void take(double money){
        if (flag==false){
            //进入等待队列
            try {
                this.wait();
                System.out.println("nv");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //取钱
        this.balance=this.balance-money;
        System.out.println(Thread.currentThread().getName()+"在卡中取走"+money+"卡中余额为"+this.balance);
        flag=false;
        //唤醒等待队列
        this.notify();
    }
}

sleep和wait得区别?
1.所在得类不同。sleep属于Thread类,wait属于Object类。
2.使用的地方: sleep可以使用再任何代码块。wait只能再同步代码块中。
3.是否释放锁资源: sleep不释放锁资源,wait会释放锁资源。
4.sleep时间到了自动唤醒,wait必须需要使用notify和notifyAll唤醒
notify和notyfyAll区别?
在这里插入图片描述

7.线程池

在这里插入图片描述
线程池的创建方式有哪些?
在这里插入图片描述

package com.hzk.demo7;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
 /*Executor:线程池的根类。里面有一个方法。execute执行线程任务的方法Runnable类型的任务
         *       ExecutorService: 线程池的子接口
         *           shutdown(); 关闭线程池。需要等待线程池中任务执行完毕后才会关闭。
         *           shutdownNow(): 立即关闭线程池。
         *           isTerminated():判断线程池是否终止了。
         *           submit(): 提交任务给线程池中线程对象、Runnable和Callable类型的任务。*/
/**
 * int corePoolSize, 核心线程数
 *          int maximumPoolSize, 最大线程数
 *          long keepAliveTime, 空闲时间
 *          TimeUnit unit, 时间单位
 *          BlockingQueue<Runnable> workQueue: 堵塞队列,
 *
 *  根据你自己的业务以及服务器配置来设置。
 */
/**
 * @author hzk
 * @date 2022/7/20 18:21
 */
public class Test {
    public static void main(String[] args) {
        //单一线程池:适应场景:队列要求线程有序执行
        /*ExecutorService executorService = Executors.newSingleThreadExecutor();*/
        //创建固定长度的线程池对象
        //ExecutorService executorService = Executors.newFixedThreadPool(3);
        //创建可变长度的线程池
        //ExecutorService executorService = Executors.newCachedThreadPool();
        //创建延迟线程池对象
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(3);

        /*for (int i = 0; i < 100; i++) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"==================");
                }
            });
        }*/
        for (int i = 0; i < 5; i++) {
            executorService.schedule(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"==================");
                }
            },10, TimeUnit.SECONDS);
        }
        executorService.shutdown();
    }
}

8.使用最原始的方式创建线程池

上面讲解的使用Executors创建线程池的方式,都是使用底层ThreadPoolExecutor,而阿里开发手册,建议使用最原始的方式。
在这里插入图片描述

public class Test02 {
    public static void main(String[] args) {
        /**
         * int corePoolSize, 核心线程数
*          int maximumPoolSize, 最大线程数
*          long keepAliveTime, 空闲时间
*          TimeUnit unit, 时间单位
*          BlockingQueue<Runnable> workQueue: 堵塞队列,
         *
         *  根据你自己的业务以及服务器配置来设置。
         */
        //LinkedBlockingDeque:可以设置等待的个数,如果不设置默认为Integer的最大值。
        BlockingQueue blockingQueue=new LinkedBlockingDeque(3);
        ThreadPoolExecutor threadPoolExecutor=
                new ThreadPoolExecutor(2,5,10, TimeUnit.SECONDS,blockingQueue);


        for (int i = 0; i <5 ; i++) {
            threadPoolExecutor.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"~~~~~~~~~~~~~~");
                }
            });
        }

        threadPoolExecutor.shutdown();
    }
}

9.创建线程的第三种方式

实现Callable接口,它和实现Runnable接口差不多,只是该接口种的方法有返回值和异常抛出

package com.hzk.demo12;

import java.util.concurrent.*;

/**
 * @author hzk
 * @date 2022/7/21 9:41
 */
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        My my = new My();
        My1 my1 = new My1();
        final ExecutorService executorService = Executors.newFixedThreadPool(2);
        final Future<Integer> future = executorService.submit(my);
         Future<Integer> future1 = executorService.submit(my1);
        final Integer sum = future.get();//需要等线程执行完毕后,才会把结果返回给该变量、
        final Integer sum1 = future1.get();
        System.out.println(sum+sum1);
    }
}
class My implements Callable<Integer>{

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

    @Override
    public Integer call() throws Exception {
        int sum=0;
        for (int i = 51; i <=100; i++) {
            sum+=i;
        }
        return sum;
    }
}

10. 手动锁

Lock它是手动锁的父接口,它下面有很多实现类。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值