Java学习(第十天)线程

线程与进程:

进程:是指一个内存中运行的应用程序,每个进程都有一个独立的内存空间。

 

线程:

是进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行,一个进程最少有一个线程。

线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。

 

线程调度:

分时调度:

所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。

抢占式调度:

优先让优先级高的线程使用CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为

抢占式调度。

CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻,

只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时

刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使

用率更高。

 

 

同步与异步&并发与并行

同步:排队执行,效率低但是安全。

异步:同时执行,效率高但是数据不安全。

 

并发与并行

并发:指两个或多个事件在同一个时间段内发生。

并行:指两个或多个事件在同一时刻发生(同时发生)。

 

继承Thread

需要自行实现run()方法。

run方法就是线程要执行的任务方法。

在run方法中,实现run方法的代码就是一条新的执行路径

这个执行路径的处罚方式,不是调用run方法,而是通过thread对象的start()来启动任务

 

每个线程都拥有自己的栈空间,共用一份堆内存。

由一个线程调用的方法,这个方法也会执行在这个线程里面。

 

实现Runnable

创建一个类实现Runnable,用来作为实现子线程任务的类。

也是需要自行实现run方法,来实现线程的任务。

使用方式:

1.创建一个任务对象

2.创建一个线程,并为其分配一个任务

3.执行这个线程

 

实现Runnable 和 继承Thread相比有如下优势:

1.通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况。

2.可以避免单继承所带来的局限性。

3.任务与线程本身是分离的,提高了程序的健壮性。

4.后续学习的线程池技术,接受Runnable类型的任务,不接收Thread类型的线程。

 

也可以采用类似匿名内部类的方式实现线程。

 

Thread类(API)

getName(),getId()等方法

三个常量(最大优先级,最低优先级,默认优先级)

注意(stop()方法不能停止,已过时,不安全)

最常用的方法sleep()方法

守护线程(daemon线程)(依赖于用户线程)和用户线程

 

 

设置和获取线程名称

Thread.currentThread()(.getName()获取名称.setName()设置名称)方法可以得到当前运行的线程。

 

线程休眠sleep:

Thread.sleep()方法

 

线程阻塞:

所有消耗时间的操作都可以称为阻塞,会导致线程等待

 

线程的中断:

一个线程是一个独立的执行路径,它是否应该结束,应该由其自身决定。

(线程启动以后,可能会使用很多资源,如果在外部停用的话,就会产生资源没有释放的问题,会产生很多不能回收的垃圾)。

注意:子类不能声明比父类更大的异常抛出。

对需要中断的线程使用interrupt()方法,告知线程应该中断,会在内部产生异常进入catch块,程序员决定是否杀死进程。

 

守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡。

线程:分为守护线程和用户线程

用户线程:当一个进程不包含任何的存活的用户线程时,进行结束。

设置为守护线程的方法.setDaemon(true)。

 

线程安全问题:

数据有误

线程不安全的处理方式:

同步代码块:

线程同步:格式:synchronized(锁对象){

  需要控制的部分(可以一行代码加锁)

}

注意:每个对象都看同一把锁。

 

同步方法(在设计的方法前加上synchronized修饰):

以方法为单位进行加锁。

将要加锁的部分设计为一个方法。

如果上锁的对象方法不是静态的,那么调用后使用它锁的对象就是this,

如果是静态的,就是对象名.class

如果同时有同步代码块和同步方法,如果进入到了代码块后,后续的线程也进不去同步方法中。

同步代码块和同步方法都属于隐式锁

显示锁Lock 子类 ReentrantLock

需要创建Lock对象,在需要上锁的部分进行.lock()方法,在需要解锁的部分进行.unlock()方法。

 

显示锁和隐式锁的区别:

一、层面不同

synchronized:Java中的关键字,是由JVM来维护的,是JVM层面的锁。

synchronized底层是通过monitorenter进行加锁

底层是通过monitor对象来完成的,其中的wait/notify等方法也是依赖于monitor对象的。并且只有在同步块或同步方法中,JVM才会调用monitory对象的,才可以调用wait/notify等方)

通过monitorexit来退出锁

Lock:是JDK5以后才出现的具体的类。使用lock是调用对应的API,是API层面的锁。

lock是通过调用对应的API方法来获取锁和释放锁的。

二、使用方式不同

synchronized

程序能够自动获取锁和释放锁。Sync是由系统维护的,如果非逻辑问题的话话,不会出现死锁。

Lock

需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。

