生成线程的方式分别为:
(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()+"运行线程池任务");
}
});
}
}
}