Java.Thread Day01
文章总结自B站狂神说JAVA
1.什么是多线程
一个程序同时进行多个相同或者不同的任务。一个人吃饭时拉屎(不要脑补)也是多线程。
Java中允许应用程序同时执行多个线程,每个线程都有优先权。
2.创建多线程
2.1 继承Thread类
新建一个类继承Thread类
public class ThreadDemo01 extends Thread {
/**
* 重写run方法
*/
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run线程"+i);
}
}
}
在JAVA中多个线程开启后不一定会立即执行,由CPU调度执行。
public class ThreadDemo01 extends Thread {
/**
* run线程
*/
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run线程"+i);
}
}
/**
* main线曾
* @param args
*/
public static void main(String[] args) {
// 创建线程对象,调用start方法
ThreadDemo01 thread = new ThreadDemo01();
thread.start();
for (int i = 0; i < 2000; i++) {
System.out.println("main线程"+i);
}
}
}
上面的程序中,run线程不一定会调用start方法后立即执行,会根据cpu的调度执行。
idea生成for循环快捷键 xx.for
会生成:
//xx必须为数字
for (int i = 0; i < xx; i++) {
}
下载网图demo:
下载commons io 的 jar包apache官网下载
/**
* @date 2020/7/23 8:48
* 实现多线程下载图片
*/
public class ThreadDemo02 extends Thread {
private String url;
private String fileName;
public ThreadDemo02(String url,String fileName){
this.url = url;
this.fileName = fileName;
}
@Override
public void run() {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,fileName);
System.out.println("下载文件"+fileName);
}
public static void main(String[] args) {
ThreadDemo02 t1 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t1.jpg");
ThreadDemo02 t2 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t2.jpg");
ThreadDemo02 t3 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t3.jpg");
t3.start();
t2.start();
t1.start();
}
class WebDownloader{
public void downloader(String url,String fileName){
try {
FileUtils.copyURLToFile(new URL(url),new File(fileName));
}catch (IOException e){
e.printStackTrace();
System.out.println("IO异常");
}
}
}
}
这里运行顺序并不是根据调用start进程的顺序来运行的,而是根据CPU分配的。
2.2 实现Runnable接口
/**
* @date 2020/7/23 9:40
* 实现Runnable接口多线程
*/
public class ThreadDemo03 implements Runnable {
@Override
public void run() {
for (int i = 0; i < 30; i++) {
System.out.println("run线程运行ing..."+i);
}
}
public static void main(String[] args) {
// 创建一个是实现了Runnable接口的对象实例
ThreadDemo03 threadDemo03 = new ThreadDemo03();
// 创建一个Thread对象并且将实现了Runnable接口的实例对象传递进去。
Thread thread = new Thread(threadDemo03);
// 使用Thread对象调用start方法。
thread.start();
// 上面两行代码可以简写为:
new Thread(threadDemo03).start();
for (int i = 0; i < 3000; i++) {
System.out.println("main线程运行ing..."+i);
}
}
}
使用Runnable接口来实现多线程,在调用时与使用Thread类实现多线程有不同。由于Java的单一继承机制,使用改接口就可以避免继承机制的局限性,方便一个对象被多个线程调用。
所以推荐使用实现Runnale接口来实现多线程。
多线程同时操作一个对象:
/**
* @date 2020/7/23 9:40
* 实现Runnable接口多线程,多个线程操作一个对象。
*/
public class ThreadDemo04 implements Runnable {
private int ticketNums = 100;
@Override
public void run() {
while (true){
if (ticketNums < 0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到第"+ticketNums--+"票!!");
}
}
public static void main(String[] args) {
ThreadDemo04 threadDemo04 = new ThreadDemo04();
new Thread(threadDemo04,"吴彦祖").start();
new Thread(threadDemo04,"梁朝伟").start();
new Thread(threadDemo04,"彭于晏").start();
}
}
如果同时操作一个对象,线程不安全,数据紊乱。
比如:
梁朝伟抢到第49票!!
彭于晏抢到第49票!!
梁朝伟抢到第-1票!!
彭于晏抢到第-2票!!
这种问题称为并发问题。
2.3 实现callable接口
实现callable接口实现多线程需要重写call
方法,而不是run方法。
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,fileName);
System.out.println("下载文件"+fileName);
return true;
}
在调用时与上面连个的不同是callable使用的是创建服务提交服务的方法。
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadDemo02 t1 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t1.jpg");
ThreadDemo02 t2 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t2.jpg");
ThreadDemo02 t3 = new ThreadDemo02("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1595477956679&di=6086c0909a5a4499ab5784c42ea20183&imgtype=0&src=http%3A%2F%2Fb-ssl.duitang.com%2Fuploads%2Fitem%2F201901%2F17%2F20190117230425_eofqv.thumb.700_0.jpg","t3.jpg");
// 创建执行服务
ExecutorService service = Executors.newFixedThreadPool(3);
// 提交执行
Future<Boolean> r1 = (Future<Boolean>) service.submit(t1);
Future<Boolean> r2 = (Future<Boolean>) service.submit(t2);
Future<Boolean> r3 = (Future<Boolean>) service.submit(t3);
// 获取返回集
Boolean rs1 = r1.get();
Boolean rs2 = r2.get();
Boolean rs3 = r3.get();
// 关闭服务
service.shutdown();
}
相较于前面两个的好处是可以自定义返回值。