Java中关于多线程的一些概念理解


进程:

是指内存中的一个应用程序,拥有独立的内存空间。一个进程可以包含多个线程。

线程:

是进程中的一个执行路径,共享一个内存空间(每个线程都拥有自己的栈空间,共用一份堆空间)

由一个线程调用的方法,那么这个方法也会执行在这个线程内。

线程调度 :

(1)分时调度: 所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。

(2)抢占式调度 :优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),。

Java使用的为 抢占式调度。 CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核新而言,某个时刻, 只能执行一个线程,而 CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是 在同一时 刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的 使 用率更高。

同步和异步

同步:各线程排队执行 , 效率低但是安全.

异步:各线程同时执行 , 效率高但是数据不安全.

并发与并行

并发:指两个或多个事件在指定的时间段内发生(比如一个小时的并发数量是多少) 并行:指两个或多个时间在同一时刻发生(强调的是同时发生)

实现多线程的方法,常用的有两个

(1)继承Thead类

public class MyThread extends Thead{ //需要重写run方法,run方法就是线程要执行的任务方法 public void run(){ 这里的代码 就是一条新的执行路径 这个执行路径的触发方式,不是调用run方法,而是通过thread对象的start方法来启动任务

} }

(2)实现接口Runnable

方法:Runnable run = new MyRunnable ();//创建一个任务对象,

Thread t = new Thread(run);//创建一个线程,并为其分配一个任务,

t.start;//调用start执行这个任务

public class MyRunnable implements Runnable{ public void run(){ 线程的任务 } }

实现Runnable,与继续Thread相比有如下优势

1、通过创建任务,然后给线程分配的方式来实现的多线程,更适合多个线程同时执行相同任务的情况 2、可以避免单继承所带来的的局限性,因为接口可以实现多个 3、任务与线程本身是分离的,提高了程序的健壮性 4、线程池技术接受Runnable类型的任务,不接受Thread类型的线程

其实还有一种不常用的创建线程的方法:使用Callable

Callable也是一个接口,使用它必须实现这个接口,

class MyCallable implements Callable<T> {
    @Override
    public <T> call() throws Exception {
        return T;
    }
}

//使用方法
         Callable<Integer> c = new MyCallable();
        FutureTask<Integer> task = new FutureTask<>(c);
        new Thread(task).start();

Callalble接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执 行,如果不调用不会阻塞。其中还有一个isDone()方法,判断这个线程的任务是否执行完毕;

还有一个cancel(传入true是表示要去取消)方法,取消这个正在执行的线程,会返回一个boolean型结果,true,是取消成功,返回false,大多情况是该线程已经执行完毕了,没办法在取消了。

Runnable 与 Callable的相同点

都是接口

都可以编写多线程程序

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

Runnable 与 Callable的不同点

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

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

线程休眠的sleep方法

sleep是一个静态方法,用类名.方法名(Thread.sleep)调用,休眠的是 当前的线程,一定是当前语句所在的线程。

线程的中断

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

中断的方法:可以给线程添加中断标记,用interrupt()方法,当在进行某些操作时,线程会不断检测自身,检测到中断标记后,会抛出中断异常,进入catch块,如果直接return,可以直接结束run方法,也就是结束线程了。

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

用户线程;当一个进程不包含任何的存活的用户线程时,进程结束 守护线程:守护用户线程的,当最后一个用户线程结束时,所有守护线程自动死亡 设置守护线程方法,在线程启动之前,调用setDaemon(true)方法即可

线程不安全的问题:

多个线程同时执行争抢一个数据,导致某个数据在判断时和使用时不一样,导致程序不符合预期

解决线程不安全问题可以利用synchronized,通过加锁的方式, 解决方案一:1同步代码块(隐式锁):锁住某几行需要加锁的代码 格式 synchronized(锁对象(只能是同一个对象)){ 里面需要排队进行的代码块 必须是同一个锁对象才能进行排队 } 解决方案二:同步方法(隐式锁),锁住这个方法,在方法中加入synchronized修饰符,锁的对象时是this(也就是调用该方法的对象),如果是static方法,那么锁就是类名.class 如 :

public synchronized void sale(){ 执行的代码 } 解决方案3 :显示锁,Lock 及其子类,ReentrantLock,这个方案更体现了面向对象的机制 在调用方法前锁住,方法结束前开锁

公平锁与非公平锁

公平锁:先来先得,也就是排队。实现方法是在创建显式锁对象时传入参数为true,就是公平锁 如 Lock lock= new ReentantLock(true),不传参数就默认是非公平锁 非公平锁:锁一解开,大家一块抢,谁抢到谁先用

线程死锁:一个线程在等待另一个线程完成:另一个线程在等待这个线程完成,两个线程互相等待,造成线程阻塞,程序卡住了。

显式锁和隐式锁的区别

1、层级不同,

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

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

2、使用方式不同;

Sync是隐式锁。使用的时候不需要手动去获取锁和释放锁,由系统自动完成

Lock是显示锁。我们使用者需要手动的获取和释放锁。如果没有释放锁,就有可能导致出现死锁的现象。手动获取锁方法:lock.lock()。释放锁:unlock方法。需要配合tyr/finaly语句块来完成。

3、是否可中断

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

Lock可以中断的。

4 、加锁的时候是否公平

Sync是非公平锁

lock:两者都可以的。默认是非公平锁。在其构造方法的时候可以传入Boolean值。true表示公平锁,false是非公平锁,不传参数默认非公平锁

线程的六种状态

NEW 表示线程刚被创建出来

RUNNABLE 表示线程正在运行

BLOCKED 表示线程正在阻塞状态下

WAITING 表示线程正在等待被唤醒

TIME_WAITING 表示线程正在等待被唤醒,如果超过了设定的时间还没被唤醒就自己醒来

TERMINATED表示线程已终止

多线程通信问题,生产者与消费者问题

多线程中,特别要注意这个问题,生产者生产的时候,消费者要处于休眠状态,等生产完后,唤醒消费者,生产者进入休眠状态,让等待消费者唤醒,循环往复。如果不这样容易产生数据错乱,程序不符合预期的情况。

休眠和唤醒可以采用Object里面的wait()和notify()方法

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/291103.html

(0)
上一篇 2022年10月15日
下一篇 2022年10月15日

相关推荐

发表回复

登录后才能评论