当前位置: 首页 > 编程日记 > 正文

java多线程样例

这里我们做一个完整的样例来说明线程产生的方式不同而生成的线程的差别:

package debug;

import java.io.*;
import java.lang.Thread;


class MyThread extends Thread{
public int x = 0;

public void run(){
System.out.println(++x);
}
}

class R implements Runnable{
private int x = 0;
public void run(){
System.out.println(++x);
}
}

public class Test {
public static void main(String[] args) throws Exception{

for(int i=0;i<10;i++){
Thread t = new MyThread();
t.start();
}
Thread.sleep(10000);//让上面的线程执行完毕
R r = new R();
for(int i=0;i<10;i++){
Thread t = new Thread(r);
t.start();
}
}
}

上面10个线程对象产生的10个线程执行时打印了10次1。以下10个线程对象产生的10个线程执行时打印了1到10。我们把以下的10个线程称为同一实例(Runnable实例)的多个线程

下节我们将研究线程对象方法,还是那句话,一般文档中能够读到的内容我不会介绍太多
请大家自己了解。

线程对象的几个重要的方法

虽然线程对象的经常用法能够通过API文档来了解,可是有非常多方法只从API说明是无法具体了解的。

本来打算用一节的篇幅来把线程方法中一些重要的知识说完,但这样下来预计要非经常的篇幅,可能要用好几节才干说把和线程方法相关的一些重要的知识说完。

首先我们接基础篇(二)来说明start()方法。

一个线程对象生成后,假设要产生一个运行的线程,就一定要调用它的start()方法.在介绍这种方法时不得不同一时候说明run方法.事实上线程对 象的run方法全然是一个接口回调方法,它是你这个线程对象要完毕的详细逻辑.简单说你要做什么就你在run中完毕,而怎样做,什么时候做就不须要你控制 了,你仅仅要调用start()方法,JVM就会管理这个线程对象让它产生一个线程并注冊到线程处理系统中。

从表面上看,start()方法调用了run()方法,其实,start()方法并没有直接调用run方法.在JDK1.5曾经 start()方法是本地方法,它怎样终于调用run方法已经不是JAVA程序猿所能了解的.而在JDK1.5中,原来的那个本地start()方法被 start0()取代,另个一个纯JAVA的start()中调用本地方法start0(),而在start()方法中做了一个验证,就是对一个全局变量 (对象变量)started做检验,假设为true,则start()抛出异常,不会调用本地方法start0(),否则,先将该变量设有true,然后 调用start0()。

从中我们能够看到这个为了控制一个线程对象仅仅能执行成功一次start()方法.这是由于线程的执行要获取当前环境,包含安全,父线程的权限, 优先级等条件,假设一个线程对象能够执行多次,那么定义一个static 的线程在一个环境中获取对应权限和优先级,执行完毕后它在还有一个环境中利用原来的权限和优先级等属性在当前环境中执行,这样就造成无法预知的结果.简单说 来,让一个线程对象仅仅能成功执行一次,是基于对线程管理的须要。

start()方法最本质的功能是从CPU中申请还有一个线程空间来执行 run()方法中的代码,它和当前的线程是两条线,在相对独立的线程空间执行,也就是说,假设你直接调用线程对象的run()方法,当然也会执行,但那是 在当前线程中执行,run()方法执行完毕后继续执行以下的代码.而调用start()方法后,run()方法的代码会和当前线程并发(单CPU)或并行 (多CPU)执行。

所以请记住一句话[调用线程对象的run方法不会产生一个新的线程],尽管能够达到同样的运行结果,但运行过程和运行效率不同。

[线程的interrupt()方法,interrupted()和isInterrupted()]

这三个方法是关系非常密切并且又比較复杂的,尽管它们各自的功能非常清楚,但它们之间的关系有大多数人不是真正的了解。

先说interrupt()方法,它是实例方法,而它也是最奇怪的方法,在java语言中,线程最初被设计为"隐晦难懂"的东西,直到如今它的 语义不没有象它的名字那样准确。大多数人以为,一个线程象调用了interrupt()方法,那它相应的线程就应该被中断而抛出异常,事实中,当一个线程 对象调用interrupt()方法,它相应的线程并没有被中断,仅仅是改变了它的中断状态。

使当前线程的状态变以中断状态,假设没有其他影响,线程还会自己继续运行。

