JAVA知识点全面总结8:线程
创始人
2024-05-29 17:45:25

八.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,时间片用完,被终止

②如何进行上下文切换

  • 需要保存当前线程的上下文(运行条件和状态)
  • 加载下一个占用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必须定义在同步代码块中,释放同步代码块中的锁(对象锁)

③代码举例

  • 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();}}}}
}

未更新

未更新

相关内容

热门资讯

27家!废止!福州仓山区教育局... 福州市仓山区教育局公告,27家民办机构办学许可证被废止。 根据《中华人民共和国民办教育促进法》...
《长江画传》新书发布,展现中国... 日前,《长江画传》新书发布暨“国家文化公园画传系列”出版座谈会在北京举行,这也代表着“国家文化公园画...
委政府驳斥美国关于委局势“不安... 据新华社加拉加斯1月10日电 委内瑞拉政府10日发表声明说,美国关于委局势“不安全”的说法不属实。声...
互动科学秀《疯狂实验室》在乌鲁... 1月10日晚,新疆乌鲁木齐市,沉浸式儿童剧——互动科学秀《疯狂实验室》在新疆人民剧场演出,悬浮沙滩球...
前谷歌、苹果研究员为New V...   资深人工智能研究员Andrew Dai表示,在谷歌DeepMind任职14年后,他近期已离职并将...