当前位置: 首页 > 科技观察

线程、多线程和线程池,这些我终于都明白了

时间:2023-03-18 17:43:24 科技观察

1。启动线程的三种方式?1)继承Thread类,重写run()方法,在run()方法体中写入CompletedtasknewThread().start();2)实现Runnable接口,实现run()方法newThread(newMyRunnable()).start();3)实现MyCallable类的Callable接口,实现call()方法,使用FutureTask类包装Callable对象,使用FutureTask对象作为Thread对象的目标来创建和启动线程;子线程执行结束后调用FutureTask对象的get()方法获取返回值。FutureTaskft=newFutureTask(newMyCallable());newThread(ft).start();2、run()和start()方法的区别run()方法只是线程的main方法,和普通方法一样,不会创建新的线程。只有调用了start()方法才会启动一个新线程,新线程会调用run()方法,线程才会开始执行。3、如何控制某个方法允许的并发访问线程数?创建一个Semaphore变量,Semaphoresemaphore=newSemaphore(5,true);当方法进入的时候,请求一个信号,如果信号用完了,等到方法结束,释放一个信号,释放的信号可以被新的线程使用。4、Java中的wait和seelp方法中的wait()方法属于Object类。当调用此方法时,线程将放弃对象锁。对象调用notify()方法后,线程才会进入对象锁池,准备获取对象锁。进入运行状态。sleep()方法属于Thread类。sleep()使程序暂停执行一段指定的时间并让出CPU,但其监控状态仍然保留。当指定的时间到了,它会回到运行状态。sleep()方法中的线程并没有释放对象锁。5.说说对wait/notify关键字的理解,直接其他线程调用这个对象的notify()或notifyAll()方法。调用wait()或notify()/notifyAll()方法时,必须锁定竞争资源,一般放在synchronized(obj)代码中。调用obj.notify/notifyAll后,调用线程仍然持有obj锁,所以等待线程虽然被唤醒,但仍然无法获得obj锁。直到调用线程退出synchronized块,释放obj锁,其他等待线程才有机会获得锁继续执行6.什么原因导致线程阻塞?一般线程阻塞1)线程执行Thread.sleep(intmillsecond)方法,让出CPU,休眠一段时间,一段时间后恢复执行;2)线程执行一段同步代码,但无法获取到相关的同步锁只能进入阻塞状态,获取到同步锁后才能恢复执行;3)线程执行了某个对象的wait()方法,直接进入阻塞状态,等待其他线程执行notify()/notifyAll()操作;4)线程执行一些IO操作进入阻塞状态,因为等待相关资源,如System.in,但是没有接收到键盘输入,则进入阻塞状态。5)线程礼遇,Thread.yield()方法,暂停当前正在执行的线程对象,将执行机会交给同等或更高优先级的线程,但不会使线程进入阻塞状态,线程仍在在可执行状态下,CPU时间可能随时被重新分配。线程自关闭,join()方法,当当前线程调用另一个线程的join()方法时,当前线程进入阻塞状态,直到另一个线程运行完毕,当前线程由阻塞变为就绪状态。6)线程执行suspend()使线程进入阻塞状态,必须调用resume()方法使线程重新进入可执行状态7、如何关闭线程?1)使用标志位2)使用stop()方法,但是该方法就像关闭电脑电源一样,可能会出现意想不到的问题3)使用中断interrupt()publicclassThread{//中断当前线程publicvoidinterrupt();//判断当前线程是否被中断publicboolenisInterrupt();//清除当前线程的中断状态,并返回之前的值publicstaticbooleninterrupted();}但是调用interrupt()方法只是传递中断请求报文,并没有并不意味着应该立即停止目标线程。8.说说java中的同步方法之所以需要synchronized,是因为在多线程并发控制中,当多个线程同时操作一个共享资源时,如果不采用同步机制,数据会不准确,所以需要加一个同步锁,保证在线程完成操作之前被其他线程调用,从而保证变量的唯一性和准确性。1)synchronized修饰同步的代码块或方法由于java的每个对象都有一个内置锁,当方法被this关键字修饰时,内置锁会保护整个方法。调用该方法前需要获取内置锁,否则会处于阻塞状态。2)volatile修饰的变量保证了变量在线程间的可见性。线程每次要访问volatile修饰的变量时,都是从内存中读取,而不是从缓存中读取,这样每个线程访问的变量都是一样的。并使用内存屏障。3)ReentrantLock可重入锁,其常用方法有ReentrantLock():创建一个ReentrantLock实例lock()获取锁unlock()释放锁4)使用局部变量ThreadLocal实现线程同步,每个线程都会保存变量的副本副本之间是相互独立的,这样每个线程都可以随意修改自己的副本,而不会影响其他线程。常用方法ThreadLocal()创建线程局部变量;get()返回本线程本地的当前线程拷贝变量;initialValue()返回本线程局部变量当前线程的初始值;set(Tvalue)设置当前线程变量线程副本中的值使用原子变量设置为值,例如AtomicInteger。常用方法AtomicInteger(intvalue)创建一个具有给定初始值的AtomicInteger整数;addAndGet(intdata)原子地将给定值添加到当前值6)使用阻塞队列实现线程同步LinkedBlockingQueue9。如何保证线程安全?线程安全体现在三种方式上:1)原子性:提供互斥访问,同一时间只能操作一行和数据。JDK中提供了很多原子类,比如AtomicInteger\AtomicBoolean\AtomicLong,都是通过CAS实现原子化的。JDK提供的锁有两种:synchronized依赖JVM实现锁,在关键字的action对象范围内只能有一个线程同时操作。另一种是LOCK,它是JDK提供的代码级锁,依赖于CPU指令。代表是ReentrantLock。2)可见性:一个线程对主存的修改能被其他线程及时看到。JVM提供了synchronized和volatile。volatile的可见性是通过内存屏障和禁止重新排序实现的。volatile写时,在写操作后加入storebarrier指令,将localmemory中的共享变量值刷新到mainMemory中;在读操作过程中,在读操作之前添加一条加载指令,从内存中读取共享变量。3)有序性:指令不会被编译器重新排序。通过volatile、synchronized、Lock可以保证顺序。10、两个进程可以同时写或者读,能实现吗?如何防止进程同步?我觉得是可以实现的。比如两个进程读取日历进程数据是没有问题的,但是同时写应该会有冲突。使用共享内存可以实现进程间的数据共享。11、线程间操作List12、Java中对象的生命周期1)创建阶段(Created):为对象分配存储空间,开始构造对象,从超类到子类初始化静态成员;父类的成员变量依次初始化,递归调用父类的构造函数,子类的成员变量依次初始化,调用子类的构造函数。2)应用阶段(InUse):对象被至少一个强引用持有。3)不可见阶段(Invisible):程序操作已经超出了对象的范围4)不可到达阶段(Unreachable):对象不再被强引用持有5)收集阶段(Collected):假设对象重写finalize()方法并没有被执行,该方法将被执行。6)终结阶段(Finalized):对象在运行finalize()方法后仍处于不可达状态,等待垃圾回收器回收对象空间。7)对象空间重新分配阶段(De-allocated):垃圾收集器回收或重新分配对象占用的内存空间,对象彻底消失。13.静态同步方法的多线程访问和功能。静态同步控制对类的所有实例的访问。无论new多少个对象,都只有一份,所以类的所有对象都被加锁了。限制多线程中该类的所有实例同时访问JVM中该类对应的代码。14.同一个类中的两个synchronized方法,两个线程同时访问的问题如果synchronized修饰的是静态方法,锁是当前类的类对象,必须先获取当前类对象的锁输入同步代码;普通方法,锁是当前实例对象,进入同步代码前获取当前实例的锁;同步代码块,括号中的对象被锁定,给定对象被锁定,在进入同步代码块库之前必须获取给定对象。对象锁定;如果两个线程访问同一个对象的synchronized方法,就会产生竞争。如果它们是不同的对象,则它们不会相互影响。15.volatile的原理当写一个volatile变量修饰的共享变量时,会多出一段汇编代码,lockaddl$0x0,以lock为前缀的指令会将当前处理器缓存行的数据写回多核处理器。系统内存,这种写回内存的操作,会在其他CPU中使缓存在该内存地址的数据失效。同时,锁前缀也相当于一个内存屏障,限制内存操作的顺序。16.synchronized原则。Synchronized通过对象的对象头(markword)实现锁机制。java中的每一个对象都有一个对象头,可以为synchronized的实现提供基础,可以作为锁对象。在字节码层面,同步块通过Insertmonitorentermonitorexit完成同步。持有monitor对象,通过进入和退出Monitor对象来实现锁机制。17、谈谈对NIO的理解NIO(NewInput/Output)介绍了一种基于通道和缓冲区的I/O方法。它可以使用Native函数库直接分配堆外内存,然后通过一个DirectByteBuffer对象作为对这块内存的引用来操作存储在Java堆中,避免在Java堆和Native堆之间来回复制数据。NIO是一种同步非阻塞IO模型。同步是指线程不断轮询IO事件是否就绪,非阻塞是指线程在等待IO的同时可以同时做其他任务。同步的核心是Selector,代替线程本身轮询IO事件,避免阻塞,减少不必要的线程消耗;非阻塞的核心是channel和buffer,当IO事件准备好后,就可以写入buffer,保证IO成功,没有线程阻塞等待。synchronized和volatile关键字的区别synchronized和Lock的区别ReentrantLock、synchronized和volatile的区别1)volatile:解决了变量在多线程间的可见性,但是不能保证原子性,只能用于不阻塞修改变量.volatile可以屏蔽编译指令的重排,不会把后面的指令安排到内存屏障之前的位置,也不会把前面的指令安排到内存屏障的后面。主要用于并行计算的单例模式。volatile规定CPU每次都必须从内存中读取数据,不能从CPU缓存中读取,这样就保证了多线程在多CPU计算中总是得到最新的值。2)synchronized:互斥锁,操作互斥,并发线程过来,串行获取锁,串行执行代码。它解决了多线程之间访问共享资源的同步,可以保证原子性,间接保证可见性,因为它会同步私有内存和公共内存中的数据。可用于修改方法和代码块。会有堵塞。当synchronized发生异常时,线程持有的锁会自动释放,所以不会造成死锁。非公平锁每次都会互相竞争资源。3)lock是接口,synchronized是java中的关键字,synchronized是内置语言的实现。lock允许等待锁的线程响应中断。当异常发生时,如果不通过unLock()主动释放锁,可能会造成死锁现象。因此,在使用Lock时,需要在finally块中释放锁。4)ReentrantLock是可重入锁,锁分配机制是基于线程分配,而不是方法调用分配。ReentrantLock有一个tryLock方法。如果锁被另一个线程持有,则返回false以避免死锁。代码加锁的粒度会更小,节省资源,提高代码性能。ReentrantLock可以实现公平锁和非公平锁。公平锁是先到先得的资源。ReentrantReadWriteLock用于读多写少,读不需要互斥的场合。ReentrantLock内部实现锁原理死锁的四个必要条件?如何避免死锁?对象锁和类锁会相互影响吗?什么是线程池以及如何使用它?谈谈Java的并发、多线程、线程模型对线程的理解多线程需要注意哪些问题?说说你对并发编程的理解,并举例说明你对多线程同步机制的理解?如何保证多线程读写文件的安全?断点续传的原理实现5)并发编程相关知识点(这个在Android开发中很少用到,建议多看):通常在Android开发中,并发编程可以做的比较少,Thread类是经常用到就会用到,但是我们要想提高自己,就不能停留在表面上。java的线程相关的源码层面也要学习一下。