创建线程的两种方式
a.、继承Thread类
b、实现Runable接口
优缺点分析:Thread类实际上也是实现的runable接口,java中只能单继承,因此继承Thread类的话会有局限。
线程的启动顺序
使用多线程技术时,代码的运行结果与代码的执行顺序或者调用顺序无关。是cpu以一种不确定方式或者说是在不确定的时间调用线程中的run方法。
调用start方法时,只是告诉线程规划器该线程已处于就绪状态,可以调用该线程的run方法。因此执行start不代表该线程立即被执行,调用start方法的顺序不代表线程启动的顺序。
多次调用start方法时,会抛出IllegalThreadStateException。
this.getName()和Thread.currentThread().getName()
getName()方法为Thread类中的方法,实现Runable接口的线程对象不能调用此方法。
Thread.currentThread().getName()在两种实现线程的方式中都可以用。
两者是同一个线程对象时,返回结果是一样的。
当将一个Thread对象当做构造参数传递给另一个Thread对象时,this代表传递的这个对象的引用,Thread.currentThread()代表当前的执行线程对象的引用。
例如:
public classmyThreadextendsThread {
publicmyThread() {
System.out.println("构造函数:Thread.currentThread().getName()="+Thread.currentThread().getName());
System.out.println("构造函数:this.getName="+this.getName());
System.out.println("构造函数:this==Thread.currentThread()"+(this==Thread.currentThread()));
}
public voidrun() {
System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
System.out.println("this==Thread.currentThread()"+(this==Thread.currentThread()));
}
public static voidmain(String[] args) {
Thread thread1 =new myThread();
thread1.setName("b");
Thread thread =newThread(thread1);
thread.setName("a");
thread.start();
}
}
执行结果为:
构造函数:Thread.currentThread().getName()=main
构造函数:this.getName=Thread-0
构造函数:this==Thread.currentThread()false
a
b
this==Thread.currentThread()false
停止线程
a、Thread.stop() 此方法unsafe,已经被废止。因为强制一个线程停止有可能导致一些清理工作无法进行,另外一个情况就是对锁定的对象进行解锁,导致数据得不到同步的处理,出现数据不一致的情况。
b、建议使用Thread.interrupt(),它是给正在执行的线程打了个标记,并没有立刻停止。特殊情况:如果线程sleep后调用interrupt会报异常。如果先调用interrupt再进入睡眠,则会将睡眠之前的代码逻辑执行完,然后进入睡眠时抛出异常。
如果想立刻停止线程有两种方法:结合interrupted()判断是否已经停止,然后抛出异常;结合interrupted()判断是否已经停止,然后return。建议抛出异常,这样可以进行一些其他处理操作。更可控。使用return会造成污染。
c、使用退出标志
this.interrupted()和this.isInterrupted()
this.interrupted为静态方法,作用是判断当前线程是否是中断状态,执行后会将状态标志清除为false;
this.isInterrupted为实例方法,作用是判断Thread对象是否是中断状态。
suspend和resume线程暂停和恢复
这两个方法也已经被废弃。原因和stop相似,容易造成数据不一致,或者锁死公共资源。
yield方法
- 作用是让出cpu。但是让出多长时间不一定,有可能刚刚让出,又马上获得。
守护线程
java中线程分为用户线程和守护线程两种。守护线程通过setDaemon(true)来设置一个线程为守护线程,必需在调用start之前设置,否则抛出异常。作用是守护非守护线程,当进程中没有非守护线程了,守护线程会自动销毁。典型的守护线程有GC(垃圾回收器),servlet容器初始化后生成的服务线程。
获得线程的返回值
第一种方法:通过类变量和方法返回数
publicclassMyThreadextendsThread
{
privateString value;
publicvoidrun()
{
value ="通过成员变量返回数据";
}
publicstaticvoidmain(String[] args)throwsException
{
MyThread thread =newMyThread();
thread.start();
thread.join();
System.out.println("value:"+ thread.value);
}
}
通过join使线程执行从异步变成同步,等线程执行完后获取该线程的变量。
第二种方法:通过实现callable接口
importjava.util.concurrent.Callable;
publicclassMyThread2implementsCallable {
@Override
publicString call()throwsException {
String string="通过实现Callable借口返回";
returnstring;
}
}
publicstaticvoidmain(String[] args) {
ExecutorService executorService=Executors.newCachedThreadPool();
Callable callable=newMyThread2();
Future future=executorService.submit(callable);
try{
if(future.isDone()){
System.out.println(future.get());
}
}catch(Exception e) {
e.printStackTrace();
}
}
必须使用ExecutorService的submit方法来执行,返回一个Future对象。可以使用isDone()方法检测future是否完成,完成后可以调用get()方法获得future的值,如果直接调用get()方法,get()方法将阻塞值线程结束。
线程池
类的继承关系:
ThreadPoolExcuter--->AbstractExcuterService-->ExcuterService-->Excuter
构造器参数:
a、 corePoolSize 核心池线程数。创建线程池后,默认里面是空的,知道有任务进来,开始创建线程。可以调用pre方法预先生成corePoolSize个线程。创建的线程数大于corePoolSize时,则会把任务放进缓存队列中。
b、 maximumPoolSize最大线程数
c、 keepAliveTime 线程存活时长 。表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
d、unit:参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性。
e、workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
f、threadFactory:线程工厂,主要用来创建线程;
g、handler:表示当拒绝处理任务时的策略,有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务