Java中的多线程如何实现?
先来简单说一下,什么是多线程,举个很简单的例子,我们生活中一个人可以同时做几件事,这就是多线程啊,对应Java中的多线程也是这样,我们以前main方法中执行,其实我们java中还有一个守护进程gc垃圾回收,这个以后再说。一个进程中有很多个线程,这些线程可以同时执行,进程就相当于一个保护的角色,这些概念性的东西可以去百度百科看,这里就不多介绍了。主要还是看看如何实现多线程。主要有三种方法:
1.继承Thread类
继承这个类,我们需要重写run()
方法,然后开启线程用start()
方法,这个时候我们再来一个main()
方法,就可以看出来,这两个线程是同时的,交替进行:
public class Thread01 extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("我是run---"+i);
}
}
public static void main(String[] args) {
Thread01 th = new Thread01();
th.start();
for (int i = 0; i < 1000; i++) {
System.out.println("我是main"+i);
}
}
}
举个下载图片的例子吧:
pom.xml
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
下载的工具类:
public class DownloadUtils {
public void webdownload(String url , String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();
}
}
}
主类:
public class PictureDowload extends Thread {
private String url;
private String name;
public PictureDowload(String url, String name) {
this.url = url;
= name;
}
@Override
public void run() {
DownloadUtils du = new DownloadUtils();
du.webdownload(url,name);
}
public static void main(String[] args) {
PictureDowload p1 = new PictureDowload("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00264-919.jpg", "1.jpg");
PictureDowload p2 = new PictureDowload("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00049-3015.jpg", "2.jpg");
PictureDowload p3 = new PictureDowload("https:///70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2683403104,2904190188&fm=26&gp=0.jpg", "3.jpg");
p1.start();
p2.start();
p3.start();
}
}
2.实现Runnable接口
这个方法也是要重写run()
方法,不过启动线程的方法有点不同,需要用Thrad来代理Runnable 接口的实现类对象。这个是为什么呢?其实啊,第一种方法中没有介绍,Thread也是实现了Runnable接口的,如下:
class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
上面是Thread一部分源码而已,不过可以看出来也是实现了Runnable接口的
那么还是上面那个例子,我们 只需要改进一点点就可以启动多线程下载图片了,如下:
public class PictureDowload01 implements Runnable {
private String url;//网络图片路径
private String name;//下载后所取得文件名
public PictureDowload01(String url, String name) {
this.url = url;
= name;
}
@Override
public void run() {
DownloadUtils du = new DownloadUtils();
du.webdownloadpicture(url,name);
}
public static void main(String[] args) {
PictureDowload01 p1 = new PictureDowload01("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00264-919.jpg", "1.jpg");
PictureDowload01 p2 = new PictureDowload01("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00049-3015.jpg", "2.jpg");
PictureDowload01 p3 = new PictureDowload01("https://ns-strategy.cdn.bcebos.com/ns-strategy/upload/fc_big_pic/part-00423-3960.jpg", "3.jpg");
new Thread(p1).start();
new Thread(p2).start();
new Thread(p3).start();
}
}
是不是觉得很简单了啊,就是启动需要借助Thread而已,其余的一样,没什么复杂的。
那么既然有两种方法,我们怎么选择是不是一个问题,并不是随便来用的啊,下面我们来分析一下:、
- 继承Thread方法的局限性,是能单继承,一个对象不能实现被多个线程使用,是不是局限性很大啊,所以这里我不推荐大家用这种方法
- 实现Runnable接口的方法,突破了上面的局限性,一个对象可以被多个线程使用,很方便,我比较推荐这种写法,举个例子:
//一份资源
ThreadTest th = new ThreadTest();
//多个代理
new Thread(th,"m1").start();
new Thread(th,"m2").start();
new Thread(th,"m3").start();
是不是突然觉得好方便,突破了单继承的局限性,对不对
举个现实中的例子,来讲解一下这个(首先这个例子没有解决并发问题,这里只是单纯演示这个而已)
public class SoldTicket implements Runnable {
int ticketnum = 15;//票数
@Override
public void run() {
while (true){
if (ticketnum <= 0){
break;
}
System.out.println(Thread.currentThread().getName()+"买到了第" + ticketnum-- +"票");
}
}
public static void main(String[] args) {
SoldTicket st = new SoldTicket();
new Thread(st,"张三").start();
new Thread(st,"李四").start();
new Thread(st,"王五").start();
}
}
3.实现Callable接口
这种方法用的比较少具体使用我就不举例子了,就是实现Callable 接口,重写call方法,然后就是开启线程有点不一样,需要先开启服务,结束后还要关闭服务。其实这种开启多线程用的还是比较少,起码我用的很少,这里就不去举例子了,如果真的要用到,我们随时百度查一下用法,也是很贱的,没什么复杂的,重点掌握前两种方法,就足够使用了,推荐使用的是第二种