搞懂多线程(一)之基本概念
一、程序、进程、线程
程序(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不可以