生成线程的方式分别为:
(1)继承Thread类
(2)实现Runnable接口
(3)实现Callable接口,通过Future得到线程执行信息
(4)通过线程池创建线程
1)通过继承Thread类实现对象
优点:
Thread类实现了Runnable接口,编写简单,如果需要访问当前线程,可以通过this获得
缺点:
java是单继承机制,扩展性降低
备注:
Synchronized如果锁的位置是方法上,锁的是当前传入的实例对象,如果是同步代码块上锁的是括号中配置的对象,可以是this当前实例对象,也可以是class对象,如果锁的是静态代码块,静态同步方法,锁的是当前类的class对象
public class ThreadTest{ public static void main(String[] args) { MyThread myThread1 = new MyThread(); MyThread myThread2 = new MyThread(); MyThread myThread3 = new MyThread(); myThread1.start(); myThread2.start(); myThread3.start(); } } class MyThread extends Thread{ @Override public void run() { synchronized (MyThread.class){ try { for(int i=0;i<10;i++){ System.out.println("i = "+i); } System.out.println(this.getName()+"---------"+this.getClass()); }catch (Exception e){ System.out.println(e.getMessage()); } } } }
2)实现Runnable接口
Runnable接口信息,只有一个无返回类型的润抽象方法
Runnable任务中的run无返回值,run方法不可以抛出异常
public class ThreadTest { public static void main(String[] args) { MyThread myThread = new MyThread(); Thread thread0 = new Thread(myThread); Thread thread1 = new Thread(myThread); Thread thread2 = new Thread(myThread); thread0.start(); thread1.start(); thread2.start(); } } class MyThread implements Runnable{ @Override public void run() { synchronized (this){ System.out.println(this); for(int i=0;i<10;i++){ System.out.println("i ="+i); } System.out.println(Thread.currentThread().getName()); } } }
3)实现callable接口
Callable接口可以有返回值,返回值必须有Future对象去接收,其可以抛出异常,根据接口信息也可以看出,可以通过FutureTask的get方法得到子线程的最终运行结果
public class ThreadTest { public static void main(String[] args) { FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() { private int i = 0; @Override public Integer call() throws Exception { for(;i<10;i++){ System.out.println("i = "+i+"------"+Thread.currentThread().getName()); } return i; } }) ; for(int i=0;i<10;i++){ System.out.println(Thread.currentThread().getName()+"==="+i); if(i==2){ Thread thread = new Thread(task,"的线程有返回值"); thread.start(); } } try { System.out.println("call线程返回的最终值为:"+task.get()); }catch (Exception e){ System.out.println(e.getMessage()); } } }
4)通过线程池创建线程
线程池创建线程的方法有很多种,jdk自带的Executors也可以创建线程,但Executors有如下弊端(参考于阿里开发手册):
1)FixedThreadPool和SingleThreadPool
允许请求的队列长度近乎无限大,可能堆积大量请求,导致oom
2)CacheThreadPool和ScheduleThreadPool
允许创建的线程数近乎无限大,可能会创建大量线程,导致oom
ThreadPoolExecutor参数为线程池大小,最大线程池容量,存活时间,队列容量大小,一旦提交的线程数超过当前可用线程数,会抛出拒绝执行异常,原因是有界队列满了便无法处理新的任务。
public class ThreadTest { public static void main(String[] args) { ExecutorService executorService = new ThreadPoolExecutor( 5,5,10,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(5)); for(int i=0;i<10;i++){ executorService.execute(new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+"运行线程池任务"); } }); } } }