仅仅有当线程运行到sleep,wait,join等方法时,或者自己检查中断状态而抛出异常的情况下,线程才会抛出异常。

假设线程对象调用interrupt()后它相应的线程就马上中断,那么interrupted()方法就不可能运行。

由于interrupted()方法是一个static方法,就是说仅仅能在当前线程上调用,而假设一个线程interrupt()后它已经中断了,那它又怎样让自己interrupted()?

正由于一个线程调用interrupt()后不过改变了中断状态,它能够继续运行下去,在没有调用sleep,wait,join等法或自己抛 出异常之前,它就能够调用interrupted()来清除中断状态(还会原状)interrupted()方法会检查当前线程的中断状态,假设为 "被中断状态"则改变当前线程为"非中断状态"并返回true,假设为"非中断状态"则返回false,它不仅检查当前线程是否为中断状态,并且在保证当 前线程回来非中断状态,所以它叫"interrupted",是说中断的状态已经结束(到非中断状态了)isInterrupted()方法则只检查线 程对象相应的线程是否是中断状态,并不改变它的状态。

眼下大家仅仅能先记住这三个方法的功能,仅仅有真正深入到多线程编程实践中,才会体会到它们为什么是对象方法,为什么是类方法。

线程究竟什么时候才被中断抛出InterruptedException异常,我们将在提高篇中具体讨论。

[sleep(),join(),yield()方法]

在如今的环节中,我仅仅能先说明这些方法的作用和调用原则,至于为什么,在基础篇中无法深入,仅仅能在提高篇中具体说明。

sleep()方法中是类方法,也就是对当前线程而言的,程序猿不能指定某个线程去sleep,仅仅能是当前线程执行到sleep()方法时,睡 眠指定的时间(让其他线程执行).其实也仅仅能是类方法,在当前线程上调用.试想假设你调用一个线程对象的sleep()方法,那么这个对象相应的线程如 果不是正在执行,它怎样sleep()?所以仅仅有当前线程,由于它正在执行,你才干保证它能够调用sleep()方法。

原则:[在同步方法中尽量不要调用线程的sleep()方法],或者简单说,对于一般水平的程序猿你基本不应该调用sleep()方法。

join()方法,正如第一节所言,在一个线程对象上调用join方法,是当前线程等待这个线程对象相应的线程结束,比方有两个工作,工作A要耗时10秒钟,工作B要耗时10秒或很多其它。我们在程序中先生成一个线程去做工作B,然后做工作A。

new?B().start();//做工作B

A();//做工作A

工作A完毕后,以下要等待工作B的结果来进行处理.假设工作B还没有完毕我就不能进行以下的工作C,所以

B?b?=?new?B();

b.start();//做工作B

A();//做工作A

b.join();//等工作B完毕。

C();//继续工作C。

原则:[join是測试其他工作状态的唯一正确方法],我见过非常多人,甚至有的是博士生,在处理一项工作时假设还有一项工作没有完毕,说让当前工 作线程sleep(x),我问他,你这个x是怎样指定的,你怎么知道是100毫秒而不是99毫秒或是101毫秒?事实上这就是OnXXX事件的实质,我们不 是要等多长时间才去做什么事,而是当等待的工作正好完毕的时候去做。

yield()方法也是类方法,仅仅在当前线程上调用,理由同上,它主是让当前线程放弃本次分配到的时间片原则:[不是很必要的情况下,没有理 由调用它].调用这种方法不会提高不论什么效率,仅仅是减少了CPU的总周期上面介绍的线程一些方法,基于(基础篇)而言仅仅能简单提及.以后具体应用中我会结合 实例具体论述。

线程本身的其他方法请參看API文档.下一节介绍非线程的方法,但和线程密切相关的两[三]个对象方法:

[wait(),notify()/notifyAll()]

这是在多线程中很重要的方法。

关于这两个方法,有非常多的内容须要说明.在以下的说明中可能会有非常多地方不能一下子明确,但在看完本节后,即使不能全然明确,你也一定要回过头来记住以下的两句话:

[wait(),notify()/notityAll()方法是普通对象的方法(Object超类中实现),而不是线程对象的方法]

[wait(),notify()/notityAll()方法仅仅能在同步方法中调用]

[线程的相互排斥控制]

多个线程同一时候操作某一对象时,一个线程对该对象的操作可能会改变其状态,而该状态会影响还有一线程对该对象的真正结果.

