Java ReentrantLock
目录
1 互斥性
2 公平性
3 可重入性
4 获取和释放锁
5 尝试获取锁
6 可中断的锁定
7 条件变量
8 性能
9 使用场景
ReentrantLock
是 Java 提供的一种可重入的互斥锁,位于 java.util.concurrent.locks
包中,它实现了 Lock
接口。这个锁提供了与内置监视器锁(通过 synchronized
关键字实现)类似的互斥性和内存可见性,但具有更强大的功能和灵活性。以下是 ReentrantLock
的一些基本概念:
1 互斥性
ReentrantLock
可以确保同一时间只有一个线程可以执行由该锁保护的代码块,从而避免了竞态条件。
2 公平性
- 非公平锁:默认情况下,
ReentrantLock
是非公平的。这意味着线程获取锁的顺序并不是按照它们请求锁的顺序来确定的。非公平锁通常提供更高的吞吐量。 - 公平锁:当创建
ReentrantLock
实例时,可以通过传入true
参数来指定为公平锁。公平锁保证线程将按照它们请求锁的顺序获得锁。这可能会降低吞吐量,但能减少饥饿现象。
示例代码:默认是非公平锁,如果需要公平锁,则在构造函数中传入 true
。
import java.util.concurrent.locks.ReentrantLock;// 默认非公平锁
ReentrantLock lock = new ReentrantLock();// 公平锁
ReentrantLock fairLock = new ReentrantLock(true);
3 可重入性
可重入意味着如果一个线程已经持有某个锁,那么它可以再次获取这个锁而不会被阻塞。这是非常有用的特性,因为它允许方法在调用其他可能也需要相同锁的方法时不会导致死锁。
示例代码:
public void reentrantExample() {lock.lock();try {// 第一次获取锁System.out.println("First time locked, thread: " + Thread.currentThread().getName());// 在已经持有锁的情况下再次获取锁lock.lock();try {System.out.println("Second time locked, thread: " + Thread.currentThread().getName());} finally {lock.unlock(); // 释放第二次获取的锁}} finally {lock.unlock(); // 释放第一次获取的锁}
}
输出结果:
First time locked, thread: main
Second time locked, thread: main
使用可重入锁的具体例子:假设有一个类,其中包含一个递归方法,且这个方法需要是线程安全的。由于方法会多次调用自身,因此需要一个可重入锁来确保同一个线程可以重复获取锁。
import java.util.concurrent.locks.ReentrantLock;public class FactorialCalculator {private final ReentrantLock lock = new ReentrantLock();public int factorial(int number) {lock.lock(); // 获取锁try {if (number <= 1) {return 1;} else {return number * factorial(number - 1); // 递归调用}} finally {lock.unlock(); // 释放锁}}
}
4 获取和释放锁
获取锁通过调用 lock()
方法,释放锁则通过 unlock()
方法。通常建议在 finally
块中释放锁,以确保即使发生异常也能正确释放锁。
public void doSomething() {lock.lock();try {// 执行同步代码} finally {lock.unlock();}
}
5 尝试获取锁
tryLock()
方法尝试获取锁,如果无法立即获得锁,则返回 false
,不会阻塞线程。
if (lock.tryLock()) { // 尝试获取锁,如果无法获取,则立即返回falsetry {// 执行同步代码} finally {lock.unlock();}
} else {// 锁未获得时的操作
}
tryLock()
方法有两个版本:
- 无参数的
tryLock()
:尝试获取锁,如果锁可用则立即获取并返回true
,否则立即返回false
。 - 带超时的
tryLock(long timeout, TimeUnit unit)
:尝试获取锁,如果在指定的时间内可以获取到锁,则返回true
;如果超过了指定时间仍无法获取到锁,则返回false
。
具体使用示例:
无参数的 tryLock():
import java.util.concurrent.locks.ReentrantLock;public class TryLockExample {private final ReentrantLock lock = new ReentrantLock();public void doWork() {if (lock.tryLock()) { // 尝试获取锁try {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing work.");} finally {lock.unlock(); // 确保释放锁}} else {// 如果无法获取锁,执行其他操作System.out.println("Thread " + Thread.currentThread().getName() + " could not get the lock and will do something else.");}}public static void main(String[] args) {TryLockExample example = new TryLockExample();// 创建两个线程来调用 doWork 方法Thread t1 = new Thread(() -> example.doWork(), "Thread-1");Thread t2 = new Thread(() -> example.doWork(), "Thread-2");t1.start();t2.start();}
}
在这个例子中,如果 Thread-1
先获取到了锁,那么 Thread-2
将无法获取锁,并且会直接输出“无法获取锁”的信息。
带超时的 tryLock(long timeout, TimeUnit unit)
:
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;public class TryLockWithTimeoutExample {private final ReentrantLock lock = new ReentrantLock();public void doWork() {boolean locked = false;try {// 尝试在5秒内获取锁locked = lock.tryLock(5, TimeUnit.SECONDS);if (locked) {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing work.");} else {// 如果在5秒内无法获取锁,执行其他操作System.out.println("Thread " + Thread.currentThread().getName() + " could not get the lock within 5 seconds and will do something else.");}} catch (InterruptedException e) {// 处理中断异常Thread.currentThread().interrupt();System.out.println("Thread " + Thread.currentThread().getName() + " was interrupted while waiting for the lock.");} finally {if (locked) {lock.unlock(); // 确保释放锁}}}public static void main(String[] args) {TryLockWithTimeoutExample example = new TryLockWithTimeoutExample();// 创建两个线程来调用 doWork 方法Thread t1 = new Thread(() -> example.doWork(), "Thread-1");Thread t2 = new Thread(() -> example.doWork(), "Thread-2");t1.start();t2.start();}
}
在这个例子中,如果 Thread-1
持有锁超过5秒钟,那么 Thread-2
将会在等待5秒后放弃尝试获取锁,并执行相应的逻辑。
使用场景
- 避免死锁:当多个线程试图以不同的顺序获取多个锁时,可能会导致死锁。使用
tryLock()
可以帮助检测这种情况,并采取适当的措施。 - 提高响应性:在某些情况下,你可能不希望线程一直等待锁,而是希望线程能够快速响应其他任务。这时可以使用
tryLock()
来检查锁是否可用,如果不可用就立即执行其他工作。 - 资源竞争控制:在资源有限的情况下,可以使用
tryLock()
来尝试获取资源,如果获取不到则可以选择放弃或重试。
6 可中断的锁定
lockInterruptibly()
方法允许在等待获取锁的过程中响应中断。
try {lock.lockInterruptibly(); // 尝试获取锁,可以响应中断try {// 执行同步代码} finally {lock.unlock();}
} catch (InterruptedException e) {// 处理中断情况
}
示例代码:
import java.util.concurrent.locks.ReentrantLock;public class CancellableTask {private final ReentrantLock lock = new ReentrantLock();private boolean isCancelled = false;public void runTask() {Thread thread = new Thread(() -> {try {// 尝试获取锁,同时可以响应中断lock.lockInterruptibly();try {// 模拟任务执行while (!isCancelled) {System.out.println("Task is running...");// 假设任务需要一段时间完成Thread.sleep(1000);}} finally {lock.unlock();}} catch (InterruptedException e) {System.out.println("Task was interrupted, stopping execution.");// 通常在这里你会清理资源并退出// 重新设置中断状态,以便调用者知道该线程已被中断Thread.currentThread().interrupt();}});thread.start();// 在某个时刻决定取消任务try {Thread.sleep(3000); // 等待几秒钟} catch (InterruptedException e) {e.printStackTrace();}isCancelled = true;thread.interrupt(); // 中断线程}public static void main(String[] args) {CancellableTask task = new CancellableTask();task.runTask();}
}
当主线程决定取消任务时,它会中断工作线程。工作线程会在 lockInterruptibly()
调用处立即响应中断,并抛出 InterruptedException
,从而允许任务快速终止。
输出结果:
Task is running...
Task is running...
Task is running...
Task was interrupted, stopping execution.
除了使用 lockInterruptibly()
来实现中断锁,还可以使用带超时的 tryLock(long timeout, TimeUnit unit)来达成这一目的。以下是例子:
import java.util.concurrent.locks.ReentrantLock;public class ResourceService {private final ReentrantLock resourceLock = new ReentrantLock();public void useResource() {Thread thread = new Thread(() -> {try {if (resourceLock.tryLock()) {try {// 使用资源System.out.println("Using resource...");Thread.sleep(10000); // 模拟长时间操作} finally {resourceLock.unlock();}} else {System.out.println("Could not acquire the resource, operation is cancelled.");}} catch (InterruptedException e) {System.out.println("Resource usage was interrupted, releasing the resource.");resourceLock.unlock(); // 如果已经获得锁,则释放Thread.currentThread().interrupt(); // 保持中断状态}});thread.start();// 在某个时刻决定取消使用资源的操作try {Thread.sleep(5000); // 等待几秒钟} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 中断线程}public static void main(String[] args) {ResourceService service = new ResourceService();service.useResource();}
}
是的,tryLock(long time, TimeUnit unit)
方法也可以响应中断。当一个线程调用 tryLock(long time, TimeUnit unit)
时,它会尝试在指定的时间内获取锁。如果在这段时间内没有获取到锁,方法将返回 false
。此外,如果在此期间线程被中断,该方法会立即抛出 InterruptedException
并且不会获取锁。
注意:ReentrantLock
的 lock()
方法确实不会响应中断。当一个线程调用 lock()
试图获取锁时,如果锁已经被其他线程持有,那么该线程将一直阻塞,直到它能够获取到锁为止。即使在此期间该线程被中断(例如通过调用 Thread.interrupt()
),它也不会抛出 InterruptedException
或者以其他方式退出等待状态。只有在获取到锁之后,中断状态才会被检查。
7 条件变量
ReentrantLock
提供了条件变量 Condition
,它可以替代传统的 Object.wait/notify
机制。这些对象类似于 Object
类中的 wait/notify
机制,但是更加灵活。每个 Condition
实例都可以独立地挂起和唤醒线程,这对于复杂的同步需求是非常有用的。
下面是一个具体的例子,展示了如何使用条件变量来实现生产者-消费者模式:
在这个模式中,有一个共享缓冲区,生产者向缓冲区添加元素,消费者从缓冲区移除元素。当缓冲区满时,生产者必须等待;当缓冲区空时,消费者必须等待。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.LinkedList;public class ProducerConsumerExample {private final int BUFFER_SIZE = 5; // 缓冲区大小private final LinkedList<Integer> buffer = new LinkedList<>();private final ReentrantLock lock = new ReentrantLock();private final Condition notFull = lock.newCondition(); // 缓冲区不满的条件private final Condition notEmpty = lock.newCondition(); // 缓冲区不空的条件public void put(int value) throws InterruptedException {lock.lock();try {// 如果缓冲区已满,则等待while (buffer.size() == BUFFER_SIZE) {notFull.await();}// 向缓冲区添加元素buffer.add(value);System.out.println("Produced: " + value);// 唤醒可能正在等待的消费者notEmpty.signal();} finally {lock.unlock();}}public int get() throws InterruptedException {lock.lock();try {// 如果缓冲区为空,则等待while (buffer.isEmpty()) {notEmpty.await();}// 从缓冲区移除元素int value = buffer.removeFirst();System.out.println("Consumed: " + value);// 唤醒可能正在等待的生产者notFull.signal();return value;} finally {lock.unlock();}}public static void main(String[] args) {final ProducerConsumerExample example = new ProducerConsumerExample();Thread producerThread = new Thread(() -> {for (int i = 0; i < 20; i++) {try {example.put(i);Thread.sleep(100); // 模拟生产时间} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}});Thread consumerThread = new Thread(() -> {for (int i = 0; i < 20; i++) {try {example.get();Thread.sleep(200); // 模拟消费时间} catch (InterruptedException e) {Thread.currentThread().interrupt();break;}}});producerThread.start();consumerThread.start();try {producerThread.join();consumerThread.join();} catch (InterruptedException e) {e.printStackTrace();}}
}
代码解释:
- ReentrantLock 和 Condition:我们使用
ReentrantLock
来保护对共享资源(即缓冲区)的访问,并且创建了两个条件变量notFull
和notEmpty
。 - put() 方法:生产者调用此方法向缓冲区添加数据。如果缓冲区已满,则生产者将调用
notFull.await()
等待直到有空间可用。一旦添加了数据,就通过notEmpty.signal()
通知等待中的消费者。 - get() 方法:消费者调用此方法从缓冲区获取数据。如果缓冲区为空,则消费者将调用
notEmpty.await()
等待直到有数据可用。一旦取出了数据,就通过notFull.signal()
通知等待中的生产者。 - 主线程:启动生产者和消费者线程,并等待它们完成。
8 性能
ReentrantLock
在某些情况下比 synchronized
更高效,尤其是在高度竞争的情况下。这是因为 ReentrantLock
可以使用自旋而不是完全阻塞来等待锁,这样可以减少上下文切换的成本。
9 使用场景
- 当你需要比
synchronized
更细粒度的控制时,例如尝试获取锁、响应中断或使用多个条件变量。 - 当你需要实现公平锁时。
- 当你希望在性能上有所提升,并且能够处理高级并发模式时。
示例代码:
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();public void doSomething() {// 获取锁lock.lock();try {// 执行需要同步的操作System.out.println("Thread " + Thread.currentThread().getName() + " is doing something.");} finally {// 确保锁最终会被释放lock.unlock();}}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 创建两个线程来调用 doSomething 方法Thread t1 = new Thread(() -> example.doSomething(), "Thread-1");Thread t2 = new Thread(() -> example.doSomething(), "Thread-2");t1.start();t2.start();}
}
在这个例子中,doSomething
方法使用 ReentrantLock
来确保每次只有一个线程可以执行其中的代码。注意在 finally
块中释放锁,以防止因异常导致锁未被释放的情ss
相关文章:
Java ReentrantLock
目录 1 互斥性 2 公平性 3 可重入性 4 获取和释放锁 5 尝试获取锁 6 可中断的锁定 7 条件变量 8 性能 9 使用场景 ReentrantLock 是 Java 提供的一种可重入的互斥锁,位于 java.util.concurrent.locks 包中,它实现了 Lock 接口。这个锁提供了与内…...
【Linux系统编程】第二十六弹---彻底掌握文件I/O:C/C++文件接口与Linux系统调用实践
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、回顾C语言文件接口 1.1、以写的方式打开文件 1.2、以追加的方式打开文件 2、初步理解文件 2.1、C文件接口 3、进一步理…...
数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理
文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …...
Ubuntu-WSL2一键设置代理操作
现状: Window11中拥有自己的代理软件 ,可以科学上网已在WSL2中安装Ubuntu22.04 需求: Ubuntu-WSL2实现科学上网 实现: 参考:为 WSL2 一键设置代理 Linux 子系统中的网关指向的是 Windows,DNS 服务器指…...
ubuntu命令行连接wifi
在Ubuntu上,你可以通过命令行连接到Wi-Fi网络。以下是详细步骤,主要使用 nmcli 和 nmtui 命令。 方法 1:使用 nmcli nmcli 是 NetworkManager 的命令行界面,用于管理网络连接。以下是使用 nmcli 连接到 Wi-Fi 网络的步骤&#x…...
日常工作第10天:
vim 批量编辑的命令是 移动光标到列首。按键 CtrlV 进入 Visual block 模式,标记你想要进行编辑的列(HJKL可以向左下上右移动光标)。按键 ShiftI 进入 Insert 模式,在列首输入文本;或者 Shift A,追加文本…...
CNN+Transformer解说
CNN(卷积神经网络)和Transformer是两种在深度学习领域广泛使用的模型架构,它们在处理不同类型的数据和任务时各有优势。 CNN擅长捕捉局部特征和空间层次结构,而Transformer擅长处理序列数据和长距离依赖关系。 将CNN与Transform…...
jmeter中token测试
案例: 网站:http://shop.duoceshi.com 讲解:用三个接口来讲解 第一个接口code:GET http://manage.duoceshi.com/auth/code 第二个登录接口:http://manage.duoceshi.com/auth/login 第三个接口:http://…...
基于解压缩模块的JPEG同步重压缩检测论文学习
一、论文基本信息: 论文题目:基于解压缩模块的JPEG同步重压缩检测 作者:王金伟1 ,胡冰涛1 ,张家伟1 ,马 宾2 ,罗向阳3 (1.南京信息工程大学计算机学院、网络空间安全学院…...
音视频入门基础:FLV专题(7)——Tag header简介
一、引言 从《音视频入门基础:FLV专题(3)——FLV header简介》中可以知道, 在FLV header之后,FLV文件剩下的部分应由PreviousTagSize和Tag组成。FLV文件 FLV header PreviousTagSize0 Tag1 PreviousTagSize1 Ta…...
【Linux 报错】“make: ‘xxxx‘ is up to date.” 解决办法
一、报错原因 我们使用 make 命令,想要将 text.c 文件编译形成 可执行文件 text 时,报错如下 make: test is up to date. 中文含义:test 文件已经达到最新状态 意思是: test.c 文件里面的 所有源代码都没有修改过,你…...
【FPGA开发】Xilinx FPGA差分输入时钟的使用方法
正文 以前在使用ZYNQ的领航者ZYNQ7020进行FPGA学习时,它们使用的单端50M的输入时钟,在verlog代码编写上比较简单,而现在使用Alinx的AXU3EG开发板时,发现它使用的是200M的差分输入时钟,哪这个时候,输入时钟要…...
面试扩展知识点
1.C语言中分为下面几个存储区 栈(stack): 由编译器自动分配释放堆(heap): 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收全局区(静态区): 全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域&#…...
【经验分享】MyCAT 中间件
学习了一下数据量过大的解决方案,使用 MyCAT 中间件。 MyCAT 可以解决分布式事务、读写分离、主从、分片等一系列MySQL集群和分布式问题。 整体过程可以概括为拦截 - 分发 - 响应 例如设置 MyCAT 分片规则为每500万条数据就换一个数据库存储。 分库分表的中心思想都是将数据…...
Kotlin:1.8.0 的新特性
一、概述 Kotlin 1.8.0版本英语官方文档 Kotlin 1.8.0 中文官方文档 The Kotlin 1.8.0 release is out and here are some of its biggest highlights: Kotlin 1.8.0发布了,下面是它的一些亮点: JVM 平台新增实验性函数:递归复制或删除目录内容改进了 …...
深度学习之开发环境(CUDA、Conda、Pytorch)准备(4)
目录 1.CUDA 介绍 1.1 CUDA 的基本概念 1.2 CUDA 的工作原理 1.3 CUDA 的应用领域 2. 安装CUDA 2.1 查看GPU版本 2.2 升级驱动(可选) 2.3 查看CUDA版本驱动对应的支持的CUDA ToolKit工具包 2.4 下载Toolkit 2.5 安装(省略࿰…...
10月2日笔记(内网资源探测篇)
内网资源探测 在内网渗透中,测试人员往往需要通过各种内网扫描技术来探测内网资源的情况,为后续的横向渗透做准备,通常需要发现内网存活的主机,并探测主机的操作系统、主机开放了哪些端口、端口上运行了哪些服务、服务的当前版本…...
SpringCloud-基于Docker和Docker-Compose的项目部署
一、初始化环境 1. 卸载旧版本 首先,卸载可能已存在的旧版本 Docker。如果您不确定是否安装过,可以直接执行以下命令: sudo yum remove docker \docker-client \docker-client-latest \docker-common \docker-latest \docker-latest-logro…...
Linux下的基本指令/命令(一)
目录 基本命令 1. Is命令/指令: 罗列当前目录下指定的文件或者目录. 2. pwd命令: 查看当前工作的路径 3. cd命令: 切换到指定路径下。 只能切换到目录中 4. tree命令: 树状显式目录 使用前要输入命令 yum install -y tree ,用来安装一个…...
从零开始Ubuntu24.04上Docker构建自动化部署(四)Docker安装gitlab
你会发现所有gitlab都无法拉取到的情况下,经查找极狐下的gitlab还可以下载,具体如下: 建议保存地址: https://packages.gitlab.cn/#browse/searchkeyword%3Dgitlab 最新版本: sudo docker pull registry.gitlab.cn…...
No module named ‘_ssl‘
目录 背景具体报错检验升级过程步骤一:升级ssl。步骤二:重新编译安装python 背景 换了台服务器按照之前centos升级python版本升级python正常编译安装成功,但是当使用时又出现了奇怪的报错,估计是机器太老了 具体报错 这个报错也…...
Linux —— Socket编程(三)
一、本章重点 1. tcp服务器实现思路,进一步了解和总结相关的接口 2. 了解日志和守护进程 二、tcp服务器核心思路 tcp版的服务器与udp的不同在于,udp是面向数据报传输数据,在数据传输中不需要建立与客户端的链接,直接用recvfrom…...
5G N2 N3 N6 NB口
在5G架构中,N2、N3和N6是三种关键的接口,每个接口都有其特定的功能和应用场景。 N2接口: N2接口是5G无线接入网(RAN)与5G核心网(5GC)之间的控制面接口。它主要负责传递控制平面消息,…...
【数据结构】堆(Heap)详解
在深入了解堆这一重要的数据结构之前,不妨先回顾一下我之前的作品 ——“二叉树详解”。 上篇文章👉剖析二叉树(Binary Tree) 二叉树作为一种基础的数据结构,为我们理解堆以及其他更复杂的数据结构奠定了坚实的基础。它…...
《Linux从小白到高手》理论篇(四):Linux用户和组相关的命令
List item 本篇介绍Linux用户和组相关的命令,看完本文,有关Linux用户和组相关的常用命令你就掌握了99%了。Linux用户和组相关的命令可以分为以下六类: 一.用户和用户组相关查询操作命令: Id id命令用于显示用户的身份标识。常见…...
OpenGL ES 之EGL(6)
OpenGL ES 之EGL(6) 简述 EGL是OpenGL ES的封装,目的是跨设备跨平台,隔离不同平台对窗口不同的实现。上一节我们基本没有使用到EGL,因为GLSurfaceView帮助我们处理了相关的逻辑,我们这一节来看一下EGL的一些概念以及接口的使用。…...
kotlin 委托
一、类委托 interface DB{fun insert() } class SqliteDB : DB {override fun insert() {println(" SqliteDB insert")} }class MySql : DB{override fun insert() {println(" MySql insert")} }class OracleDB : DB{override fun insert() {println(&quo…...
Stream流的中间方法
一.Stream流的中间方法 注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程 注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据 二.filter filter的主要用法是…...
【车载开发系列】ParaSoft单元测试环境配置(四)
【车载开发系列】ParaSoft单元测试环境配置(四) 【车载开发系列】ParaSoft单元测试环境配置(四) 【车载开发系列】ParaSoft单元测试环境配置(四)一. 如何设置过滤二. 如何设置静态扫描的规则三. 如何设置单…...
IDEA 设置自动定位文件
一、场景分析 IDEA 在使用的过程中,发现有时候,打开一个类,它并不能自动帮我们在左侧 Project 树中定位出文件,需要自己手动点击 瞄准 图标。很不方便。 二、解决方法 1、点击 瞄准 图标旁边的 竖三点 2、将 Alwasy Select Opene…...
网站改版优化/国内新闻最新消息
2019独角兽企业重金招聘Python工程师标准>>> 1.系统级:(一般需要root权限)(1)/etc/profile:该文件是用户登录时,操作系统定制用户环境时使用的第一个文件,应用于登录到系统的每一个用户。该文件…...
深圳营销型网站建设服务/西安分类信息seo公司
一个应用,应该保持一套统一的样式,包括Button、EditText、ProgressBar、Toast、Checkbox等各种控件的样式,还包括控件间隔、文字大小和颜色、阴影等等。web的样式用css来定义,而android的样式主要则是通过shape、selector、layer-…...
wordpress忘记密码怎么办/口碑营销有哪些方式
卢松松编著 对新手来说,口播类视频核心理念就是:大力出奇迹。 口播类视频是短视频领域最简单、最初级的一种视频模式。只要你人站在哪里说话就可以了,门槛非常低。因为口播类视频,在同一时期可产生几个、甚至几十个视频。所以“…...
做淘宝客网站必须备案吗/手机百度搜索app
一.URL(hash): 1) http://www.example.com/index.html#location1 #后面的部分即为URl的hash, 是当前页面的锚部分,浏览器读取到这部分, 就会将这部分内容滚动到可视区域; 在本页面下实现跳转的两种方式: <div name"location1"> <div id"location2&…...
wordpress主题模板仿/免费技能培训在哪里报名
导航:网站首页 >before和after区别 after的用法before和after区别 after的用法相关问题:匿名网友:一、意思不同1、before作连词意思是在 ... 以前。作介词意思是在 ... 以前;在..前面。作副词意思是以前;以往;之前。2、after作…...
如何生成一个网站/友情链接地址
谁能找到更多Python‘ic,更漂亮的解决方案?我正在循环浏览文件中的一些文本行,以检查它们是否符合某些条件.由于某种原因,决定了线内部的分隔符是”,即双倍空间.如何检查文本字符串以验证所有分隔符是否正好是两个空格?该行末尾的空格不是问题,因为该行最…...