看大佬的吧:
https://blog.youkuaiyun.com/z69183787/article/details/86229514
https://blog.youkuaiyun.com/kai46385076/article/details/97621035
1.创建线程有几种不同的方式?你喜欢哪一种?为什么?
有三种方式可以用来创建线程:
1.继承Thread类:在run中写要执行的代码,myThread.start();开启新线程
2.实现Runnable接口:
1.创建Runnable实现类的实例:定义Runnable接口的实现类,
并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
2.并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
//创建线程对象
//myThread是Runnable接口的实现类的对象
Thread t = new Thread(myThread, "小强");
3.调用线程对象的start()方法来启动线程。
3.线程池 JAVA线程池原理,执行流程
线程池:其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
使用线程池中线程对象的步骤:
- 创建线程池对象。
- 创建Runnable接口子类对象。(task)
- 提交Runnable接口子类对象。(take task)
- 关闭线程池(一般不做)。
// 创建线程池对象
//包含2个线程对象
ExecutorService service = Executors.newFixedThreadPool(2);
// 创建Runnable实例对象
MyRunnable r = new MyRunnable();
// 从线程池中获取线程对象,然后调用MyRunnable中的run()
service.submit(r);
// 注意:submit方法调用结束后,程序并不终止,
// 是因为线程池控制了线程的关闭。
// 将使用完的线程又归还到了线程池中
// 关闭线程池
//service.shutdown();
实现Runnable接口比继承Thread类所具有的优势:
- 适合多个相同的程序代码的线程去共享同一个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独立。
- 线程池只能放入实现Runable或Callable类线程,不能直接放入继承Thread的类。
2. 线程同步、JAVA中同步机制
1. synchronized :
同步代码块: synchronized 关键字可以用于方法中的某个区块中,表示对这个区块的资源实行互斥访问。
//synchronized(同步锁){
// 需要同步操作的代码
//}
Object lock = new Object();
synchronized (lock) {
。。。
}
**同步方法:**使用synchronized修饰的方法,就叫做同步方法,保证A线程执行该方法的时候,其他线程只能在方法外等着。
public synchronized void method(){
可能会产生线程安全问题的代码
}
2.Lock锁:
//public void lock() :加同步锁。
//public void unlock() :释放同步锁。
Lock lock = new ReentrantLock();
public void run() {
lock.lock();
//需要同步操作的代码
...
lock.unlock();
}
3.volatile
请谈谈 volatile 有什么特点,为什么它能保证变量对所有线程的可见性?
关键字 volatile 是 Java 虚拟机提供的最轻量级的同步机制。当一个变量被定义成 volatile 之后,具备两种特性:
保证此变量对所有线程的可见性。当一条线程修改了这个变量的值,新值对于其他线程是可以立即得知的。而普通变量做不到这一点。
禁止指令重排序优化。普通变量仅仅能保证在该方法执行过程中,得到正确结果,但是不保证程序代码的执行顺序。
链接1
链接2
3.线程的五大状态
原文链接:https://blog.youkuaiyun.com/peter_teng/article/details/10197785

1.新建状态(New):
当用new操作符创建一个线程时, 例如new Thread®,线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码。
2.就绪状态(Runnable)
一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。
处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。
3.运行状态(Running)
当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.
4. 阻塞状态(Blocked)
线程运行过程中,可能由于各种原因进入阻塞状态:
1>线程通过调用sleep方法进入睡眠状态;
2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
3>线程试图得到一个锁,而该锁正被其他线程持有;
4>线程在等待某个触发条件;
…
所谓阻塞状态是正在运行的线程没有运行结束,暂时让出CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。
5. 死亡状态(Dead)
有两个原因会导致线程死亡:
1) run方法正常退出而自然死亡,
2) 一个未捕获的异常终止了run方法而使线程猝死。
为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false。
本文详细探讨了Java中创建线程的三种主要方式,包括继承Thread类、实现Runnable接口及使用线程池。同时,文章深入讲解了线程同步机制,如synchronized关键字和Lock接口的使用,以及volatile关键字确保变量可见性的原理。最后,分析了线程的五种基本状态,帮助读者全面理解Java线程的生命周期。

被折叠的 条评论
为什么被折叠?



