创建线程的三种方式:
1.创建Thread的子类对象,子类必须重写run方法,调用start()方法开启线程。
new Thread() {
@Override
public void run() {
// 任务代码
}
}.start();
2.往Thread构造器中传入一个Runnable子类对象,调用start()方法开启线程。
new Thread(new Runnable() {
@Override
public void run() {
// 任务代码
}
}).start();
3.创建Callable子类对象,重写call方法,(call方法是有返回值的,返回值类型由Callable和FutureTask的泛型所指定)传入FutureTask构造器,再将FutureTask对象传入Thread构造器,调用start()方法开启线程。,通过FutureTask的对象调用get()方法获取任务的返回值,获取该值的前提是该任务已经执行完毕,否则会一直等待该任务完成,有join效果。
FutureTask<String> task = new FutureTask<String>(new Callable<String>() {
@Override
public String call() throws Exception {
//任务代码
return "";
}
});
Thread t=new Thread(task);
t.start();
sleep(long time):调用此方法的线程将会处于睡眠状态,它还是具有CPU执行权的,只不过程序在此停留,超时后,该线程又回到就绪状态,如果被打断,会出现编译时异常InterruptedException。此方法不会释放锁资源。
interrupt():调用此方法的线程将结束sleep状态并会抛出InterruptedException异常。
static void yiled():让当前线程让出cpu,当前线程从运行状态变成就绪状态。此方法不会释放锁资源。
join():在t1线程中使用t2.join()方法时,t1线程会处于冻结(阻塞)状态,直到t2线程任务全部完成t1才会处于就绪状态。
wait():执行此方法的线程处于冻结(阻塞)状态,被wait的线程将存储在线程池中。并且会释放锁资源,当此线程重新竞争到锁时,它从被冻结的位置开始执行。该方法定义在Object中。
notify():唤醒线程池中的一个线程(任意)。该方法定义在Object中。
notifyAll():唤醒线程池中的所有线程。该方法定义在Object中。
setDaemon(boolean flag):设置为true时,是守护线程。当所有前台线程执行完毕时,所有后台线程也会结束。
【注意】wait(),notify(),notifyAll()方法只能在同步代码块中使用,而且必须使用同一个线程锁进行等待和通知。在使用多线程时,应当注意循环执行任务的条件,cpu执行速度过快导致结果不明显,还应注意锁对象的踪迹,wait的条件,notify的条件等等。
ExecutorService 线程池接口:该接口的实现类的数据结构为无界队列,队列中存放任务对象。 就是一些线程的集合,线程的状态不 是死亡状态,当线程池接收外面的任务时,线程池的管理器会查看是否有空闲线程,如果有,就会将任务分配给它,如果没有,任务处于 等待队列中。通过线程池可以减少线程对象,从而减小内存开销。
ExecutorService pool = Executors.newSingleThreadExecutor();
//创建单个线程的线程池对象,线程池中只有一个线程。
使用pool.excute(任务对象);进行添加任务。添加完毕自动开启线程。该种方式创建的线程池对象时,任务是按添加任务的顺序执行,当前任务没有执行完毕时,不能执行下个任务。
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建2个线程的线程池对象,线程池中有2个线程。
使用pool.excute(任务对象);进行添加任务。添加完毕自动开启线程。使用此种方式创建的线程池,不能保证任务的执行顺序,也不能保证线程执行的任务对象不变。
注意:
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
System.out.print("Cat");
}
};
Thread t = new Thread(r) {
public void run() {
System.out.print("Dog");
}
};
t.start();
}
运行结果:Dog