这个样例我们在太多的文档中能够看到,就象两个操售票员同一时候售出同一张票一样.

线程A线程B
1.线程A在数据库中查询存票,发现票C能够卖出
class="left"2.线程A接受用户订票请求,准备出票.
3.这时切换到了线程B运行
4.线程B在数据库中查询存票,发现票C能够卖出
5.线程B将票卖了出去
6.切换到线程A运行,线程A卖了一张已经卖出的票

所以须要一种机制来管理这类问题的发生,当某个线程正在运行一个不可切割的部分时,其他线程不能不能同一时候运行这一部分.

象这样的控制某一时刻仅仅能有一个线程运行某个运行单元的机制就叫相互排斥控制或共享相互排斥(mutual exclusion)

在JAVA中,用synchornizedkeyword来实现相互排斥控制(临时这样觉得,JDK1.5已经发展了新的机制)

[synchornizedkeyword]

把一个单元声明为synchornized,就能够让在同一时间仅仅有一个线程操作该方法.

有人说synchornized就是一把锁,其实它确实存在锁,可是是谁的锁,锁谁,这是一个很复杂的问题.

每一个对象仅仅有一把监视锁(monitor lock),一次仅仅能被一个线程获取.当一个线程获取了这一个锁后,其他线程就仅仅能等待这个线程释放锁才干再获取.

那么synchornizedkeyword究竟锁什么?得到了谁的锁?

对于同步块,synchornized获取的是參数中的对象锁:

synchornized(obj){
//...............
}

线程运行到这里时,首先要获取obj这个实例的锁,假设没有获取到线程仅仅能等待.假设多个线程运行到这里,仅仅能有一个线程获取obj的锁,然后运行{}中的语句,所以,obj对象的作用范围不同,控制程序不同.

假如:

public void test(){
Object o = new Object();

synchornized(obj){
//...............
}
}

这段程序控制不了不论什么,多个线程之间运行到Object o = new Object();时会各自产生一个对象然后获取这个对象有监视锁,各自皆大欢喜地运行.

而假设是类的属性:

class Test{
Object o = new Object();
public void test(){

synchornized(o){
//...............
}
}
}

全部运行到Test实例的synchornized(o)的线程,仅仅有一个线程能够获取到监视锁.

有时我们会这样:

public void test(){

synchornized(this){
//...............
}
}

那么全部运行Test实例的线程仅仅能有一个线程运行.而synchornized(o)和synchornized(this)的范围是不同 的,由于运行到Test实例的synchornized(o)的线程等待时,其他线程能够运行Test实例的synchornized(o1)部分,但多 个线程同一时候仅仅有一个能够运行Test实例的synchornized(this).]

而对于

synchornized(Test.class){
//...............
}

这种同步块而言,全部调用Test多个实例的线程赐教仅仅能有一个线程能够运行.

[synchornized方法]

假设一个方法声明为synchornized的,则等同于把在为个方法上调用synchornized(this).

假设一个静态方法被声明为synchornized,则等同于把在为个方法上调用synchornized(类.class).

如今进入wait方法和notify/notifyAll方法.这两个(或叫三个)方法都是Object对象的方法,而不是线程对象的方法.如同锁一样,它们是在线程中调用某一对象上运行的.

class Test{
public synchornized void test(){
//获取条件,int x 要求大于100;

if(x < 100)
wait();
}
}

这里为了说明方法没有加在try{}catch(){}中,假设没有明白在哪个对象上调用wait()方法,则为this.wait();

假如:

Test t = new Test();

如今有两个线程都运行到t.test();方法.当中线程A获取了t的对象锁,进入test()方法内.

这时x小于100,所以线程A进入等待.

当一个线程调用了wait方法后,这个线程就进入了这个对象的歇息室(waitset),这是一个虚拟的对象,但JVM中一定存在这种一个数据结构用来记录当前对象中有哪些程线程在等待.

当一个线程进入等待时,它就会释放锁,让其他线程来获取这个锁.

所以线程B有机会获得了线程A释放的锁,进入test()方法,假设这时x还是小于100,线程B也进入了t的歇息室.

这两个线程仅仅能等待其他线程调用notity[All]来唤醒.

可是假设调用的是有參数的wait(time)方法,则线程A,B都会在歇息室中等待这个时间后自己主动唤醒.

[为什么真正的应用都是用while(条件)而不用if(条件)]

在实际的编程中我们看到大量的样例都是用?

while(x < 100)

wait();go();而不是用if,为什么呢?

在多个线程同一时候运行时,if(x <100)是不安全的.由于假设线程A和线程B都在t的歇息室中等待,这时还有一个线程使x==100了,并调用notifyAll方法,线程A继续 运行以下的go().而它运行完毕后,x有可能又小于100,比方以下的程序中调用了--x,这时切换到线程B,线程B没有继续推断,直接运行go(); 就产生一个错误的条件,仅仅有while才干保证线程B又继续检查一次.

[notify/notifyAll方法]

这两个方法都是把某个对象上歇息区内的线程唤醒,notify仅仅能唤醒一个,但到底是哪一个不能确定,而notifyAll则唤醒这个对象上的歇息室中全部的线程.

一般有为了安全性,我们在绝对多数时候应该使用notifiAll(),除非你明白知道仅仅唤醒当中的一个线程.

那么是否是仅仅要调用一个对象的wait()方法,当前线程就进入了这个对象的歇息室呢?事实中,要调用一个对象的wait()方法,仅仅有当前线程获取了这个对象的锁,换句话说一定要在这个对象的同步方法或以这个对象为參数的同步块中.

class MyThread extends Thread{
Test t = new Test();
public void run(){
t.test();
System.out.println("Thread say:Hello,World!");
}
}

public class Test {

int x = 0;
public void test(){
if(x==0)
try{
wait();
}catch(Exception e){}
}
public static void main(String[] args) throws Exception{
new MyThread().start();
}
}

这个线程就不会进入t的wait方法而直接打印出Thread say:Hello,World!.

而假设改成:

public class Test {

int x = 0;
public synchornized void test(){
if(x==0)
try{
wait();
}catch(Exception e){}
}
public static void main(String[] args) throws Exception{
new MyThread().start();
}
}

我们就能够看到线程一直等待,注意这个线程进入等待后没有其他线程唤醒,除非强行退出JVM环境,否则它一直等待.

所以请记住:

[线程要想调用一个对象的wait()方法就要先获得该对象的监视锁,而一旦调用wait()后又马上释放该锁]

转载地址:http://blog.zol.com.cn/860/article_859847.html

相关文章:

Spring复习笔记:3

Spring基于xml的案例实践 在数据库中创建一张新的表 create table account(id int primary key auto_increment,name varchar(40),money float )character set utf8 collate utf8_general_ci;往表中导入数据 insert into account(name,money) values(aaa,1000); insert into…

Blender多米诺骨牌动画学习教程 The Impossible Domino Run in Blender

流派:电子学习| MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;48.0 KHz 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09;|大小:8.53 GB 含课程文件 |时长:8h 20m Blender的运动跟踪&#xff0c;建模&#xff0c;渲染和合成工具集…

unity3d游戏开发猜想——当程序猿老去

程序猿将代码注入生命去打造互联网的浪潮之巅。当有一天他们老了。会走向那里&#xff0c;会做些什么&#xff1f;非常多年以后&#xff0c;在我60岁的那天早晨&#xff0c;天刚蒙蒙亮我就起床了&#xff0c;先去公园晨练&#xff0c;然后回来做早餐&#xff08;50岁的时候我学…

【JavaScript】JavaScript基础-变量、运算符与控制语句

一.变量 变量&#xff1a; 定义一个变量&#xff0c;系统会为之分配一块内存&#xff0c;程序可以用变量名来表示这块内存中的数据。 由于javascript采用的是弱类型的变量形式&#xff0c;因此&#xff0c;在声明一个变量的时候&#xff0c;我们不必声明它的类型&#xff0c;但…

ROW_NUMBER() OVER()函数用法详解 (分组排序 例子多)

ROW_NUMBER() OVER()函数用法详解 &#xff08;分组排序 例子多&#xff09; https://blog.csdn.net/qq_25221835/article/details/82762416 posted on 2019-09-05 01:00 竹径风声 阅读(...) 评论(...) 编辑 收藏 转载于:https://www.cnblogs.com/girl1314/p/11462711.html

Blender+Substance Painter全流程制作真实的机器人学习教程

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:44节课(10h 52m) |大小解压后:9.9 GB 含课程素材 如何使用Blender 2.8和Substance Painter制作真…

