进程和线程
-
程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念
-
进程是执行程序的一次执行过程,是一个动态的概念,是系统分配资源的单位
-
线程是CPU调度和执行的单位,一个进程至少有一个线程
创建线程的三种方式
继承Thread类(不建议使用,避免单继承局限性)
步骤
- 自定义线程类继承Thread类
- 重写run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
//创建线程方式一:继承Thread类,重写run()方法,调用start()方法开启线程
public class Hello extends Thread{
//线程入口点
@Override
public void run() {
//run()方法执行体
for (int i = 0; i < 1000; i++) {
System.out.println("这是子线程");
}
}
//main()线程,主线程
public static void main(String[] args) {
//创建线程对象
Hello thread = new Hello();
//调用start()方法开启线程,main()线程也同时往下运行,不会等待;如果改为thread.run();,那就是单线程了
//线程开启不一定立即执行,由CPU调度执行
thread.start();
for (int i = 0; i < 1000; i++) {
System.out.println("这是主线程");
}
}
}
练习:多线程下载图片
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//使用多线程同步下载图片
public class Hello extends Thread{
//定义属性和构造方法
String url;
String name;
public Hello(String url, String name) {
this.url = url;
this.name = name;
}
//重写run()方法
@Override
public void run() {
//线程执行体,在这儿创建下载器对象,参数由创建线程对象时传入
new Download().download(url, name);
System.out.println("下载文件名为:" + name);
}
//main主线程
public static void main(String[] args) {
//创建多个线程对象
Hello h1 = new Hello("https://img-blog.csdn.net/20160522165107051", "1.jpg");
Hello h2 = new Hello("https://img-blog.csdn.net/20160522165107051", "2.jpg");
Hello h3 = new Hello("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("下载出现异常");
}
}
}
实现Runnable接口(推荐使用)
推荐使用Runnable接口实现类,不仅避免单继承的局限性,还方便同一个对象被多个线程使用
//创建线程方式二:实现Runnable接口,重写run()方法,创建Thread对象调用start()方法开启线程
public class Hello implements Runnable{
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println("这是子线程");
}
}
public static void main(String[] args) {
Hello hello = new Hello();
//没有直接继承Thread类,因此需要手动创建Threa对象调用start()方法
new Thread(hello).start();
for (int i = 0; i < 1000; i++) {
System.out.println("这是主线程");
}
}
}
练习:抢火车票
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Hello implements Runnable {
//火车票的数量
int num = 10;
@Override
public void run() {
//此处不能用for循环,那样在break时有的线程会出现抢到了票但来不及打印出来的情况
while (true) {
//模拟延时
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (num <= 0) {
break;
}
System.out.println(Thread.currentThread().getName() + "抢到了第" + num-- +"张票");
//此处如果把num--;写在了下一行,会导致第10张票被抢3次
// num--;
}
}
public static void main(String[] args) {
Hello hello = new Hello();
new Thread(hello, "老师").start();
new Thread(hello, "学生").start();
new Thread(hello, "黄牛").start();
}
}
练习:龟兔赛跑
public class Hello implements Runnable {
boolean flag = false;
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//只要胜者出现了,就一直break,其他线程无法再继续跑
if (flag) {
break;
}
System.out.println(Thread.currentThread().getName() + "----->跑了" + i + "步");
//设置获胜者的判断,当出现胜者时,其他线程不可再继续进行,避免出现多个胜者
if (i == 100) {
flag = true;
System.out.println(Thread.currentThread().getName() + "赢了!");
}
}
}
public static void main (String[]args){
Hello hello = new Hello();
new Thread(hello, "兔子").start();
new Thread(hello, "乌龟").start();
}
}