Java多线程01:线程的创建
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
进程是执行程序的一次执行过程,是一个动态的概念,是系统分配资源的单位
线程是CPU调度和执行的单位,一个进程至少有一个线程
创建线程的三种方式
1、继承Thread类(不建议使用,避免单继承局限性)
/**
* 1、自定义线程类继承Thread类
*/
public class Main extends Thread{
/**
* 2、重写run()方法,编写线程执行体,也是线程入口点
*/
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("这是子线程" + i);
}
}
/**
* main()主线程
*/
public static void main(String[] args) {
/**
* 3、子类创建线程对象,调用start()方法开启线程
* main()方法和run()方法会同时运行(但是线程开启不一定立即执行,由CPU调度执行)
* 如果只是调用thread.run()方法,那就是先执行run()方法再执行main()方法,那就是单线程了
*/
Main thread = new Main();
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("这是主线程" + i);
}
}
}
练习:多线程下载图片
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
/**
* 使用多线程同步下载图片
*/
public class Main extends Thread{
String url;
String name;
public Main(String url, String name) {
this.url = url;
this.name = name;
}
/**
* 多线程
*/
@Override
public void run() {
/**
* 线程执行体,在这儿创建下载器对象,参数由创建线程对象时传入
*/
new Download().download(url, name);
System.out.println("下载文件名为:" + name);
}
/**
* 主线程
*/
public static void main(String[] args) {
Main h1 = new Main("https://img-blog.csdn.net/20160522165107051", "1.jpg");
Main h2 = new Main("https://img-blog.csdn.net/20160522165107051", "2.jpg");
Main h3 = new Main("https://img-blog.csdn.net/20160522165107051", "3.jpg");
h1.start();
h2.start();
h3.start();
}
}
/**
* 创建下载器类
*/
class Download{
public void download(String url, String name){
try {
/**
* 使用common-io.jar包的FileUtils工具类下载,此处传入的参数,看JDK文档是URL类型,也就是URL类的对象
*/
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("下载出现异常");
}
}
}
2、实现Runnable接口(推荐使用)
推荐使用Runnable接口实现类,不仅避免单继承的局限性,还方便同一个对象被多个线程使用
/**
* 1、自定义线程类实现Runnable接口
*/
public class Main implements Runnable{
/**
* 2、重写run()方法
*/
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("这是子线程" + i);
}
}
/**
* main()主线程
*/
public static void main(String[] args) {
/**
* 3、先创建实现类对象
* 再创建代理类对象,最后调用start()方法开启线程
*/
Main main = new Main();
Thread thread = new Thread(main);
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("这是主线程" + i);
}
}
}
练习:抢火车票
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Main implements Runnable {
/**
* 火车票剩余数量
*/
int num = 10;
@Override
public void run() {
while (true) {
if (num <= 0) {
break;
}
/**
* 模拟延时
*/
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* Thread.currentThread().getName()方法获取当前线程的名字
*/
System.out.println(Thread.currentThread().getName() + "抢到了第" + num-- +"张票");
}
}
public static void main(String[] args) {
Main main = new Main();
/**
* 实现Runnable接口,可以多个线程调用同一个对象
*/
new Thread(main, "老师").start();
new Thread(main, "学生").start();
new Thread(main, "黄牛").start();
}
}
练习:龟兔赛跑
public class Main implements Runnable {
boolean flag = false;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
/**
* 谁赢了就结束比赛
*/
if (flag) {
break;
}
/**
* 延时模拟兔子每跑十步休息一次
*/
if (Thread.currentThread().getName() == "兔子" && i % 10 == 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "----->跑了" + i + "步");
if (i == 100) {
flag = true;
System.out.println(Thread.currentThread().getName() + "赢了!");
}
}
}
public static void main (String[]args){
Main main = new Main();
new Thread(main, "兔子").start();
new Thread(main, "乌龟").start();
}
}