Java多线程-01-线程的创建
作者:互联网
目录
一.线程简介
二.线程创建(重点)
Thread类
package 线程创建.Thread;
public class Thread_Test01 {
public static void main(String[] args) {
//main线程,主线程
//创建一个线程对象
MyThread t = new MyThread();
//调用start方法开启线程
t.start();
for (int i = 0; i < 2000; i++) {
System.out.println("当前游戏场景运行中"+i);
}
}
}
//创建线程的方式一:继承Thread类,重写run方法,调用start开启线程
class MyThread extends Thread{
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 2000; i++) {
System.out.println("后台加载下一游戏场景"+i);
}
}
}
主线程和子线程并行交替执行
练习:同时下载三张网络图片
需要用到工具类库:commons-io
下载方法参考博客:(54条消息) commons-io的下载和使用_唯有一片炙热的博客-CSDN博客_commons-io
导入到IDEA使用:
1.新建包lib
2.拷贝外部库到lib
3.右键lib,点击“添加为库...”选项
public class Thread_WebPictureDown_Test {
//main,主线程
public static void main(String[] args) {
//创建三个线程
//地址太长这里省略了
WebPictureDownLoad_Thread t1 = new WebPictureDownLoad_Thread("https:...", "1");
WebPictureDownLoad_Thread t2 = new WebPictureDownLoad_Thread("https:...", "2");
WebPictureDownLoad_Thread t3 = new WebPictureDownLoad_Thread("https:...", "3");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
//网图下载线程
class WebPictureDownLoad_Thread extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名
//构造器
public WebPictureDownLoad_Thread(String url, String name){
this.url = url;
this.name = name;
}
//网图下载线程的执行体
@Override
public void run() {
DownLoader downLoader = new DownLoader();
downLoader.downLoad(this.url, this.name);
System.out.println(name+"下载完毕");
}
}
//下载器
class DownLoader{
//下载方法
public void downLoad(String url, String name){
try {
FileUtils.copyURLToFile(new URL(url), new File(name));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常,downLoad方法出现问题");
}
}
}
Runnable接口(推荐)
具体原理在静态代理部分介绍
public class Runnable_Test {
public static void main(String[] args) {
//创建Runnable接口的实现类对象
LoadScene Scene_city_of_tear = new LoadScene("City_Of_Tear");
//创建线程对象,通过线程对象来开启我们的线程 (代理)
Thread thread = new Thread(Scene_city_of_tear);
thread.start();
for (int i = 0; i < 2000; i++) {
System.out.println("当前游戏场景运行情况:"+i);
}
}
}
//创建线程的方式二:实现Runnable接口,重写run方法,执行线程需要丢入Runnable接口实现类,调用start方法
class LoadScene implements Runnable{
private String levelName;
public LoadScene(String levelName){
this.levelName = levelName;
}
@Override
public void run() {
//run方法线程体
for (int i = 0; i < 2000; i++) {
System.out.println("后台加载场景:"+this.levelName+" 加载情况:"+i);
}
}
}
小结:对比Thread类和Runnable接口
实现Runnable接口的好处
1.接口可以多继承,更灵活方便
2.方便同一个对象被多个线程使用
初识并发问题
例子:买电影票
//《阿凡达2》就要上映了,电影院一共放出10张票,小明,小红,小玉都想去看(并且想看很多次!,所以他们都要抢很多票)
public class BuyTicket {
public static void main(String[] args) {
Ticket_Selling_Service Buy_AFanDa = new Ticket_Selling_Service("阿凡达2");
//第二个参数是线程的name,可用.getName()获得
new Thread(Buy_AFanDa, "小明").start();
new Thread(Buy_AFanDa, "小红").start();
new Thread(Buy_AFanDa, "小玉").start();
}
}
//售票服务
class Ticket_Selling_Service implements Runnable{
//电影的名字
private String movieName;
//阿凡达电影票总数
private int ticketNums_AFanDa = 10;
public Ticket_Selling_Service(String movieName){
this.movieName = movieName;
}
@Override
public void run() {
while(true){
if(this.ticketNums_AFanDa <= 0){
System.out.println("尊敬的用户"+Thread.currentThread().getName()+"你好,《阿凡达2》电影票已售完");
break;
}
//模拟延时(数据量小,cpu跑太快了,不便于测试)
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"抢到了"+this.movieName+"的第"+this.ticketNums_AFanDa-- +"张票");
}
}
}
发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
在后面的线程同步部分解决这个问题
龟兔赛跑
//龟兔赛跑,兔子比乌龟跑快,兔子每隔10米睡一次觉
public class Race_Test {
public static void main(String[] args) {
Race race = new Race();
new Thread(race, "兔子").start();
new Thread(race, "乌龟").start();
}
}
class Race implements Runnable{
//赛道总长100米
private static int TotalLength = 100;
//获胜者
private static String winner = null;
@Override
public void run() {
if(Thread.currentThread().getName().equals("兔子")){
//模拟兔子跑步,一轮循环加25米
for (int howFarHasRuned = 0; howFarHasRuned <= 100; howFarHasRuned = howFarHasRuned + 25){
//模拟兔子睡觉,每10米睡1纳秒
if(howFarHasRuned % 10 == 0){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("兔子-->已经跑了"+howFarHasRuned+"米");
//判断比赛是否已经结束了
if(GameOver(howFarHasRuned)) break;
}
}
else{
//模拟乌龟跑步,一轮循环加1米
for (int howFarHasRuned = 0; howFarHasRuned <= 100; howFarHasRuned++) {
System.out.println("乌龟-->已经跑了"+howFarHasRuned+"米");
//判断比赛是否已经结束了
if(GameOver(howFarHasRuned)) break;
}
}
}
//判断比赛是否结束
private boolean GameOver(int howFarHasRuned){
if(winner != null) return true;//已经有获胜者了
else{
if(howFarHasRuned >= 100){//获胜者产生
winner = Thread.currentThread().getName();
System.out.println("获胜者是-->"+winner);
}
}
return false;
}
}
Callable接口
静态代理
结婚的例子
//静态代理模式总节
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色
//好处
//代理对象可以做很多真实对象做不了的事情
//真实对象只需要专注于做自己的事情
//一下是我目前的理解:
//举个现实生活中的例子
//个人游戏开发者A是程序高手可是不会其他的
//那么A可以把音乐代理给音乐创作者B,把美术代理给美术创作者C
//A自己只需要专注于把程序部分做好就可以了
//简而言之:把专业的事情代理给专业的对象去做,每个对象专精于自己的事情
public class Marry_Test {
public static void main(String[] args) {
MarryPeople people_Alice = new MarryPeople("Alice");
WeddingCompany weddingCompany = new WeddingCompany(people_Alice);
weddingCompany.HappyMarry();
}
}
//结婚接口
interface Marry{
void HappyMarry();
}
//真实角色:结婚的人
class MarryPeople implements Marry{
private String name;
//构造器
public MarryPeople(String name){
this.name = name;
}
@Override
public void HappyMarry() {
System.out.println(this.name+"-->结婚");
}
}
//代理角色:婚庆公司帮助别人结婚
class WeddingCompany implements Marry{
//代理目标(实现了结婚接口的真实对象)
private Marry target;
//构造器
public WeddingCompany(Marry target){
this.target = target;
}
@Override
public void HappyMarry() {
before();
this.target.HappyMarry();//真实对象
after();
}
private void before() {
System.out.println("---结婚前---");
}
private void after() {
System.out.println("---结婚后---");
}
}
静态代理模式总节:
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色
好处:
代理对象可以做很多真实对象做不了的事情
真实对象只需要专注于做自己的事情
我目前的理解:
举个现实生活中的例子
个人游戏开发者A是程序高手可是不会其他的
那么A可以把音乐代理给音乐创作者B,把美术代理给美术创作者C
A自己只需要专注于把程序部分做好就可以了
简而言之:把专业的事情代理给专业的对象去做,每个对象专精于自己的事情
多线程中的静态代理
第一行代码是多线程中的静态代理
第二行代码是之前结婚的例子
Thread(代理对象)和蓝色框里的东西(真实对象)都实现了Runnnable接口
(这里用到了Lamda表达式,之后会介绍)
猜测:
Thread调用start()方法后会调用自己的run()方法 (具体怎么调的不太清楚)
在run()方法中调用真实对象的run()方法
Lamda表达式
推导Lamda表达式
使用前提:接口为函数式接口(接口只有一个方法且是抽象方法)
场景:名叫Apple的类只使用一次
逐步优化,把代码变简单
1.外部类
public class Lamda_Test01 {
public static void main(String[] args) {
Food apple = new Apple();
apple.lamda();
}
}
//外部类
class Apple implements Food{
@Override
public void lamda() {
System.out.println("吃了苹果");
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
2.静态内部类
public class Lamda_Test02 {
//静态内部类
static class Apple implements Food{
@Override
public void lamda() {
System.out.println("吃了苹果");
}
}
public static void main(String[] args) {
Food apple = new Apple();
apple.lamda();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
3.局部内部类
public class Lamda_Test03 {
public static void main(String[] args) {
//局部内部类
class Apple implements Food{
@Override
public void lamda() {
System.out.println("吃了苹果");
}
}
Food apple = new Apple();
apple.lamda();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
4.匿名内部类
public class Lamda_Test04 {
public static void main(String[] args) {
//匿名内部类
Food apple = new Food() {//new了一个继承Food接口但是没有名称的实现类
@Override
public void lamda() {
System.out.println("吃了苹果");
}
};
apple.lamda();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
5.用lamda简化
public class Lamda_Test05 {
public static void main(String[] args) {
//lamda
Food apple = ()->{
System.out.println("吃了苹果");
};
apple.lamda();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
6.简化大括号(当方法内只有一行语句时)
public class Lamda_Test05 {
public static void main(String[] args) {
//lamda
Food apple = ()->System.out.println("吃了苹果");
apple.lamda();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void lamda();
}
推导完毕
带参数的lamda表达式
public class Lamda_Test06 {
public static void main(String[] args) {
//lamda
Food apple = (int i)->System.out.println("吃了"+i+"个苹果");
apple.eat(3);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(int i);
}
1.简化参数类型
public class Lamda_Test07 {
public static void main(String[] args) {
//lamda
Food apple = (i)->System.out.println("吃了"+i+"个苹果");
apple.eat(3);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(int i);
2.简化小括号(函数只有一个参数时)
public class Lamda_Test08 {
public static void main(String[] args) {
//lamda
Food apple = i->System.out.println("吃了"+i+"个苹果");
apple.eat(3);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(int i);
}
两个参数,有多行语句的例子
public class Lamda_Test09 {
public static void main(String[] args) {
//lamda
Food apple = (name,i)->{
System.out.println(name+"吃了"+i+"个苹果");
System.out.println(name+"吃饱了");
};
apple.eat("Alice", 7);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(String name, int i);
}
使用举例
无参数
public class Lamda_Test {
public static void main(String[] args) {
//lamda
Food apple = ()->System.out.println("吃了苹果");
apple.eat();
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat();
}
一个参数
public class Lamda_Test {
public static void main(String[] args) {
//lamda
Food apple = i->System.out.println("吃了"+i+"个苹果");
apple.eat(7);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(int i);
}
多个参数,多行语句
public class Lamda_Test {
public static void main(String[] args) {
//lamda
Food apple = (name,i)->{
System.out.println(name);
System.out.println("吃了"+i+"个苹果");
};
apple.eat("Alice", 7);
}
}
//定义函数式接口
interface Food{
//隐式声明为 public abstract
void eat(String name, int i);
}
标签:lamda,Java,String,Food,void,class,线程,多线程,public 来源: https://www.cnblogs.com/FlyingFishStudio/p/16119902.html