有三种方式可以创建线程:继承Thread类、实现Runnable接口、应用程序可以使用Executor框架来创建线程池 实现Runnable接口这种方式更受欢迎,因为这个不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承,但Java不支持多继承,所以只能实现接口。同时线程池也是非常高效的,很容易实现和使用
所谓回调,就是客户程序C调用服务程序S中的某个方法A,然后S又在某个时候反过来调用C中的某个方法B,对于C来说,这个B就是回调方法。
启动线程有三种方式: 一、继承Thread类创建线程类 1.定义Thread类的子类,并重写该类的run()方法,run()方法的方法体就代表了线程要完成的任务。把run()方法称为执行体。 2.创建Thread子类的实例,即创建线程对象。 3.调用线程对象的start()方法启动该线程。
package com.thread; public class Test1 extends Thread{ int i = 0; public void run(){ for(;i < 100;i++){ System.out.println(getName() + " " + i); } } public static void main(String[] args){ for(int i = 0; i < 100; i++){ System.out.println(Thread.currentThread().getName() + " " "+ i); if(i == 20){ new Test1().start(); } } } }上述代码中Thread.currentThread()方法返回当前正在执行的线程对象。 getName()方法返回调用该方法的线程名字。 二、通过Runnable接口创建线程类 1.定义runnable接口的实现类,并重写该接口的run()方法。 2.创建Runnable实现类的实例,并依此实例作为Thread的target来创建Thread对象,该线程对象才是正真的线程对象。 3.调用线程对象的start()方法来启动该线程
package com.thread; public class test2 implements Runnable{ private int i ; public void run(){ for(;i < 100;i++){ System.out.println(getName() + " " + i); } } public static void main(String[] args){ for(int i = 0; i < 100;i++){ System.out.println(Thread.currentThread().getName() + " " + i); if(i == 20){ test2 t = new test2(); new Thread(t,"新线程1").start(); new Thread(t,"新线程2").start(); } } } }三、通过Callable和Future创建线程(用的较少) 1.创建Callable接口的实现类,并实现call()方法,该call()方法作为线程执行体,有返回值。 2.创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call()方法的返回值。 3.使用FutureTask对象作为Thread对象的target创建并启动新线程。 4.调用FutureTask对象的get()方法来获得子线程执行结束后的返回值。
CountdownLatch和Cyclicbarrier都能够实现线程之间的等待,但侧重点不同: 1.countDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,他才执行。 2.CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行; 3.CountdownLatch是不能够重用的,而Cyclicbarrier是可以重用的。
1.降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2.提高响应速度。当任务到达时,任务可以不需要等到线程创建救恩那个执行。 3.提高线程的可管理性,线程是稀缺资源,如果无限的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。
1.newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个线程,如果工作线程达到线程池初始化的最大数,则将提交的任务存入到池队列。 2.newCachedThreadPool创建一个可以缓存的线程池。工作线程的创建数量几乎没有限制(其实也有限制的,数目为Integer.MAX_VALUE)这样可灵活的往线程池中添加线程.如果长时间没有往线程池中提交任务,即如果工作线程空闲了指定的时间(默认60秒),则该工作线程将自动终止。终止后,如果你又提交了新的任务,则线程池重新创建一个工作线程。 3.newSingleThreadExecutor创建一个单线程化的Executor,即只创建唯一的工作者线程来执行任务,如果这个线程异常结束,会有另一个线程取代他,保证顺序执行,这是特色。单工作线程最大的特点是可保证顺序的执行各个任务,并且再任意给定的时间不会有多个线程是活动的。 4.newScheduleThreadPool创建一个定长的线程池,而且支持定时的以及周期性的任务执行,类似于Timer。
1.继承自Thread类 2.实现Runnable接口 3.即实现Runnable接口,也继承Thread类,并重写run方法
继承Thread类需要从Java.lang.Thread类派生一个新的线程类,重载它的run方法 实现runnable接口,重载Runnable接口中的run方法 实现Runnable接口更好,使用这个方式创建的线程可以处理同一资源,从而实现资源的共享
AQS其实就是一个可以给我们实现锁的框架。 内部实现的关键是:先进先出的队列、state状态;定义了内部类ConditionObject拥有两种线程那个模式独占模式和共享模式;在LOCK包中的相关锁(常用的有ReentrantLock、ReadWriteLock)都是基于AQS来构建,一般我们叫AQS为同步器。
线程池主要就是指定线程池核心线程数大小,最大线程数,存储的队列,拒绝策略,空闲线程存活时长。当需要任务大于核心线程数的时候,就开始增加线程池创建的线程数量,如果当线程数也达到最大,就开始执行拒绝策略,比如记录日志,直接丢弃,或者丢弃最老的任务
生产者和消费者在同一时间段内共用统一存储空间,生产者向空间里面生产数据,而消费者取走数据。 优点:支持并发,解耦。
可以使用synchronized、lock、volatile和ThreadLocal来实现同步
可以使用加锁,比如synchronized或者Lock,也可以使用Concurrent包下的原子类。
不安全。因为i++不是原子性操作。I++分为读取I值,对I值加1,再赋值给I++,执行期中任何一步都是有能被其他线程抢占的。