Spring复习笔记:4

在复习笔记三中我们进行的案例的编写&#xff0c;我们可以发现&#xff0c;就算使用了注解的方式&#xff0c;xml配置文件文件还是不能够删除&#xff0c;现在我们来将一些新的注解可以让我们去掉xml配置文件。 Configuration 作用&#xff1a;指定当前类是一个配置类 细节&a…

Extjs PROXY查询params无法传参,改用extraParams

转载于:https://www.cnblogs.com/cocoat/p/5153009.html

详解Paint的setPathEffect(PathEffect effect)

一、setPathEffect() 这个方法一看就和path有关&#xff0c;顾名思义&#xff0c;它就是给path设置样式&#xff08;效果&#xff09;的。PathEffect这个路径效果类没有具体的实现&#xff0c;效果是由它的六个子类实现的&#xff1a; 这六个子类分别可以实现不同的路径效果&am…

返回手势导致页面卡死并且UI错乱的问题解决

问题记录:在做了部分页面的转场动画之后,返回手势不灵了,快速连续返回的话会卡住,App退到后台再重新激活之后页面不卡了,但是UI错乱. 解决方案: 1. 在UINavigationController子类实现代理UIGestureRecognizerDelegate,并在viewDidLoad方法中增加代理设置: - (void)viewDidLoad …

Spring学习笔记:3(面向切面AOP)

AOP&#xff1a;Aspect Oriented Program&#xff08;面向切面&#xff09; 我们再回顾一下AOP的一些术语&#xff1a; 通知&#xff08;Advice&#xff09; 就是你想要的功能&#xff0c;也就是的安全、事物、日志等。先定义好&#xff0c;然后在想用的地方用一下。 连接…

Blender全流程制作真实感3D产品学习教程

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:41节课(4h 29m) |大小解压后:4.53 GB 仅使用blender 2.8进行建模、纹理、光照和渲染&#xff0c;…

如何给iOS应用添加原生的二维码扫描功能

之前总觉得二维码扫描很高大上&#xff0c;其实apple工程师早就为我们提供了便捷的方法。二维码扫描第三方的库也挺多的&#xff0c;不过效率高的当属系统提供的扫描方法。 二维码扫描主要用到了以下几个类&#xff1a;AVCaptureDevice,AVCaptureDeviceInput,AVCaptureMetadata…

2021-2027年中国市医疗电子场投资分析及前景预测报告

【报告类型】产业研究 【报告价格】4500起 【出版时间】即时更新&#xff08;交付时间约3个工作日&#xff09; 【发布机构】智研瞻产业研究院 【报告格式】PDF版 本报告介绍了中国医疗电子行业市场行业相关概述、中国医疗电子行业市场行业运行环境、分析了中国医疗电子行…

RabbitMQ入门(4)--路由

2019独角兽企业重金招聘Python工程师标准>>> ###路由 ###&#xff08;使用Java客户端&#xff09; 在先前的指南中&#xff0c;我们建立了一个简单的日志系统。我们可以将我们的日志信息广播到多个接收者。 在这部分的指南中&#xff0c;我们将要往其中添加一个功能…

从一个数组中寻找出现奇数次的数字

假设给定了数组nums为[0,1,2,3,4,5,6,7,8,9,10,10,9,8,7,6,5,4,1,2,3,3,0] 其中3出现了3次 而其他数字都出现了两次 则我们应该得到结果为3 第一种方式&#xff1a;使用Hash 1 /**2 * 使用hash3 * */4 public static int singleNumber_1(int[] nums) {5 …

Blender写实建筑场景制作学习教程 Exterior Visualization in Blender 2.9

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:26节课(3h 41m) |大小:3.3 GB 使用Blender创建惊人的外部渲染。 你会学到: Blender中的建模、着色…

Postman增删改查接口测试

查 一.选择Get方式 二.点击Send开始测试&#xff0c;得出结果 增 一.选择Post方式 二.输入需要增添的数据 三.点击Send测试 四.没有报错&#xff0c;添加成功 查看一下&#xff0c;发现确实添加进去了 删 一.选择Delete方式 二.根据id删除&#xff0c;在请求路径下直接写出…

新安装Ubuntu加载时提示“为/检查磁盘时发生严重错误”的解决方法

