文章目录
线程的概念
Java中线程的启动
继承Thread类型创建线程
- 定义Thread类的子类,并重写该类的run方法,该run方法的方法体就代表了线程要完成的任务。因此把run()方法称为执行体。
- 创建Thread子类的实例,即创建了线程对象。
- 调用线程对象的start()方法来启动该线程。
实现Runnable接口创建线程
- 定义runnable接口的实现类,并重写该接口的run()方法,该run()方法的方法体同样是该线程的线程执行体。
- 创建 Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调用线程对象的start()方法来启动该线程
通过Future和Callable创建线程
- 创建Callable接口的实现类,并实现call()方法,该call()方法将作为线程执行体,并且有返回值。
- 创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。
- 使用FutureTask对象作为Thread对象的target创建并启动新线程。
- 调用FutureTask对象的get()方法来获得子线程执行结束后的返回值
使用线程池例如Excutor框架(工厂的方法)
- 创建线程池
- 创建线程池的三大方式 Executors的静态方法
- Executors.newSingleThreadExecutor()
- Executors.newFixedThreadPool(n)
- Executors.newCachedThreadPool()
- 自行创建线程池的七大参数 new ThreadPoolExecutor
- 执行WxecutorServices.execute(Runnable command)
创建线程的方式的对比
- 采用实现Runnable、callable接口的方式创建线程
线程了只是实现了Runnble接口或者Callable接口,还可以继承其他类。在这种方式下,多个线程可以共享同一个target对象,所以非常适合多个相同线程来处理同一份资源的情况,从而可以将CPU,代码和数据分开。但是,缺点是编程稍微复杂,如果要访问当前线程,则必须使用Thread.currentThread
2、使用继承Thread类的方式创建多线程
如果需要访问当前线程,则无需使用Thread.currentThread()方法,直接使用this即可 获得当前线程。缺点是线程类已经继承了Thread类,就不能再继承其他父类了
3、Runnable和Callable的区别
- Callablle规定重写的方法是call(),Runnable规定(重写的方法)是run
- Callable的任务执行后可以返回值,而Runnavle的任务是不能返回值的
- call的方法可以抛出异常,run方法不可以
- 运行Callable任务可以拿到一个Future对象,表示异步计算的结果,它提供了检查计算是否完成的情况,可以取消任务的执行,还可获取执行结果
线程的状态
在Thread.state类中进行了定义
新建状态
表示刚刚创建的线程,这些线程还没有执行
可运行
可能正在运行,也饿能正在等待CPU时间片
阻塞
等待获取一个排他锁,其他线程释放了锁就会结束此状态
无线期等待
等待其他线程显示的唤醒,否则不会被分配CPU时间片
限期等待
:无须等待其他线程显示的唤醒,在一定时间之后会被系统自动唤醒
死亡
:可以是线程结束之后自己结束,或者产生了异常而结束
线程的常用方法
基本操作
Daemon
当所有非守护线程结束时,程序也就终止,同时会杀死所有守护线程
sleep
Thread.sleep(millsec)方法会休眠当前正在执行的线程,millsec单位是毫秒(==抱着锁睡觉)
yield 礼让
对静态方法thread.yield()的调用声明了当前线程已经完成了声明周期最重要的部分,可以切换给其他线程来执行
join
一直阻塞当前线程直到目标线程执行完毕
start
启动线程
new
创建线程
线程协作
wait notify() 和notifyall() (工作在同步代码块中-)
使用
wait()
挂起期间,线程会释放锁。这是因为,如果没有释放锁,那么其他线程就无法进入对象 的同步方法或同步控制块中,那么就无法执行notify
或者notifyAll
来唤醒挂起的线程,造成死锁工作流程
- 一个线程调用了object.wait(),它就会进入object对象的等待队列;这个等待队列中,可能会有多个线程,因为系统运行多个线程同时等待某个对象
- 当object.notify()被调用时,它就会从这个等待队列中,随机选择一个线程,并将其环形
- 方法不能随便调用,必须包含在对应的synchronized中
- 会自动释放锁
wait和sleep的区别
- 来自不同的类
- 关于锁的释放: wait会释放目标对象的锁,sleep不会释放任何资源
- 使用的范围的不同: wait必须工作在synchromnized中
3、await()、signal和singalAll()
- java.util.concurrent 类库中提供快乐Condition类来实现线程之间的协调
- 可以在Condition上调用await()来使得线程等待,其他线程调用singal()和singalAll()来唤性等待的线程
- 相比于wait这种等待方式,await可以指定等待的条件,因此更加灵活
class Data3{ // 资源类 Lock private Lock lock = new ReentrantLock(); private Condition condition1 = lock.newCondition(); private Condition condition2 = lock.newCondition(); private Condition condition3 = lock.newCondition(); private int number = 1; // 1A 2B 3C public void printA(){ lock.lock(); try { // 业务,判断-> 执行-> 通知 while (number!=1){ // 等待 condition1.await(); } System.out.println(Thread.currentThread().getName()+"=>AAAAAAA"); // 唤醒,唤醒指定的人,B number = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printB(){ lock.lock(); try { // 业务,判断-> 执行-> 通知 while (number!=2){ condition2.await(); } System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB"); // 唤醒,唤醒指定的人,c number = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void printC(){ lock.lock(); try { // 业务,判断-> 执行-> 通知 // 业务,判断-> 执行-> 通知 while (number!=3){ condition3.await(); } System.out.println(Thread.currentThread().getName()+"=>BBBBBBBBB"); // 唤醒,唤醒指定的人,c number = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }