多线程
注意:线程开启不一定立即执行,由cpu调度执行
学习如何创建线程
Thread
- 自定义线程类继承Thread类
- 重写run( )方法,编写线程执行体
- 创建线程对象,调用start( )方法启动线程
package ye;
public class TestThread01 extends Thread{
@Override
public void run() {
//重写run方法 线程体
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) { //main线程,主线程
//这里如果想要开启另一个线程需要做的操作是:创建一个线程对象,调用start方法
TestThread01 testThread01 = new TestThread01();
testThread01.start();//调用start方法 程序是同时运行的(交替执行),计算机运行速度很快,观察不会很明显
for (int i = 0; i < 1000; i++) {
System.out.println("我在学习多线程"+i);
}
}
}
两种调用函数的区别,如果调用run()是一条线执行,调用start()则是多线交替执行!
案例:下载图片
package ye;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//联系Thread,实现多线程同步下载图片
public class TestThread2 extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名
public TestThread2(String url,String name){
this.url = url;
this.name = name;
}
@Override
public void run() {
WebDownLoader webDownLoader = new WebDownLoader();
webDownLoader.downLoader(url,name);
System.out.println("下载了文件名为"+name);
}
public static void main(String[] args) {
TestThread2 t1 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/ebf65c1852cabd5ccbf27620aa14d7b2.jpeg","1.jpg");
TestThread2 t2 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/905c6c494a1203e213daf7ec43e844b6.jpeg","2.jpg");
TestThread2 t3 = new TestThread2("https://img-blog.csdnimg.cn/img_convert/60d5727bbe0692f3f4fe46ba26560111.gif","3.jpg");//这里后缀名决定了文件的类型
t1.start();//3个线程同时执行,每次效果都不相同
t2.start();
t3.start();
}
}
//下载器
class WebDownLoader{
//下载方法
public void downLoader(String url,String name){
try {
FileUtils.copyURLToFile(new URL(url),new File(name));//文件工具包FileUtils
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoader方法出现问题");
}
}
}
实现Runnable
- 定义MyRunnable类实现Runnable接口
- 定义run()方法,编写线程执行体
- 创建线程对象,调用start()方法启动线程
package ye;
public class TestThread03 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在敲代码"+i);
}
}
public static void main(String[] args) {
TestThread03 testThread03 = new TestThread03();
//创建线程对象,通过线程对象来开启我们的线程,,代理
new Thread(testThread03).start();
for (int i = 0; i < 1000; i++) {
System.out.println("我在看电视"+i);
}
}
}
Runnable与Thread两者区别
看源码-> Thread继承了Runnable!!!
小结:
-
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动对象:子类对象.start()
- 不建议使用:避免oop单继承局限性
-
实现Runnable接口
- 实现接口Runnable具有多线程能力
- 启动线程:传入目标对象+Thread对象.start()
- 推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
怎么实现多个线程操作同一个对象
买火车票的例子:
package ye;
//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class TestThread04 implements Runnable{
private int ticketNums = 10;
@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--+"张票");//Thread.currentThread().getName() 解析 Thread.currentThread()返回值是Thread 通过getName()可以获取当前执行线程的名字。
}
}
public static void main(String[] args) {
TestThread04 ticket = new TestThread04();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛").start();
}
}
实现龟兔赛跑的例子
package ye;
public class Race implements Runnable{
//胜利者
private static String winner;//只有一个可以用static修饰
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
//模拟兔子休息,让它赢不了比赛
if(Thread.currentThread().getName().equals("兔子")&& i%40==0){
try {
Thread.sleep(1);//假设兔子每跑40米就休息1毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if(flag){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"m");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if (winner!=null){//已经存在胜利者
return true;
}{
if (steps >= 100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race = new Race();
new Thread(race,"乌龟").start();
new Thread(race,"兔子").start();
}
}