多线程
程序 :程序指的是为完成某种任务,用某种语言编写的一组指令的集合。
进程:指的是程序的一次执行过程,或者是正在运行的一个程序,也就是说当一个程序被运行,就开启了一个进程
进程可以进一步细化为线程,是一个程序内部执行的一条路径,也就是说一个进程可以有多个线程,一个线程就是一个指令流,cpu 调度的最小单位,由 cpu 一条一条执行指令,每个线程拥有独立的线程栈和程序计数器。
并行 :多个 cpu 同时执行多个任务。
并发 :一个 cpu 同时执行多个任务
多线程的创建:继承 Thread 类、实现 Runable 接口、实现 Callable 接口、线程池
(1) 继承 Thread 类:
该方式主要是创建 Thread 类的子类,并且重写 run 方法,而 run 方法就是线程代码执行体,创建对应的 Thread 类的子类对象即创建线程,最后调用 start()方法即可开启线程
(2) 实现 Runable 接口:
该方式主要是创建 Runable 接口的实现类,并实现 run 方法,而 run 方法就是线程代码执行体,然后创建 Runable 实现类的对象,并且将该对象传入到 Thread 对象中,最后 Thread对象调用 start 方法即可开启线程。
(3) 实现 Callable 接口:
该方式主要是创建 Callable 接口的实现类,并且实现 call 方法,而 call 方法就是线程代码执行体,然后将对应实现类的对象传递到 FutureTask 对象中,并且将 FutureTask 对象传递给 Thread 对象,最后 Thread 对象调用 start 方法即可开启线程。需要注意的是 Callable 接口方式可以获取到返回值即 call 方法有返回值。
(4) 线程池:
如果我们频繁的去创建线程销毁线程,那么必将会造成很大的开销,对此我们在实际开发中,一般都是通过线程池去创建线程。
Thread的常见方法:
start():1.启动当前线程 2.调用线程中的 run 方法
run():通常需要重写 Thread 类中的此方法,将创建的线程要执行的操作声明在此方法中
currentThread():静态方法,返回执行当前代码的线程
getName():获取当前线程的名字
setName():设置当前线程的名字
yield():主动释放当前线程的执行权
join():在线程中插入执行另一个线程,该线程被阻塞,直到插入执行的线程完全执行完毕以后,该线程才继续执行下去
stop():过时方法。当执行此方法时,强制结束当前线程。
sleep(long millitime):线程休眠一段时间
isAlive():判断当前线程是否存活
Thread&Runable 区别 :
继承 Thread 类和实现 Runable 接口都可以去创建线程,这二者实际上并没有可比性, 它们只是接口和类的区别,同时也是继承关系。
线程同步 :
那么我们为了保证多线程所共享数据的不被破坏,我们就需要采取一定措施,也就是说一个线程在操作数据的时候其它线程不能参与进来,只能当前线程某个操作完成以后才可以让其它线程参与进来即线程同步。为了保证线程的同步,Java 提供了一些关键字用于保证线程的同步性。
1、Synchronized
如果我们要保证多线程的操作安全,那么我们可以通过 Synchronized 去同步代码块或者方法从而保证线程的安全。
(1) 同步代码快
同步代码块就是通过 Synchronized 关键字将需要待同步的代码给放到指定代码块里面,并使用同步锁去控制同步
同步方法
同步方法和同步代码块是一样的都是保证线程相应代码同步的一种机制,只是同步代码块是将待同步的代码放在代码块中而同步方法则是将待同步代码放在方法里面,如果我们要去使用同步方法只需要在方法前面加一个 Synchronized 关键字即可。
synchronized 的作用:即线程的原子性、有序性、可见性等
原子性:保证被 synchronized 修饰的一个或者多个操作,在执行的过程中不会被任何的因素打断,即所谓的 原子操作 ,直到锁被释放。
有序性: synchronized保证了每个时刻都只有一个线程访问同步代码块,也就确定了线程执行同步代码块是分先后顺序的,保证了有序性。
可见性:为了确保所有线程能够看到共享变量的值是最新的,所有执行读操作或写操作的线程都必须在同一个锁上同步。
Lock 锁:Lock锁提供了的比synchronized关键字更加灵活的锁操作。实现手动加锁、手动解锁。
Lock的常见的主要方法:
lock()获得锁
unlock()释放锁
lock&Synchronized 区别
1. lock 是一个接口,而 synchronized 是 java 的一个关键字,synchronized 是内置的语言实现;
2. lock 等待锁过程中可以用 interrupt 来终端等待,而 synchronized 只能等待锁的释放,不能响应中断;
3.lock 可以通过 trylock 来知道有没有获取锁,而 synchronized 不能;
4. Lock 可以提高多个线程进行读操作的效率。(可以通过 readwritelock 实现读写分离)
死锁 :在使用多线程时,两个或两个以上的线程,各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源
解决死锁
(1)改变加锁顺序
(2)开放调用
(3)定时锁
线程通信:一个线程可以去完成属于自己的任务,如果我们想要多个线程按照规则去协同执行任务就需要使用到线程通信
如果线程之间采用 synchronized 来保证线程安全,则可以利用wait()、notify()、notifyAll()来实现线程通信。
Wait()用于将当前线程进入阻塞状态,并且释放锁,要抛出一个异常即InterruptException
Notify用于唤醒单线程
Notifyall用于唤醒所有线程
Wait()和notify必须放在synchronize同步方法或代码代码块里通过锁对象去调用wait和notify方法
Wait与sleep的区别
Sleep()不会释放锁,wait会释放锁
Sleep()到时间自动发唤醒,wait需要notify手动唤醒