答:这道题主要考察线程的属性和创建过程,可以这么回答。
可以使用 InheritableThreadLocal 来代替 ThreadLocal,ThreadLocal 和 InheritableThreadLocal 都是线程的属性,所以可以做到线程之间的数据隔离,在多线程环境下我们经常使用,但在有子线程被创建的情况下,父线程 ThreadLocal 是无法传递给子线程的,但 InheritableThreadLocal 可以,主要是因为在线程创建的过程中,会把
InheritableThreadLocal 里面的所有值传递给子线程,具体代码如下:
// 当父线程的 inheritableThreadLocals 的值不为空时
// 会把 inheritableThreadLocals 里面的值全部传递给子线程
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
四种:
1、继承Thread类,重写run()方法,
2、实现Runnable接口,实现run()方法
3、实现Callable接口,重写call()方法
与Runnable接口相比,可以实现的功能:提供返回值,抛出异常,拿到Future对象,获取异步的结果。
4、使用线程池。
Runnable接口更好
1、代码架构角度
具体的任务,run方法里面的任务,创建线程。从生命周期的角度看,实现Runnable接口可以实现解耦。
2、新建线程的损耗
Thread需要new,还要销毁;Runnable可以反复利用一个线程
3、Java不支持多继承
volatile的理解
用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。
注意:不能对已经启动的线程再次调用start()方法,否则会出现java.lang.IllegalThreadStateException异常。
2、就绪状态(Runnable)
处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线 程就绪队列(尽管是采用队列形式,事实上,把它称为可运行池而不是可运行队列。因为cpu的调度不一定是按照先进先出的顺序来调度的),等待系统为其分配 CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调 度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。
提示:如果希望子线程调用start()方法后立即执行,可以使用Thread.sleep()方式使主线程睡眠一伙儿,转去执行子线程。
3、运行状态(Running)
处于运行状态的线程最为复杂,它可以变为阻塞状态、就绪状态和死亡状态。
处于就绪状态的线程,如果获得了cpu的调度,就会从就绪状态变为运行状态,执行run()方法中的任务。如果该线程失去了cpu资源,就会又从运 行状态变为就绪状态。重新等待系统分配资源。也可以对在运行状态的线程调用yield()方法,它就会让出cpu资源,再次变为就绪状态。
当发生如下情况是,线程会从运行状态变为阻塞状态:
①、线程调用sleep方法主动放弃所占用的系统资源
②、线程调用一个阻塞式IO方法,在该方法返回之前,该线程被阻塞
③、线程试图获得一个同步监视器,但更改同步监视器正被其他线程所持有
④、线程在等待某个通知(notify)
⑤、程序调用了线程的suspend方法将线程挂起。不过该方法容易导致死锁,所以程序应该尽量避免使用该方法。
当线程的run()方法执行完,或者被强制性地终止,例如出现异常,或者调用了stop()、desyory()方法等等,就会从运行状态转变为死亡状态。
4、阻塞状态(Blocked)
处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。阻塞的情况分三种:
(一)、等待阻塞:运行的线程执行wait()方法,JVM会把该线程放入等待池中。
(二)、同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁(synchronized)被别的线程占用,则JVM会把该线程放入锁池中。
(三)、其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。
在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行。
5、死亡状态(Dead)
需要说明的是,synchronized锁和调用wait()的对象应为同一对象!否则会报java.lang.IllegalMonitorStateException错误。
详细说明
线程是指程序在执行过程中,能够执行程序代码的一个执行单元。Java语言中,线程有4中状态:运行、就绪、挂起和结束。
进程是指一段正在执行的程序。
线程也被称为轻量级进程,是程序执行的最小单元,一个进程可以有多个线程,各个线程之间共享程序的内存单元和进程级资源,但是各个线程都有自己的栈空间。
为什么使用多线程?原因:
1、可以减少程序的响应时间。单线程情况下,某个操作很耗时,就会。。
2、线程的创建和切换开销更小。
3、多CPU或多核计算机本身就有执行多线程的能力。
4、简化程序结构,便于理解和维护。
同步:数据共享问题,当多个线程需要访问同一个资源时,需要确保某一时刻只能被一个线程使用,能够保证资源的安全。
异步:每个线程都包含了运行时自己所需要的数据或方法。在进程输入输出的时候,不必关心其他线程的状态或行为。
start()方法来启动一个线程,该线程处于就绪状态,可以被JVM来调度。JVM通过调用run()方法来完成实际的操作。(异步)
如果直接调用run()方法,则是普通的函数调用。(同步)
会出现IllegalThreadStateException异常;start()方法调用时候会先检查状态,不符合所以会抛出异常
(1)synchronized关键字
~~synchronized方法
~~synchronized块
(2)wait()方法与notify()方法
(3)Lock
~~lock()不会抛出异常
~~tryLock()
~~tryLock(long timeout,TimeUnit unit)
~~lockInterruptibly()
sleep()和wait()都是使线程暂停执行一段时间的方法。区别如下:
(1)原理
sleep()是Thread类的静态方法,是线程控制自身流程的
wait()方法是Object类的方法,用于线程间通信,其他线程调用notify()方法才“醒”来,也可以设定一个时间。
(2)锁的处理机制不同
sleep()不会释放锁,wait()会。
(3)使用区域不同
wait()只能在同步方法或块,sleep()在任何地方。
Sleep(0),如果线程调度器的可运行队列中有大于或等于当前线程优先级的就绪线程存在,操作系统会将当前线程从处理器上移除,调度其他优先级高的就绪线程运行;如果可运行队列中的没有就绪线程或所有就绪线程的优先级均低于当前线程优先级,那么当前线程会继续执行,就像没有调用 Sleep(0)一样。
1、sleep方法给其他线程运行机会时不考虑线程的优先级,因此会给低线程优先级运行的机会,而yield方法只会给相同优先级或者更高优先级线程运行的机会
2、线程执行sleep()方法后转入阻塞状态,所以,执行sleep()方法的线程在指定的时间内不会被执行,而yield()方法只是使当前线程重新回到可执行状态,所以执行yield()方法的线程可能在进入可执行状态后马上又被执行
3、sleep()方法声明抛出InterruptedException,而yield()方法没有声明任何异常
4、sleep()方法比yield()方法(跟操作系统相关)有更好的可移植性
stop()和suspend()方法
(1)用法
(2)性能
(3)锁机制
让调用该方法的线程在执行完run()后,再执行join方法后面的代码。
原理
static的方法属于类方法,它属于这个Class(注意:这里的Class不是指Class的某个具体对象),那么static获取到的锁,是属于类的锁。而非static方法获取到的锁,是属于当前对象的锁。所以,他们之间不会产生互斥。
https://blog.csdn.net/qq_33805483/article/details/104099367
AtomicInteger,原理CAS,unSafe类。
提供了线程池的创建类 ThreadPoolExecutor、Executors 等;
提供了各种锁,如 Lock、ReentrantLock 等;
提供了各种线程安全的数据结构,如 ConcurrentHashMap、LinkedBlockingQueue、DelayQueue 等;
提供了更加高级的线程同步结构,如 CountDownLatch、CyclicBarrier、Semaphore 等。
线程池
newCache是创建可扩容的带有缓存的一池多线程
newFixed是创建固定个数的线程池
shutdown:
1、调用之后不允许继续往线程池内继续添加线程;
2、线程池的状态变为SHUTDOWN状态;
3、所有在调用shutdown()方法之前提交到ExecutorSrvice的任务都会执行;
4、一旦所有线程结束执行当前任务,ExecutorService才会真正关闭。
shutdownNow():
1、该方法返回尚未执行的 task 的 List;
2、线程池的状态变为STOP状态;
3、阻止所有正在等待启动的任务, 并且停止当前正在执行的任务。
ScheduledThreadPoolExecutor 设置定时,进行调度,可以用来在给定延时后执行异步任务或者周期性执行任务。
public ScheduledThreadPoolExecutor(int corePoolSize, ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0,TimeUnit.NANOSECONDS, new DelayedWorkQueue(),threadFactory);
}
单例,通过单例工厂 DefaultSingletonBeanRegistry实现单例;
AsyncTaskExecutor保证并发的安全。
public static void main(String[] args) {
final String str=”abc”;
ExecutorService executorService= Executors.newFixedThreadPool(3);
executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“1”+str);
}
});executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“2”+str);
}
});executorService.execute(new Runnable() {
@Override
public void run() {
System.out.println(“2”+str);
}
});
}
ThreadLocal有什么用
Collections.synchronizedList() ConcurrentLinkedQueue
CAS基于jdk提供的原子类原语实现,例如AtomicReference
CAS
根据具体情况(sleep,wait,join等),酌情选择notifyAll,notify进行线程唤醒。
1
2