搞懂多线程(一)之基本概念

一、程序、进程、线程

  • 程序(program):为完成特定任务,用某种语言编写的`一组指令的集合`。即指一段静态的代码。

  • 进程(process):程序的一次执行过程,或是正在内存中运行的应用程序。程序是静态的,进程是动态的。进程作为操作系统调度和分配资源的最小单位。

  • 线程(thread):进程可进一步细化为线程,是程序内部的一条执行路径。线程作为CPU调度和执行的最小单位

  • 管程(monitor):为一个同步结构,具有线程互斥特性,以及能够根据某些条件来阻塞线程;简单来说Java中的锁就是管程,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成时释放管程。在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程

二、并行与并发

  • 并发(concurrency):指两个或多个事件在同一个时间段内发生,但是同一时刻只有一个任务在处理

  • 并行(parallel):指两个或多个事件在同一时刻发生(同时发生)。指在同一时刻,有多条指令多个CPU同时执行

三、用户线程和守护线程

  • 用户线程:用户自定义的线程,不受主线程影响

  • 守护线程:是在后台运行的,它的任务是为其他线程提供服务的,这种线程被称为守护线程。JVM的垃圾回收线程就是典型的守护线程。一旦主线程结束,守护线程也就结束了

    • 可以调用isDaemon()可以判断线程是否是守护线程

    • 调用thread.setDaemon(true)设置一个线程为守护线程

四、synchronized的锁

同步锁对象可以是任意类型,但是必须保证竞争“同一个共享资源”的多个线程必须使用同一个“同步锁对象”。

  • 静态方法加锁:锁对象是类的Class对象,而一个类的Class对象在内存中肯定只有一个

  • 非静态方法加锁:需要注意实例化对象只能实例化一次,否则synchronized是无效的

  • 同步代码块:synchronized (自定义指定),通常为this,需要注意指定的是不是唯一的

五、sleep()和wait()

相同点:一旦执行,都会使得当前线程结束执行状态,进入阻塞状态。

不同点

  • 定义方法所属的类:sleep():Thread中定义。 wait():Object中定义

  • 使用范围的不同:sleep()可以在任何需要使用的位置被调用; wait():必须使用在同步代码块或同步方法中

  • 都在同步结构中使用的时候,是否释放同步监视器的操作不同:sleep():不会释放同步监视器 ;wait():会释放同步监视器

  • 结束等待的方式不同:sleep():指定时间一到就结束阻塞。 wait():可以指定时间也可以无限等待直到notify或notifyAll。

  • wait()要写在while循环中,否则可能会引起虚假唤醒,因为wait()被唤醒之后,会接着当前方法继续向下执行

六、wait()和notify()/notify()使用细节

  • wait方法与notify方法必须要由同一个锁对象调用。因为:对应的锁对象可以通过notify唤醒使用同一个锁对象调用的wait方法后的线程。

  • wait方法与notify方法是属于Object类的方法的。因为:锁对象可以是任意对象,而任意对象的所属类都是继承了Object类的。

  • wait方法与notify方法必须要在同步代码块或者是同步函数中使用。因为:必须要通过锁对象调用这2个方法。否则会报java.lang.IllegalMonitorStateException异常。

七、synchronized与Lock

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域、遇到异常等自动解锁

  • Lock只有代码块锁,synchronized有代码块锁和方法锁

  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类),更体现面向对象。

  • (了解)Lock锁可以对读不加锁,对写加锁,synchronized不可以

  • (了解)Lock锁可以有多种获取锁的方式,可以从sleep的线程中抢到锁,synchronized不可以