本文部分内容转载自&#xff1a; http://jingyan.baidu.com/article/0aa22375bbffbe88cc0d6419.html http://www.aichengxu.com/view/35086 解决方法&#xff1a; 1. 进入Ubuntu启动菜单时按e 键进入启动项编辑模式&#xff1a; 2. 找到代码【ro rootflagsync】&#xff0c;将其…

android binder机制之——(创建binder服务)

Binder机制编程前面的几篇文章具体介绍了android中binder机制的方方面面&#xff0c;相信你对binder机制已经有了较深刻的理解。俗话说得好“学以致用”&#xff0c;以下我们就通过在android系统中创建一个我们自己的binder服务&#xff0c;来加深对binder机制的理解。&#xf…

通过NSProxy来解决NSTimer使用不当造成内存泄漏的问题

NSTimer的一般使用: 1 interface ViewController : UIViewController2 property (nonatomic, strong) NSTimer *timer;3 end4 5 implementation ViewController6 - (void)viewDidLoad {7 [super viewDidLoad];8 [self startTimer];9 } 10 11 - (void)startTimer { 12 …

Blender 2.9中的真实感三维产品全流程制作学习教程

MP4 |视频:h264&#xff0c;1280720 |音频:AAC&#xff0c;44.1 KHz&#xff0c;2 Ch 语言&#xff1a;英语中英文字幕&#xff08;根据原英文字幕机译更准确&#xff09; |时长:41节课(6h 23m) |大小:5.83 GB 含课程文件 使用blender 2.9建模、纹理、光照和渲染真实的吉他 …

SpringBoot中的SFL4J日志

SpringBoot&#xff1a;底层是Spring框架&#xff0c;Spring框架默认使用的是JCL日志的抽象层 SpringBoot选用SLF4J和logback 如何系统地在系统中使用SLF4J 在开发的使用&#xff0c;日志记录方法的调用&#xff0c;不应该来直接调用日志的实现类&#xff0c;而是调用日志抽象…

web开发性能优化---用户体验篇

怎样从技术角度怎样增强用户体验。都是非常多平台都在做的事情&#xff0c;依据个人实际经验碰到几种体验做下总结。1、降低页面刷新白屏适当使用ajax技术。改善刷新白屏现象。2、信息提醒&#xff0c;邮件、站内信、短信在购物流程、售后流程适当添加信息温馨提醒环节&#xf…

linux命令2--cd和pwd

2019独角兽企业重金招聘Python工程师标准>>> cd是linux中最为常见的一个命令&#xff0c;其作用就是切换到某一个路径下 例子1 到自己的用户目录下 cd ~ 也可以直接cd 例子2 返回进入当前目录之前的目录 cd - 例子3 把上一个命令作为cd的参数命令 cd !$ 参考文档&am…

COALESCE语句解救sql的sum问题

mysqlmybatis有一个sql语句是统计用的 <select id"getNum" resultType"map"> 结果是一个map&#xff0c; select语句的结果是一些sum select sum(t.anum),sum(t.bnum)from tableSum twhere t.id #{id} 调试发现&#xff0c;数据库明明记录已经落入&…

在UE5创造一个多山的松树森林场景学习教程

UE5游戏场景设计制作视频教程 大小解压后&#xff1a;4.37G 1920X1080 mp4 语言&#xff1a;英语中英字幕&#xff08;机译&#xff09;时长&#xff1a;5小时 20分 课程获取&#xff1a;在UE5创造一个多山的松树森林场景学习教程

编写纳新网站后端的相关知识总结

使用HSSFWorkbook导出数据库中的数据 导入Apache POI Maven jar包 <!-- Apache POI --> <dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>3.6</version> </dependency>在控制层编…

IOSUIcontrol事件

UIKit提供了一组控件&#xff1a;UISwitch开关、UIButton按钮、UISegmentedControl分段控件、UISlider滑块、UITextField文本字段控件、UIPageControl分页控件。 控件是对UIView派生类的实用增强及补充&#xff0c;并可以直接附着于导航栏、表格单元&#xff0c;甚至更大的对象…

Xcode Debugging

程序员日常开发中有大量时间都会花费在 debug 上&#xff0c;从事 iOS 开发不可避免地需要使用 Xcode。这篇博客就主要介绍了 Xcode 中几种能够大幅提升代码调试效率的方式。“If debugging is the process of removing bugs, then programming must be the process of putting…