八.JAVA知识点全面总结8:线程
1.线程进程,并发并行,同步异步?
2.为什么引入多线程?出现的问题是什么?
3.线程的生命状态是什么?
4.上下文切换的意思?
5.线程死锁的概念?
6.sleep和wait的异同?wait,notify为什么不定义在thread类里?
7.thread的run如何理解?
8.创建线程的方式?
9.线程安全的方式?
10.原子性,可见性,有序性?volatile关键字?
11.ReentrantLock和synchronized的使用?
12.什么是乐观锁?什么是悲观锁?
13.ThreadLocal的作用是什么?
14.FutureTask和Runable和Callable的关系?
15.对比四种创建线程方式?
16.线程如何通信?
未更新
未更新
八.JAVA知识点全面总结八:线程
1.线程进程,并发并行,同步异步?
①线程进程
启动程序后,JVM启动了一个进程,而main函数启动了一个线程 同类的多个线程共享堆方法区资源 每个线程都有自己独立的程序计数器(保证程序切换后仍然能正确执行),虚拟机栈(局部变量的保存)
②并行并发
③同步异步
同步:调用后不返回等结果 异步:调用后直接返回不等待结果
2.多线程,出现的问题是什么?
①多线程
多核CPU意味着多线程可同时进行 减少进程切换的损失(进程切换开销很大)
②出现的问题
3.线程的生命状态是什么?
新建-就绪-运行-死亡 运行-阻塞-就绪(运行到阻塞可以利用sleep,wait)
4.什么时候进行上下文切换?如何进行上下文切换?
①什么时候进行上下文切换
②如何进行上下文切换
需要保存当前线程的上下文(运行条件和状态) 加载下一个占用cpu的线程上下午
5.线程死锁的概念?
①什么是线程死锁
线程1占用a资源等待b资源,线程二占用b资源等待a资源
②如何预防死锁
一次性申请全部资源 申请不到资源则释放已占用资源 按顺序使用资源
6.sleep和wait的异同?wait,notify为什么不定义在thread类里?
①异同
sleep 未释放锁 等待执行 超时自动苏醒 Thread静态方法 wait 释放了锁 线程通信 notify object本地方法
②wait notify定义在object本地方法中
重点:锁就是对象,故用锁.wait清晰知道等待哪把锁 Java提供的锁是对象级别的而不是线程级(每个对象都有锁),即wait在object类中 wait定义在thread中,wait等待的锁就不明显了,而wait定义在object中,wait等待的锁即为对象的锁 wait必须定义在同步代码块中,释放同步代码块中的锁(对象锁)
③代码举例
package threadTest;import java.lang.reflect.Method;public class WaitTest {public static void main(String[] args) {//锁就是对象,故锁.wait知道等待哪把锁//注意 wait必须是当前对象调用,而synchronized里面的锁可以不是当前对象//所以 只有当synchronized的锁时当前对象时,才可以直接调用wait,否则应该调用对象.wait//故两个线程用wait notify等待和唤醒,需要用同一把锁(锁时对象级别的)//如果定义在Thread类中,那么等待的是哪一把锁呢?Object o = new Object();Thread thread1 = new Thread(new MyWait("thread1",o));Thread thread2 = new Thread(new MyWait("thread2",o));thread1.start();thread2.start();
// thread1进入了wait等待
// thread2唤醒thread1
// 成功唤醒了}
}class MyWait extends Thread{Object o;String method;MyWait(String str,Object o ){this.method = str;this.o = o;}void thread1() throws Exception{synchronized (o){System.out.println("thread1进入了wait等待");o.wait();System.out.println("成功唤醒了");}}void thread2() throws Exception{synchronized (o){System.out.println("thread2唤醒thread1");o.notify();}}@Overridepublic void run() {Class aClass = this.getClass();Method declaredMethod = null;try {declaredMethod = aClass.getDeclaredMethod(method);declaredMethod.invoke(this);} catch (Exception e) {e.printStackTrace();}}
}
7.thread的run如何理解?
调用继承Thread接口的run并不是线程,只是普通方法(Callable的call也一样) start开启线程是就绪态
8.创建线程的方式?
①继承Thread
extends Thread 重写run方法 类作为参数传入线程,线程.start
②实现runnable
implements Runnable,重写run方法 类作为参数传入线程,线程.start
③实现callable
implements Callable,重写call方法 类作为参数传入futureTask 再作为参数传入线程,线程.start
④使用线程池
Executors.newFixedThreadPool(10);生成线程池 线程池.submit() 参数可以为extends Thread 参数可以为implements Runnable,implements Callable,FutureTask
⑤代码使用
package threadTest;import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;public class NewThreadMethodTest {public static void main(String[] args) {thread1 thread1 = new thread1();thread2 thread2 = new thread2();thread3 thread3 = new thread3();//利用继承Thread创建的线程//利用实现Runnable创建的线程//利用实现Callable创建的线程new Thread(thread1).start();new Thread(thread2).start();new Thread(new FutureTask(thread3)).start();//利用继承Thread创建的线程//利用实现Callable创建的线程//利用实现Runnable创建的线程//利用实现Callable创建的线程()ExecutorService executorService = Executors.newFixedThreadPool(10);executorService.submit(thread1);executorService.submit(thread2);executorService.submit(thread3);executorService.submit(new FutureTask(thread3));}
}class thread1 extends Thread{@Overridepublic void run() {System.out.println("利用继承Thread创建的线程");}
}
class thread2 implements Runnable{@Overridepublic void run() {System.out.println("利用实现Runnable创建的线程");}
}
class thread3 implements Callable{@Overridepublic Object call() throws Exception {System.out.println("利用实现Callable创建的线程");return null;}
}
9.线程安全的方式?
①线程安全的方式
同步代码块 synchronized(){} 同步方法public synchronized void method(){}(默认锁时当前this,static默认锁时当前类.Class) 同步锁 ReentrantLock lock = new ReentrantLock(); lock.lock(); lock.unlock();
②三种实现线程安全
package threadTest;public class ThreadAnQuanTest {public static void main(String[] args) {//不加同步块num会小于0Ticket ticket = new Ticket();Thread thread = new Thread(ticket);Thread thread1 = new Thread(ticket);thread.start();thread1.start();}
}class Ticket extends Thread{int num = 100;synchronized void send1(){System.out.println(Thread.currentThread().getName() +"______"+ num);num--;}void send2(){while(num>0){synchronized (this){System.out.println(Thread.currentThread().getName() +"________"+num);num--;}}}@Overridepublic void run() {send2();}
}
package threadTest;import sun.awt.SunHints;import java.util.concurrent.locks.ReentrantLock;public class LockTest {public static void main(String[] args) {Tick tick = new Tick();Thread thread = new Thread(tick);Thread thread1 = new Thread(tick);thread.start();thread1.start();}
}
class Tick implements Runnable{int num = 0;ReentrantLock lock = new ReentrantLock();void buy1(){while(num<100) {//不加锁num会超过100lock.lock();System.out.println(Thread.currentThread().getName() + "_________"+num);num++;lock.unlock();}}@Overridepublic void run() {buy1();}
}
10.原子性,可见性,有序性?volatile关键字?
①三个性质
原子性: 例如 n++,经过三个步骤,取值,+1,赋值 如果多个线程执行,有可能两个都取值拿到同一值,故最终只加了一个一 可见性: 修改共享变量后立即更新到内存中,其他线程在内存中读取 有序性: 对代码进行(多核处理,前后无关的指令) 例如:if(finish){val = 1;finish = true;},如果指令重排finish到前面去,如果出现多线程,可能发生val不等于1的情况
②volatie关键字
保证可见性:线程直接从共享内存中存取数据 保证有序性:禁止指令重排 无法保证原子性
③代码例子
package threadTest;public class YuanZitest {public static void main(String[] args) {//由于num++不是原子操作,故结果应该为100,每次输入一个数//但是//96//98//99Test test = new Test();while(test.num<=100){new Thread(test).start();System.out.println(test.num);}}
}class Test extends Thread{int num=0;@Overridepublic void run() {num++;}
}
package threadTest;public class KeJianTest {private static boolean stop;public static void main(String[] args) throws Exception {//正常情况下如果可见,在线程B修改了stop值后,线程A应该结束循环,但是未结束//修改stop为volatile解决 ,A结束循环new Thread(() -> {System.out.println("Ordinary A is running...");while (!stop) ;System.out.println("Ordinary A is terminated.");}).start();Thread.sleep(10);new Thread(() -> {System.out.println("Ordinary B is running...");stop = true;System.out.println("Ordinary B is terminated.");}).start();}
}
package threadTest;public class YouXuTest {private static boolean ready = false;private static int number;private static class ReaderThread extends Thread {@Overridepublic void run() {Thread.yield();while (!ready) {Thread.yield();}System.out.println(number);}}public static void main(String[] args) throws Exception {//多次执行后,如果ready到前面,则numer输出为0Thread t = new ReaderThread();t.start();number = 42;ready = true;t.join();String[] a = new String[]{"abc","cde"};YouXuTest.main(a);}
}
11.ReentrantLock和synchronized的使用?
①对比
synchronized 非公平锁,代码块,方法,自动加锁释放锁,JVM(操作系统级别) ReentrantLock 公平锁或非公平锁,变量,手动加锁释放锁,API(调用级别) 公平锁和非公平锁的区别 公平锁:多个线程按照申请锁的顺序去获得锁,直接进入等待队列,按顺序获取锁 非公平锁:多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进入等待队列
②ReentrantLock和Synchronized都是可重入式独占锁
可重入式和不可重入式 可重入:线程可以再次获得自己的内部锁(对同一线程而言,否则容易发生死锁)。同一个线程内部再次调用锁,可以调用。 不可重入:同一个线程内部再次调用所不可调用。 独占锁和共享锁 独占锁:排他锁(其他线程不可读不可写),加入排他锁后不能再加其他任何类型的锁。 共享锁:(其他线程可读不可写),还可以再加共享锁。
③底层原理
sychronized 依赖底层的Monitor,而wait/notify也依赖Monitor,故需要放在同步代码快中 ReentrantLock只是一个API函数的调用
12.什么是乐观锁?什么是悲观锁?
乐观锁:每次被访问不会出现问题,只需要提交验证之前读取的变量是否被修改即可(CAS) 可能会出现ABA问题,别人改完与现在相同。 会进行自旋操作,如果一直被修改则一直重读。 用于少量读的情况。 悲观锁:每次共享资源只要被同时访问则会出现问题,同步代码块和ReentrantLock。 每次只能被一个线程使用。 用于大量读的情况。
13.ThreadLocal的作用是什么?
各个线程间数据隔离 创建一个ThreadLocal变量,每个线程可以访问变量时都有自己的本地副本(get,set) 一个线程有一个ThreadLocalMap,其中key是ThreadLocl,value是Object
14.FutureTask和Runable和Callable的关系?
①介绍FutureTask
FutureTask可以查看任务,取消任务等,并且实现了Callable接口 FutureTask相当于对Callable实现封装 FutureTask本身不能创造线程,需要Thread类 FutureTask=任务+结果(run+get,get未运行完run会被阻塞,get异步获取执行结果) FutureTask仅执行一次
②代码测试
package threadTest;import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class FutureTest {public static void main(String[] args)throws Exception{//只执行了一次FutureTaskFutureTask futureTask = new FutureTask(new Mytest());for(int i = 0 ;i<100;i++){new Thread(futureTask).start();}//get不会先执行,先等待futureTask执行完成后,再执行getnew Thread(futureTask).start();Object o = futureTask.get();System.out.println(o);}
}class Mytest implements Callable{@Overridepublic Object call() throws Exception {System.out.println("执行一次FutureTask");return 1;}
}
15.对比四种创建线程方式? ### ①继承和实现接口(单继承)
继承:继承Thread类,通过Thread或者线程池启动 实现:实现Callable或Runnable接口,通过Thread或者线程池启动 继承 代码简单易懂 实现 能够继承别的类(因为单继承模式)
②Callable和Runnable两者区别
Callable:有结果抛异常,需要FutureTask封装个 Runnable:无结果不需要封装
③Thread启动和线程池启动
Thread:可以 继承,实现Runnable,封装FutureTask 线程池:可以输入Thread,继承,实现Runnable或Callable,封装FutureTask
16.线程如何通信?
①线程通信的三种方式
共享内存:volatile 消息传递:wait/notify 管道流:线程间数据传输
②消息传递代码(a与b交替卖票)
package threadTest;public class XiaoXiChuanDitest {public static void main(String[] args) {//注意 wait会释放同步代码块中的锁,故可以在一个同步代码块中设置wait和notifyTicket1 ticket1 = new Ticket1();new Thread(ticket1).start();new Thread(ticket1).start();}
}class Ticket1 extends Thread{int num = 100;@Overridepublic void run(){while(num>=0){synchronized (this){//注意 wait会释放当前线程的锁notify();System.out.println(Thread.currentThread().getName()+"卖了一张还剩"+num--);try {wait();} catch (InterruptedException e) {e.printStackTrace();}}}}
}
未更新
未更新