JavaSE线程知识回顾

1.实现多线程的方法

1.1 继承Thread类

步骤:1.自定义类继承Thread类 2.重写run方法,run方法中是需要执行的多线程程序 3.创建自定义类的对象 4.对象.start()

public static void main(String[] args) {
        MyThread m1 = new MyThread();
        MyThread m2 = new MyThread();
        m1.start();
        m2.start();
    }

线程方法:

start():开启线程
sleep():线程休眠指定时间
join():等待此线程执行完毕后再执行其他线程
interupt():中断线程,把线程状态终止,并抛出异常。

1.2 实现Runable接口

步骤:1.自定义类实现Runable接口 2.重写run方法,run方法中是需要执行的多线程程序 3.创建自定义类的对象 4.创建Thread类对象,并把创建的对象作为构造参数传递。
在这里插入图片描述

1.3 实现Callable接口+FutureTask(泛型可以拿到返回结果,处理异常)

步骤:1.自定义类实现Callable接口 2.重写call方法,call方法中是需要执行的多线程程序 3.创建自定义类的对象 4.创建FutureTask类对象,并把创建的对象作为构造参数传递。
在这里插入图片描述
在这里插入图片描述
若get()后还有执行的语句,将在获得结果后才会执行相应语句,执行get()为阻塞等待,在返回结果之前下面的语句不会执行

1.4 线程池

在业务代码中,以上三种启动线程的方法都不用。
将所有的多线程异步任务都直接给线程池执行。
一个系统中池只有一两个,每个异步任务都交给他自己去执行

//固定数量为10个的线程池
public static ExecutorService service=Executors.new FixedThreadPool(10);
service.execute(new Runable01());

线程池详解

线程池中有七大参数,详解如下:

int corePoolSize,核心线程数:一直存在,除非设置allowCoreThreadTimeOut,线程池创建好以后就准备就绪的线程数量,等待接收异步任务执行

int maximumPoolSize,最大线程数量:控制线程资源

long keepAliveTime,存活时间:如果当前的线程数量大于核心数量,只要线程空闲大于指定的keepAliveTime就释放空闲的线程(maximumPoolSize-corePoolSize的这一部分),核心线程数一直存在

TimeUnit unit:存活时间的时间单位

BlockingQueue workQueue,阻塞队列:如果任务有很多,就会将目前多的任务放在队列里,只要有线程空闲,就会去队列里取出新的任务进行执行

ThreadFactory threadFactory:线程的创建工厂

RejectedExecutionHandler handler,拒绝策略:如果workQueue队列满了,按照我们指定的拒绝策略,拒绝执行任务

题目

一个线程池, core 7 ,max 20 ,queue:50, 100并发进来怎么分配?

7个立即得到执行,50个进入队列,最大为20,可以再开13个线程执行,剩下30个使用拒绝策略。
如果不想抛弃,还要执行,拒绝策略使用 CallerRunsPolicy,同步执行。

ThreadPoolExecutor executor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

    }

常用线程池

//core为0,所有都可回收
Executors.newCachedThreadPool();  
//固定大小,core=max,所有都可回收
Executors.newFixedThreadPool(10);

1.5 四种方式区别

前两种区别:第二种方式避免了Java单继承带来的局限性(如一个子类想实现多线程,就不能继承父类了,而父类并不想实现多线程)。

四种区别:1、2不能得到返回值,3可以获取返回值。1、2、3都不能控制资源。4可以控制资源,性能稳定。

2.解决线程同步问题

2.1 同步代码块

synchronized(锁对象){
需要同步的代码块
}

同步代码块的锁对象是谁?

可以是任意对象。

同步方法的格式及锁对象?

把同步关键字加在方法上。锁对象为this。

静态方法的锁对象?

类的字节码文件对象。

2.2 锁

		Lock lock = new ReentrantLock();
        lock.lock();
        //需要同步的代码块
        lock.unlock();

死锁问题

在这里插入图片描述
在这里插入图片描述

2.Java高并发

2.1 问题

1.在一个synchronized方法执行时,另一个普通方法能否执行?

在这里插入图片描述
可以执行,因为只有同步方法执行时才需要申请锁对象。普通方法直接执行即可,不需申请锁对象就可以执行。

2.一个同步方法是否可以调用另外一个同步方法?一个线程已经拥有某个对象的锁,再次申请是否仍然会得到锁?

在这里插入图片描述
可以调用。再次申请仍可以得到锁。也就是说synchronized获得的锁是可重入(已经获得的锁仍然可以再获得)的。子类的synchronized方法也可以调用父类的synchronized方法,不会造成死锁。

3.synchronized线程抛出异常会释放锁吗?

会,如果不想释放,进行try catch。

4.解释volatile在这里插入图片描述

程序执行结果为:当不加volatile关键字时,不会打印end。加上后,打印end。
在这里插入图片描述
假设两个cpu在运行两个线程,每个cpu都有一部分为缓冲区,主内存有一变量,在线程运行时,一个cpu会将这一变量copy到它自己的缓冲区,这个cpu在运行非常忙时就不再去主内存读取这个变量(当cpu比较空闲时就可能会去cpu读取修改后的值),而是直接使用它缓存中的内容。当另一个cpu执行另一个线程时,修改这个变量的值,修改后直接写回主内存。此时主内存中与第一部分的cpu中的变量值已经不同,但第一个cpu不会去读取这个变量的值。加上volatile关键字后,当第二个cpu对这个变量值进行修改后,会通知第一个线程,重新读取这个变量值。
volatile的意思就是,当某个线程修改了一个变量的值时,会通知其他线程,需要重新读取变量值。此题中为main线程和t1线程。

5.synchronized和volatile区别

前者保证可见性和原子性,后者只保证可见性。后者的执行效率远远高于前者。
Java中提供了Atomic类,实现较为简单的同步操作,可以保证原子性,而且运行效率比synchronized要高不少。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值