多线程部分总结
阻塞式IO
一。IO请求的两个阶段:
1.等待资源阶段:IO请求一般需要请求特殊的资源(如磁盘、RAM、文件),当资源被上一个使用者使用没 有被释放时,IO请求就会被阻塞,直到能够使用这个资源。
2.使用资源阶段:真正进行数据接收和发生。
二。在等待数据阶段,IO分为阻塞IO和非阻塞IO。
1.阻塞IO: 资源不可用时,IO请求一直阻塞,直到反馈结果(有数据或超时)。
2.非阻塞IO:资源不可用时,IO请求离开返回,返回数据标识资源不可用
三。在使用资源阶段,IO分为同步IO和异步IO。
1.同步IO:应用阻塞在发送或接收数据的状态,直到数据成功传输或返回失败。
2.异步IO:应用发送或接收数据后立刻返回,数据写入OS缓存,由OS完成数据发送或接收,并返回成功或失败的信息给应用。
四。IOPS,即每秒钟处理的IO请求数量。IOPS是随机访问类型业务(OLTP类)很重要的一个参考指标
还有readLine()方法是阻塞式方法。还有ServerSocket的accept()方法。
1.对于synchronized同步代码快中的同步监视器
1.用对象 就是正在被共享的数据如:account money
2.this
3.类.class 这个是 静态方法中用的。没有this。
虽然Java程序允许任何对象作为同步监视器,但监视器的目的是:阻止两个线程对同一个共享资源进行并发访问,因此通常使用可能被并发访问的共享资源充当同步监视器。
2.Thread与Runnable与Callable
Thread每次创建线程都是new 一个Thread对象,所以多线程之间无法共享实例变量,而Runnable:多个线程共享一个target对象,这样非常适合多个相同线程来处理一份资源。
1)创建Callable接口实例+重写call();
2)ExecutorService pool = Executors.newCachedThreadPool()
Future result = pool.submit(实现类对象);
3)获取值:result.get();
4)停止服务:ser.shutdownNow();
3.Thread类常用方法
1.Thread.currentThread();获取当前线程 Thread.currentThread().getName() 获取当前线程的名字。
- 构造:new Thread() new Thread(“线程名字”); new Thread(target,“线程名字”);
- 线程对象.isAlive() 就绪、运行、阻塞返回true , 死亡或新建 返回false。
- Thread.activeCount() :计算线程存活的数量。
4.线程主动放弃所占用的资源
1.sleep();
- yield();
5.控制线程(join、sleep、yield、设置后台线程、改变优先级)
1.Thread.join():一个线程中使用该方法,该线程进入阻塞,直到被join()方法加入的线程执行完为止。
--join()
--join(long millins) :等待被join的线程执行最多为millins毫秒,若join的线程仍未结束,不再等待。
--join(long millins , int nanos)在加上nanos毫秒。(几乎不用)
2.Thread.sleep(long millins): 这在执行的线程暂停millis毫秒,进入阻塞状态。时间结束后进入就绪状态不是立刻进入运行。而且如果有锁的话,不释放锁。(wait()释放锁)
3.Thread.yield():然线程进入就绪状态,让系统重新调度,完全可能该线程执行yield后再次被调度重新执行,当某个线程执行yield()后允许具有相同优先级或者更高优先级的其他线程获得运行机会 。
4.设置后台线程也叫守护线程或精灵线程: 为其他线程服务 如 JVM的垃圾回收线程就是典型的后台线程。如果所有前台线程都死亡,那么后台线程自动死亡
线程对象.setDaemon(true) 设置该线程为后台线程。**注:**该方法一定是在start()方法前调用。
线程对象.isDaemon();判断当前是否为后台线程。
5.改变线程优先级:线程对象.setPriority(1~10) MAX_PRIORITY 10 MIN_PRIORITY 1 NORM_PRIORITY 5.
5.什么情况下线程会进入阻塞状态及解决办法,线程的生命周期(5个)
1.sleep() 主动放弃所占用的处理器资源。
2.线程调用了一个阻塞式IO
,方法返回前,该线程被阻塞。
3.线程试图获得一个同步监视器,但该监视器正在被其他线程所持有。
4.线程正在等待通知(notify)
5.程序调用了suspend()方法将该线程挂起。但该方法容易造成死锁,避免使用
解决方案:
1.sleep的时间走完,进入就绪状态(所以这里不是时间走完就直接执行,进入就绪,所以实际等待时间要超过sleep的睡眠时间)
2.线程调用阻塞式IO方法已经返回。
3.线程成功获得某个同步监视器。
4.其他线程发起了某个通知
5.正在悬挂的线程被调用了resume()方法。
注意:线程从阻塞状态只能进入就绪状态,无法直接进入运行状态。
就绪 ----------------->运行: 得到处理器资源 运行--------------->就绪:失去处理器资源(yield())
运行------------------>死亡:stop()但该方法容易发生死锁,不建议使用 、Exception、Error 或run()/call()执行完成。
6.synchronized与lock 区别与使用场景。
Lock:private final ReentrantLock lock = new ReentrantLock(); lock.lock() finally{lock.unlock()}
区别:1.synchronized 拥有同步代码快和同步方法,Lock只有代码快锁。
2.synchronized是隐式锁,Lock使显示锁(手动开启和关闭锁,别忘了关锁)。
3.使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
场景:Lock----同步代码块(已经进入了方法体,分配了相应资源)----同步方法(在方法体之外)
7.死锁的原因例子及解决方案
原因:当两个线程相互等待对方释放同步监视器是就会放生死锁。
例子:
public class DeadLockA extends Thread {
@Override
public void run() {
try{
System.out.println("LockA running");
while(true){
synchronized(Client.obj1){
System.out.println("LockA locked obj1");
//获取obj1后先等一会儿,让LockB有足够的时间锁住obj2
Thread.sleep(100);
System.out.println("LockA trying to lock obj2...");
synchronized(Client.obj2){
System.out.println("LockA locked obj2");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public class DeadLockB extends Thread {
@Override
public void run() {
try{
System.out.println("LockB running");
while(true){
synchronized(Client.obj2){
System.out.println("LockB locked obj2");
System.out.println("LockB trying to lock obj1...");
synchronized(Client.obj1){
System.out.println("LockB locked obj1");
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}
public class Client {
public static final String obj1 = "obj1";
public static final String obj2 = "obj2";
public static void main(String[] ars) {
new DeadLockA().start();
new DeadLockB().start();
}
运行结果:
LockA running
LockA locked obj1
LockB running
LockB locked obj2
LockB trying to lock obj1
LockA trying to lock obj2
public class TestClass {
public synchronized void method(TestClass clazz) {
System.out.println("TestClass method in");
clazz.method2();
System.out.println("TestClass method out");
}
public synchronized void method2() {
System.out.println("TestClass method2");
}
}
public class TestLock extends Thread {
private TestClass class1;
private TestClass class2;
public TestLock(TestClass class1, TestClass class2) {
this.class1 = class1;
this.class2 = class2;
}
@Override
public void run() {
class1.method(class2);
}
}
public class Client {
public static void main(String[] ars) {
TestClass classA = new TestClass();
TestClass classB = new TestClass();
new TestLock(classA, classB).start();
new TestLock(classB, classA).start();
}
}
运行结果:
TestClass method in
TestClass method out
解决方案:wait : 导致当前线程等待。直到其他线程调用该同步监视器的notify()或notifyAll()方法来唤醒线程。
notify : 唤醒在此同步监视器上的等待的单个线程
notifyAll: 唤醒在此同步监视器上的全部线程。
8.释放同步监视器的锁定
1.当前线程在同步代码快、同步方法执行结束。
2.当前线程在同步代码快、同步方法中遇到break,return 终止了该代码块、该方法的继续执行。
3…当前线程在同步代码快、同步方法中出现了未处理异常和Error。
4.执行了wait()
**注意:**不会释放锁:
1.Thread.sleep() Thread.yield()
2.其他线程调用了该线程的suspend()。
9.线程通信
wait : 导致当前线程等待。直到其他线程调用该同步监视器的notify()或notifyAll()方法来唤醒线程。
notify : 唤醒在此同步监视器上的等待的单个线程。
notifyAll: 唤醒在此同步监视器上的全部线程。
10.线程组(了解)
11.线程池
线程池的原理和好处,线程池的拒绝策略 。
提交target。
package com.itheima.test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class TestPool {
public static void main(String[] args) {
ExecutorService pool = Executors.newCachedThreadPool();
pool.submit(new Runnable(){
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println("王鑫亮大帅哥"+Thread.currentThread().getName()+i);
}
}
});
System.out.println("王鑫亮大帅哥"+Thread.currentThread().getName());
pool.shutdown();
}
结果:
王鑫亮大帅哥main
王鑫亮大帅哥pool-1-thread-10
王鑫亮大帅哥pool-1-thread-11
王鑫亮大帅哥pool-1-thread-12
王鑫亮大帅哥pool-1-thread-13
王鑫亮大帅哥pool-1-thread-14
王鑫亮大帅哥pool-1-thread-15
王鑫亮大帅哥pool-1-thread-16
王鑫亮大帅哥pool-1-thread-17
王鑫亮大帅哥pool-1-thread-18
王鑫亮大帅哥pool-1-thread-19
12 补充
1.双重检查 提高效率。
2.单例设计模式