在java中使用多线程有三种方式。
1.继承Thread类;
2.实现Runnable接口;
3.匿名内部类;
第一种一般不用,因为java只允许单继承,万一这个业务类有父类就无法继承了。
第二种如下:
public class ThreadBusiness implements Runnable{
@Override
public void run() {
service();
}
public void service(){
System.out.println("start");
System.out.println("end");
}
public static void main(String args[]){
Thread ttThread = new Thread(new ThreadBusiness());
ttThread.start();
}
}
这种写法对应的业务类通常比较复杂,类实现本身有很多的业务代码和方法。但又要求这个类具有并发的特征。
第三种如下:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
c.g();
}
});
这种一般用于并发处的代码比较简单,比如仅仅是对一个函数的调用,针对函数。因为如果并发类的内容很多,匿名内部类的可读性太差了。但是如果匿名类实现很简单,推荐这种写法,主要是类的数目变少了。
一旦定义了线程类,就可以调用这个线程的方法,比如start()。注意,线程只能开始一次,否则报错。
线程里的run方法本质是这个线程的执行入口,就像一个java程序的入口是main函数一样。
使用多线程可以方便程序设计,但是会带来一些问题。
尤其是在多线程共享变量的情况,典型的是对同一个具体线程的聚合。就是创建具有并发特性的业务类,然后把该业务类的实例当做是新建的线程实例的参数,运行多次。这每一个线程相当于是在调用同一个业务对象的业务方法。就会产生一些问题,比如覆盖写,一个写的线程读取了一个没有被另一个线程把最新值写回的值。那么之前的更新就会被覆盖。例子:
public class ThreadBusiness implements Runnable{
private int account = 0;
@Override
public void run() {
service();
}
public void service(){
System.out.println("start");
int i = account;
try {
Thread.sleep(1);//计算时间
i = i + 1;
} catch (InterruptedException e) {
e.printStackTrace();
}
this.account = i;
System.out.println("end");
}
public static void main(String args[]) throws InterruptedException{
ThreadBusiness t = new ThreadBusiness();
for(int i = 0; i < 10; i++){
Thread t1 = new Thread(t);
t1.start();
}
Thread.sleep(100);
System.out.println(t.getAccount());
}
public int getAccount() {
return account;
}
public void setAccount(int account) {
this.account = account;
}
}
执行了10次的自增操作,最后结果是2。在业务方法内部加入1毫秒的等待模拟业务执行过程。可以看到结果不对。这就是直接对共享变量多线程时存在的问题。
有个问题,上面的10个线程实际上是执行了业务线程10次,但是线程不知只能start一次吗?也就是业务线程为什么可以执行10次?
这个需要搞清楚run方法和start方法的区别。
run方法并不是基于并发的实现,而是顺序实现,即必须等待当前线程结束才会执行。start方法才可以开启线程。在start方法实现的内部,先做了并发的启动,然后才调用实际的run方法的。run只是描述了要做什么,并不能并发。
如果把上述程序的start改为run,结果就是:
可以看到,每一个线程都是顺序完成的。或者说,这里本身就没有多线程的概念,相当于一个for循环的顺序执行。要启动多线程,必须使用start方法。
标签:Java,Thread,语法,start,线程,run,多线程,public From: https://blog.51cto.com/u_15873544/5845109