Java线程的三种写法
Thread
注意点:线程是根据CPU分配的时间片运行,有着不确定性。
线程的写法已:通过继承Thread类,并重写Thread中的run方法。
public class ThreadTest {
public static void main(String[] args) {
new Ta().start();
new Ta().start();
}
}
class Ta extends Thread {
public void run() {
for (int i = 100; i >0; i--) {
System.out.println(Thread.currentThread().getName()+"\t"+i);
}
}
}
程序运行结果如下:
在这个程序中发现Thread 是非共享模式运行的,每个线程一旦创建就会各自执行Ta类中的run()方法(重写),也就是说每个线程都有属于自己的100个数,所以出现了上图中的重复打印。
需要注意的是,类要继承Thread,并重写run()方法,在程序的入口使用“.start()”,来启动线程。
这样写的弊端是Java只支持单继承,在代码的维护上就显得很麻烦。
另外还可以通过新建对象来对线程命名,现将上面的代码修改如下如下:
Runnable
public class ThreadTest {
public static void main(String[] args) {
ThreadTest2 threadTest2 = new ThreadTest2();
new Thread(threadTest2, "线程1").start();
new Thread(threadTest2, "线程2").start();
}
}
class ThreadTest2 implements Runnable {
Integer x = 100;
Object lock = new Object();
public void run() {
while (true) {
synchronized (ThreadTest2.class) {
try {
Thread.sleep(500);
} catch (Exception e) {}
if (x > 0) {
System.out.println(Thread.currentThread().getName() + "\t" + x--);
} else break;
}
}
}
}
在这段代码中增加了对线程的命名,使用synchronized 关键字完成对资源共享的锁定,实现了Runnable接口,同时重写run()方法。
实现Runnable接口的方式同样是重写run()方法
实现效果如下:
Thread与Runnable
在两者的使用过程中,我们发现Thread与Runnable都对run()方法进行了重写,那么他们之间有什么关系吗?如下:
Thread类继承了根类,并实现了Runnable方法。
public class Thread extends Object implements Runnable
在实际的Thread类中的run()方法调了Runnable中run()方法,而Runnable中的run()方法是由其子类去完成具体的实现的,因而在选择使用继承Thread的方式后就得重写其中的run()方法。
观察此处往下的第二个代码块:当执行Thread中的run()方法时,若传入的target不为空时,就会执行target中的run()方法。即Thread实现了Runnale接口,并重写了含有run方法的子类中的run方法(这句话太拗口了)。
public interface Runnable {
public abstract void run();}
public class Thread extends Object implements Runnable{
Private Runnable target;
public Thread(Runnable target,String name){
init(null,target,name,0);
}
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
Java中规定了单继承,一旦选择继承Thread类就意味着不能将资源共享,具体可以参考第一个代码段。而Runnable则可以实现资源的有序共享。
代码规范
在使用Runnable时创建线程时我们用阿里代码规约扫描时,提示需要使用线程池而非线程,如图下图,线程池后面会写。
Callable
实现Callable接口,重写其的clall()方法。并使用FutureTask类来包装Callable实现类的对象,且以此FutureTask对象作为Thread对象的target来创建线程。
public class ThreadTest {
public static void main(String[] args) {
Callable<Integer> myCallable = new MyCallable();
FutureTask<Integer> futuretask = new FutureTask<Integer>(myCallable);
Thread thread = new Thread(futuretask);
thread.start();
}
}
class MyCallable implements Callable<Integer> {
private int i = 0;
public Integer call() {
int sum = 0;
for (int i= 100; i > 0; i--) {
System.out.println(Thread.currentThread().getName() + " " + i);
sum += i;
}
return sum;
}
}
从代码中可以看出线程的实现由run()变成了call(),call()方法有返回值,注意数据类型为引用数据类型Integer,在线程使用数据类型应该是引用数据类型 。这种写法对我这种懒人来说不如前两种来的快,所以我比较喜欢前两种写法,在实际使用中较多使用线程池即
ExecutorService ServiceTable = Executors.newFixedThreadPool();
三种线程的创建使用
在实际使用中,由于java单继承特性的限定,我本人一般使用Runnable。在测试线程时,当目的达到应该进行中断处理,使用Thread.interrupt(),Thread.stop()停止已过时,但其实这些都是假的。