手动获取锁方法:lock.lock()。释放锁:unlock方法。并且需要配合tyr/finaly语句块来完成。

三、等待是否可中断

synchronized

不可中断,除非抛出异常或者正常运行完成。

 

Lock

可以中断的。

中断方式:

 

调用设置超时方法tryLock(long timeout ,timeUnit unit)

调用lockInterruptibly()放到代码块中,然后调用interrupt()方法可以中断

四、加锁的时候是否可以设置成公平锁

synchronized

只能为非公平锁。

 

lock:两者都可以的。默认是非公平锁。

在其构造方法的时候可以传入Boolean值。true:公平锁、false:非公平锁

 

五、锁绑定多个条件来condition

synchronized

不能精确唤醒线程。要么随机唤醒一个线程;要么是唤醒所有等待的线程。

 

Lock

用来实现分组唤醒需要唤醒的线程,可以精确的唤醒。

 

六、性能区别

synchronized

托管给JVM执行,Java1.5中,由于需要调用操作接口,可能导致加锁消耗时间过长,与Lock性比性能低。1.6以后,语义定义更加清晰,有适应自旋、锁粗化、锁消除、轻量级锁、偏向锁等,可进行许多优化,性能提高了,与Lock差不多。

Lock

java写的控制锁的代码,性能高。

 

公平锁和非公平锁

公平锁:先来先到,有排队的流程。

非公平锁:抢占式,抢的快的先走。

创建公平锁的方法: 创建显示锁时,在ReentrantLock()中将参数设置为true。

 

线程死锁:

解决方法:在任何有可能导致锁产生的方法里,不要再调用另外有可能产生锁的方法。

 

多线程通信问题(生产者与消费者问题):

对进行完成的部分进行沉睡,需要时再去唤醒这样的操作。

this.notifyAll()方法(唤醒)和this.wait()方法(沉睡)

线程的六种状态:

new,Runnable,Blocked,Waiting(无限等待),TimeWaiting(有时间限制的等待),Terminated(终止)

 

带返回值的线程Callable

 

Runnable 与 Callable

接口定义 //Callable接口

public interface Callable<V> { V

call() throws Exception;

}

//Runnable接口

public interface Runnable {

public abstract void run();

}

 

1. 编写类实现Callable接口 , 实现call方法

class XXX implements Callable<T> {

@Override

public <T> call() throws Exception {

return T; }

}

2. 创建FutureTask对象 , 并传入第一步编写的Callable类对象

FutureTask<Integer> future = new FutureTask<>(callable);

3. 通过Thread,启动线程

new Thread(future).start();

Runnable 与 Callable的相同点

都是接口

都可以编写多线程程序

都采用Thread.start()启动线程

 

Runnable 与 Callable的不同点

Runnable没有返回值;Callable可以返回执行结果

Callable接口的call()允许抛出异常;Runnable的run()不能抛出

 

Callable获取返回值

Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执

行,如果不调用不会阻塞

.isDone()方法,.cancel()方法(看API)

 

线程池:所有的线程池调用都需要ExecutorService类型

缓存线程池(长度无限制):

任务加入后的执行流程:

1.判断线程池是否存在空闲线程

2.存在则使用

3.不存在,则创建线程并放入线程池,然后使用

.execute()方法重写run()方法

 

定长线程池(长度是指定的数值):

任务加入后的执行流程:

1.判断线程池是否存在空闲线程

2.存在则使用

3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用

4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

 

单线程线程池:

执行流程:

1.判断线程池的那个线程是否空闲

2.空闲则使用

3.不空闲,则等待池中的单个线程空闲后使用

 

周期任务定长线程池:

执行流程

1.判断线程池是否存在空闲线程

2.存在则使用

3.不存在空闲线程,且线程池未满的情况下,则创建线程并放入线程池,然后使用

4.不存在空闲线程,且线程池已满的情况下,则等待线程池存在空闲线程

 

周期性任务执行时:定时执行,当某个时机触发时,自动执行某任务。

创建方式:ScheduledExecutorService service = Executors.newScheduledThreadPool();

定时执行

service.schedule()方法来执行

有三个参数

参数1.定时执行的任务,参数2.时长数字,参数3.时长数字的单位时间,TimeUnit的常量指定

 

service.scheduleAtFixedRate()

周期性执行任务

参数1.任务,参数2.延迟时长数字(第一次执行在什么时间以后),参数3.周期时长数字(每隔多久执行一次)

参数4.时长数字的单位

 

Lambda表达式(函数式编程思想)

面向对象:创建对象调用方法解决问题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值