多线程(基础)

进程:正在运行的一个程序

线程:由进程创建的,是进程的一个实体,线程也可以创建线程

一个进程可以拥有多个线程

一个java程序启动后它就是一个进程,它只提供资源装载的空间,具体的调度是由进程来完成的,一个java程序从main开始之后,进程启动,这个线程就是主线程。
进程仅仅是一个容器,进程不能运行,能运行的是进程中的线程。

并发:同一时间段内,多任务交替执行

并行parallel:同一时刻,多任务同时进行

创建线程的2种方法:

  1. 继承Thread类,重写run方法,实例化线程对象,调用start()方法,因为java是单继承,这种方法不适合共享资源

  2. 实现Runnable接口,重写run方法,实例化实现了接口的对象,实例化线程对象,调用start()方法,是一种代理模式

Thread是实现了Runnable接口的类,使用run支持多线程。无论使用Runnable还是Thread,都会new Thread,然后执行run方法。

start0()方法是底层方法,由JVM调用,真正实现多线程。

run方法是一个普通的方法,没有真正启动一个线程

Thread线程常用方法:

  • start启动线程

  • set/getName设置获取名字

  • currentThread获取当前线程对象

  • yield线程让步,当前运行--->就绪

  • join加入线程,当前运行--->阻塞

  • sleep线程休眠,当前运行--->阻塞,如果超:阻塞--->就绪

  • 线程优先级setPriority(1~10,10最高,5默认)

  • 执行方法run

JVM直接调用的是Thread类中的Run()方法

Thread(Runnable target, String name)

target接口对象,name线程的名字

线程生命周期

  • Thread.currentThread()获取当前线程

  • synchronized关键字,当某个对象被它修饰时,表明该对象在任一时刻只能被一个线程访问

new -→ ready --- running -→teminated

      timedwaiting

   waiting

   blocked

线程通信

wait⽅法与notify⽅法必须要在同步代码块或同步函数中使用,并由同⼀个锁对象调⽤。

wait⽅法与notify⽅法是属于Object类的⽅法的。因为:锁对象可以是任意对象。

  • wait() 让当前线程处于等待状态,并释放锁

  • notify() 唤醒某个等待中的线程

  • notifyAll() 唤醒所有等待中的线程

  • wait 是Object顶级类的方法,只能在同步方法或者同步块中使用。wait会释放锁,要用notify()唤起,时长大于等于sleep。

  • sleep 是Thread线程类的静态方法,可以在任何地方使用,不会释放锁,它也不需要占用锁。

  • BlockingQueue自带阻塞

单线程

同一时刻,只允许执行一个线程

多线程

同一时刻可以执行多个线程

  • ⼀个程序运⾏后⾄少有⼀个进程,⼀个进程中可以包含多个线程

  • 并发:指两个或多个事件在同⼀个时间段内发⽣,多任务交替执行。

  • 并⾏:指两个或多个事件在同⼀时刻发⽣(同时发⽣),需要多核cpu。

  • Runnable的实现方式是实现其接口 implements

  • Thread的实现方式是继承其 extends

  • run()方法只是一个类中的普通方法,调用run方法跟调用普通方法一样

  • 而start()方法是启动线程,它创建线程等一系列工作,然后自己调用run里面的任务内容。start0()真正的多线程

守护线程(Daemon Thread)

比如垃圾回收线程,就是最典型的守护线程。

Java程序入口就是由JVM启动main线程,main线程又可以启动其他线程。当所有线程都运行结束时,JVM退出,进程结束。

如果有一个线程没有退出,JVM进程就不会退出。所以,必须保证所有线程都能及时结束。

但是有一种线程的目的就是无限循环,例如,一个定时触发任务的线程:

如果这个线程不结束,JVM进程就无法结束。问题是,由谁负责结束这个线程?

然而这类线程经常没有负责人来负责结束它们。但是,当其他线程结束时,JVM进程又必须要结束,怎么办?

答案是使用守护线程(Daemon Thread)。

守护线程是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。

因此,JVM退出时,不必关心守护线程是否已结束。

如何创建守护线程呢?方法和普通线程一样,只是在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程:

在守护线程中,编写代码要注意:守护线程不能持有任何需要关闭的资源,例如打开文件等,因为虚拟机退出时,守护线程没有任何机会来关闭文件,这会导致数据丢失。

线程优先级

1 - 10级

1最低,10最高优先级。

线程的优先级越高 执行cpu的执行权越大 并不是一定会优先执行

synchronized重量级

  1. 同步代码块(性能好)

  2. 同步方法

同一个锁才会有互斥现象

类锁和静态方法锁是同一把锁

1、修饰普通方法(锁住的是当前实例对象)

  • 同一个实例调用会阻塞

  • 不同实例调用不会阻塞

2、同步代码块传参this(锁住的是当前实例对象)

  • 同一个实例调用会阻塞

  • 不同实例调用不会阻塞

3、同步代码块传参变量对象 (锁住的是变量对象)

  • 同一个属性对象才会实现同步

4、同步代码块传参class对象(全局锁)

  • 所有调用该方法的线程都会实现同步

5、修饰静态方法(全局锁)

  • 所有调用该方法的线程都会实现同步

对象锁(this)和类锁(class)区别?

1、对于静态方法,由于此时对象还未生成,所以只能采用类锁;

2、只要采用类锁,就会拦截所有线程,只能让一个线程访问。

3、对于对象锁(this),如果是同一个实例,就会按顺序访问,但是如果是不同实例,就可以同时访问。

4、如果对象锁跟访问的对象没有关系,那么就会都同时访问

当使用 synchronized 加锁 class 时,无论共享一个对象还是创建多个对象,它们用的都是同一把锁,而使用 synchronized 加锁 this 时,只有同一个对象会使用同一把锁,不同对象之间的锁是不同的。

Lock(更轻量)

1.lock Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作

2.方法

void lock() 获取锁对象

void unlock() 释放锁对象

死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。
由于线程被无限期地阻塞,因此程序不可能正常终止。

线程1
synchronized(obj1){
	synchronized(obj2){}
}

线程2
synchronized(obj2){
	synchronized(obj1){}
}

线程中常用方法

方法名称 方法描述
public fifinal void stop() 终止线程
public void interrupt() 中断线程(只是标记改变中断状态而已,它不会中断一个正在运行的线程。)

|
|public static void yield()

|暂停当前正在执行的线程对象,并执行其他线程(礼让)|
|public fifinal void join()|等待该线程终止(执行当前线程 再执行其它的线程)必须是在开启之后调用

|

Thread.interrupt的作用其实不是中断线程,而是通知线程应该中断了,如果线程处于正常活动状态,线程将继续正常执行,不受影响。

notify方法:

从类 java.lang.Object 继承的方法
用于唤醒在此对象监视器上等待的单个线程。

posted @ 2023-01-17 21:27  Rix里克斯  阅读(15)  评论(0)    收藏  举报