synchronized使用和原理全解

synchronizedJava中的关键字,是一种同步锁。它修饰的对象有以下几种:

修饰一个方法

被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

修饰一个静态的方法

其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

修饰一个代码块

被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

修饰一个类

其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

 

修饰一个方法

被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;

如果多个线程访问同一个对象的实例变量,可能出现非线程安全问题。

例子:a线程set后sleep2000ms,看b线程是否可以趁机set,造成非线程安全

HasSelfPrivateNum.java:

package service;public class HasSelfPrivateNum {private int num = 0;public void addI(String username) {try {if (username.equals("a")) {num = 100;System.out.println("a set over!");Thread.sleep(2000);} else {num = 200;System.out.println("b set over!");}System.out.println(username + " num=" + num);} catch (InterruptedException e) {e.printStackTrace();}}}

A:

package extthread;import service.HasSelfPrivateNum;public class ThreadA extends Thread {private HasSelfPrivateNum numRef;public ThreadA(HasSelfPrivateNum numRef) {super();this.numRef = numRef;}@Overridepublic void run() {super.run();numRef.addI("a");}}

B:

package extthread;import service.HasSelfPrivateNum;public class ThreadB extends Thread {private HasSelfPrivateNum numRef;public ThreadB(HasSelfPrivateNum numRef) {super();this.numRef = numRef;}@Overridepublic void run() {super.run();numRef.addI("b");}}

run:

package test;import service.HasSelfPrivateNum;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {HasSelfPrivateNum numRef = new HasSelfPrivateNum();ThreadA athread = new ThreadA(numRef);athread.start();ThreadB bthread = new ThreadB(numRef);bthread.start();}
}

结果:a线程set后sleep2000ms,b线程可以趁机set,造成非线程安全

这时我们使用synchronized修饰addI方法:

package service;public class HasSelfPrivateNum {private int num = 0;synchronized public void addI(String username) {try {if (username.equals("a")) {num = 100;System.out.println("a set over!");Thread.sleep(2000);} else {num = 200;System.out.println("b set over!");}System.out.println(username + " num=" + num);} catch (InterruptedException e) {e.printStackTrace();}}}

结果:B不能set了,说明线程安全。

注意:我们取得的是对象锁,也就是说,一个对象一个锁,而不是锁住整个类或者代码或者方法。

例子:两个对象两个锁

myobject.java

打印名字后sleep,最后打印end

package extobject;public class MyObject {synchronized public void methodA() {try {System.out.println("begin methodA threadName="+ Thread.currentThread().getName());Thread.sleep(5000);System.out.println("end");} catch (InterruptedException e) {e.printStackTrace();}}}

A:

package extthread;import extobject.MyObject;public class ThreadA extends Thread {private MyObject object;public ThreadA(MyObject object) {super();this.object = object;}@Overridepublic void run() {super.run();object.methodA();}}

B:

package extthread;import extobject.MyObject;public class ThreadB extends Thread {private MyObject object;public ThreadB(MyObject object) {super();this.object = object;}@Overridepublic void run() {super.run();object.methodA();}
}

RUN:

package test.run;import extobject.MyObject;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {MyObject object = new MyObject();ThreadA a = new ThreadA(object);a.setName("A");ThreadB b = new ThreadB(object);b.setName("B");a.start();b.start();}}

结果:两个对象互不影响,各自运行各自上锁

其它方法被调用是什么效果呢?

之前做实验是因为怕大家不理解知识,现在大家已经有了一定的基础,这个结论不再做实验。

结论:

如果A线程持有x对象的锁,B线程不可调用synchronized修饰的方法,但是可以异步调用没有被synchronized修饰的方法

 

synchronized具有锁重入功能,也就是说一个线程获得锁,再次请求是可以再次得到对象的锁的

下面做实验验证这个结论:

service.java

package myservice;public class Service {synchronized public void service1() {System.out.println("service1");service2();}synchronized public void service2() {System.out.println("service2");service3();}synchronized public void service3() {System.out.println("service3");}}

thread:

package extthread;import myservice.Service;public class MyThread extends Thread {@Overridepublic void run() {Service service = new Service();service.service1();}}

run:

package test;import extthread.MyThread;public class Run {public static void main(String[] args) {MyThread t = new MyThread();t.start();}
}

结果验证了上面的结论:

注:在父子类之间同样适用,不再做实验

 

但是如果一个线程出现了异常,难道就永远锁住了资源吗?其实不是的,出现异常自动释放锁

实验:让a锁住对象后出现异常,看b是否可以拿到锁,代码不再展示。

结果:b可以正常执行。

 

修饰一个静态的方法

 

其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

也就是说整个类就一个锁,这也和静态的概念相符(静态方法和属性是属于类的而不是具体一个对象的)

让我们来验证这个结论:

service:

package service;public class Service {synchronized public static void printA() {try {System.out.println("线程名称为:" + Thread.currentThread().getName()+ "在" + System.currentTimeMillis() + "进入printA");Thread.sleep(3000);System.out.println("线程名称为:" + Thread.currentThread().getName()+ "在" + System.currentTimeMillis() + "离开printA");} catch (InterruptedException e) {e.printStackTrace();}}synchronized public static void printB() {System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "进入printB");System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "离开printB");}}

a:

package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service = service;}@Overridepublic void run() {service.printA();}
}

b:

package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service = service;}@Overridepublic void run() {service.printB();}
}

run:

package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {Service service1 = new Service();Service service2 = new Service();ThreadA a = new ThreadA(service1);a.setName("A");a.start();ThreadB b = new ThreadB(service2);b.setName("B");b.start();}}

结果:

但是,请注意一个显而易见的结论:a线程访问synchronized修饰的静态方法时,b线程可以访问synchronized修饰的非静态方法,原因也很容易想到:静态方法是属于类的,普通方法是属于对象本身的,所以一个是对象锁,一个是class锁,不会影响。

为了验证这个结论,我们做实验看看结果。

service:

AB为静态的,C普通的。

package service;public class Service {synchronized public static void printA() {try {System.out.println("线程名称为:" + Thread.currentThread().getName()+ "在" + System.currentTimeMillis() + "进入printA");Thread.sleep(3000);System.out.println("线程名称为:" + Thread.currentThread().getName()+ "在" + System.currentTimeMillis() + "离开printA");} catch (InterruptedException e) {e.printStackTrace();}}synchronized public static void printB() {System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "进入printB");System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "离开printB");}synchronized public void printC() {System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "进入printC");System.out.println("线程名称为:" + Thread.currentThread().getName() + "在"+ System.currentTimeMillis() + "离开printC");}}

a: 

package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service = service;}@Overridepublic void run() {service.printA();}}

b:

package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service = service;}@Overridepublic void run() {service.printB();}
}

c:

package extthread;import service.Service;public class ThreadC extends Thread {private Service service;public ThreadC(Service service) {super();this.service = service;}@Overridepublic void run() {service.printC();}
}

 run:

package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;
import extthread.ThreadC;public class Run {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();ThreadC c = new ThreadC(service);c.setName("C");c.start();}}

结果:

说明:验证了结论,因为AB同步执行,C异步执行。

 

修饰一个代码块

被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;(而不一定是本对象了)

synchronized方法是对当前对象加锁,synchronized代码块是对某个对象加锁

用synchronized声明方法是有弊端的,比如A调用同步方法执行一个很长时间的任务,这时B就必须等待,有时候这种长时间等待是低效率且没有必要的,这时我们就要认识一下synchronized同步代码块了。

格式是这样的:synchronized(类名){......}

我们先来认识第一种用法来体会修饰代码块的好处:

synchronized(this)同步代码块:a调用相关代码后,b对其它synchronized方法和synchronized(this)同步代码块调用会阻塞。但是没有被synchronized修饰的代码就得以执行,不像之前修饰方法那么死板了。

我们来看一个例子:

第一个循环异步,第二个循环同步:

package mytask;public class Task {public void doLongTimeTask() {for (int i = 0; i < 100; i++) {System.out.println("nosynchronized threadName="+ Thread.currentThread().getName() + " i=" + (i + 1));}System.out.println("");synchronized (this) {for (int i = 0; i < 100; i++) {System.out.println("synchronized threadName="+ Thread.currentThread().getName() + " i=" + (i + 1));}}}
}

thread1:

package mythread;import mytask.Task;public class MyThread1 extends Thread {private Task task;public MyThread1(Task task) {super();this.task = task;}@Overridepublic void run() {super.run();task.doLongTimeTask();}}

thread2:

package mythread;import mytask.Task;public class MyThread2 extends Thread {private Task task;public MyThread2(Task task) {super();this.task = task;}@Overridepublic void run() {super.run();task.doLongTimeTask();}}

run:

package test;import mytask.Task;
import mythread.MyThread1;
import mythread.MyThread2;public class Run {public static void main(String[] args) {Task task = new Task();MyThread1 thread1 = new MyThread1(task);thread1.start();MyThread2 thread2 = new MyThread2(task);thread2.start();}
}

结果:

在非同步代码块时,是交叉打印的

同步代码块时排队执行:

另一个用法:

synchronized(非this)同步代码块(也就是说将任意对象作为对象监视器):

格式:synchronized(非this对象x){......}

1、当多个线程持有的对象监听器为同一个对象时,依旧是同步的,同一时间只有一个可以访问,

2、但是对象不同,执行就是异步的。

这样有什么好处呢?

(因为如果一个类有很多synchronized方法或synchronized(this)代码块,还是会影响效率,这时用synchronized(非this)同步代码块就不会和其它锁this同步方法争抢this锁)

实验

第一点我们就不验证了,因为体现不出它的好处,我们验证第二点:

service:

一个同步非this代码块,一个同步方法:

package service;public class Service {private String anyString = new String();public void a() {try {synchronized (anyString) {System.out.println("a begin");Thread.sleep(3000);System.out.println("a   end");}} catch (InterruptedException e) {e.printStackTrace();}}synchronized public void b() {System.out.println("b begin");System.out.println("b   end");}}

a:

package extthread;import service.Service;public class ThreadA extends Thread {private Service service;public ThreadA(Service service) {super();this.service = service;}@Overridepublic void run() {service.a();}}

b:

package extthread;import service.Service;public class ThreadB extends Thread {private Service service;public ThreadB(Service service) {super();this.service = service;}@Overridepublic void run() {service.b();}}

 run:

package test;import service.Service;
import extthread.ThreadA;
import extthread.ThreadB;public class Run {public static void main(String[] args) {Service service = new Service();ThreadA a = new ThreadA(service);a.setName("A");a.start();ThreadB b = new ThreadB(service);b.setName("B");b.start();}}

结果:

确实是异步的。

最后一个要注意的点:我们知道synchronized(非this对象x){......}是将对象x监视,这也就意味着当线程a调用这段代码时,线程b调用类x中的同步方法和代码块也会是同步的效果(阻塞)。

为了让大家更明白,做最后一个例子:

首先创建一个有静态方法的类:

package test2.extobject;public class MyObject {synchronized public void speedPrintString() {System.out.println("speedPrintString ____getLock time="+ System.currentTimeMillis() + " run ThreadName="+ Thread.currentThread().getName());System.out.println("-----------------");System.out.println("speedPrintString releaseLock time="+ System.currentTimeMillis() + " run ThreadName="+ Thread.currentThread().getName());}
}

然后用 synchronized(非this对象x){......}的形式引用它,并进入这个代码块,然后看看这时这个静态方法是否可以被调用。

service:作用是synchronized(object)

package test2.service;import test2.extobject.MyObject;public class Service {public void testMethod1(MyObject object) {synchronized (object) {try {System.out.println("testMethod1 ____getLock time="+ System.currentTimeMillis() + " run ThreadName="+ Thread.currentThread().getName());Thread.sleep(5000);System.out.println("testMethod1 releaseLock time="+ System.currentTimeMillis() + " run ThreadName="+ Thread.currentThread().getName());} catch (InterruptedException e) {e.printStackTrace();}}}}

threadA:

package test2.extthread;import test2.extobject.MyObject;
import test2.service.Service;public class ThreadA extends Thread {private Service service;private MyObject object;public ThreadA(Service service, MyObject object) {super();this.service = service;this.object = object;}@Overridepublic void run() {super.run();service.testMethod1(object);}}

 threadB:

package test2.extthread;import test2.extobject.MyObject;public class ThreadB extends Thread {private MyObject object;public ThreadB(MyObject object) {super();this.object = object;}@Overridepublic void run() {super.run();object.speedPrintString();}
}

run:

 

package test2.run;import test2.extobject.MyObject;
import test2.extthread.ThreadA;
import test2.extthread.ThreadB;
import test2.service.Service;public class Run {public static void main(String[] args) throws InterruptedException {Service service = new Service();MyObject object = new MyObject();ThreadA a = new ThreadA(service, object);a.setName("a");a.start();Thread.sleep(100);ThreadB b = new ThreadB(object);b.setName("b");b.start();}}

结果:

 

是同步的。

当然,把修饰方法改为修饰代码块也是一样不能被执行的。

Synchronized的作用主要有三个:(1)确保线程互斥的访问同步代码(2)保证共享变量的修改能够及时可见(3)有效解决重排序问题。

synchronized底层原理

在 Java 早期版本中,synchronized属于重量级锁,效率低下,因为监视器锁(monitor)是依赖于底层的操作系统来实现,Java 的线程是映射到操作系统的原生线程之上的。如果要挂起或者唤醒一个线程,都需要操作系统帮忙完成,而操作系统实现线程之间的切换时需要从用户态转换到内核态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高。

在 Java 6 之后从 JVM 层面对synchronized 较大优化,锁的实现引入了如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销。

 

synchronized 同步语句块的实现使用的是 monitorenter 和 monitorexit 指令。

其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置。

当执行 monitorenter 指令时,线程试图获取锁也就是获取 monitor(monitor对象存在于每个Java对象的对象头中,synchronized 锁便是通过这种方式获取锁的,也是为什么Java中任意对象可以作为锁的原因) 的持有权.当计数器为0则可以成功获取,获取后将锁计数器设为1也就是加1。相应的在执行 monitorexit 指令后,将锁计数器设为0,表明锁被释放。如果获取对象锁失败,那当前线程就要阻塞等待,直到锁被另外一个线程释放为止。

synchronized 修饰的方法并没有 monitorenter 指令和 monitorexit 指令,取得代之的是ACC_SYNCHRONIZED标识,该标识指明了该方法是一个同步方法,JVM 通过该 ACC_SYNCHRONIZED 访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。但是原理其实都是类似的。具体的实现是操作系统的知识可以去翻我操作系统的文章。

锁详解

锁主要存在四种状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态,他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。

自旋:当有个线程A去请求某个锁的时候,这个锁正在被其它线程占用,但是线程A并不会马上进入阻塞状态,而是循环请求锁(自旋)。这样做的目的是因为很多时候持有锁的线程会很快释放锁的,线程A可以尝试一直请求锁,没必要被挂起放弃CPU时间片,因为线程被挂起然后到唤醒这个过程开销很大,当然如果线程A自旋指定的时间还没有获得锁,仍然会被挂起。

自适应性自旋:自适应性自旋是自旋的升级、优化,自旋的时间不再固定,而是由前一次在同一个锁上的自旋时间及锁的拥有者的状态决定。例如线程如果自旋成功了,那么下次自旋的次数会增多,因为JVM认为既然上次成功了,那么这次自旋也很有可能成功,那么它会允许自旋的次数更多。

锁消除是指虚拟机即时编译器在运行时,对一些代码上要求同步,但是被检测到不可能存在共享数据竞争的锁进行消除。

偏向锁的目的是消除数据在无竞争情况下的同步原语,进一步提高程序的运行性能。如果说轻量级锁是在无竞争的情况下使用CAS操作去消除同步使用的互斥量,那么偏向锁就是在无竞争的情况下把整个同步都消除掉,连CAS操作都不用做了。偏向锁默认是开启的,也可以关闭。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445047.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法(11)-leetcode-explore-learn-数据结构-链表的经典问题

leetcode-explore-learn-数据结构-链表31.反转一个链表2.移除链表元素3.奇偶链表4.回文链表5.小结本系列博文为leetcode-explore-learn子栏目学习笔记&#xff0c;如有不详之处&#xff0c;请参考leetcode官网&#xff1a;https://leetcode-cn.com/explore/learn/card/linked-l…

Linux线程池的设计

我设计这个线程池的初衷是为了与socket对接的。线程池的实现千变万化&#xff0c;我得这个并不一定是最好的&#xff0c;但却是否和我心目中需求模型的。现把部分设计思路和代码贴出&#xff0c;以期抛砖引玉。个人比较喜欢搞开源&#xff0c;所以大家如果觉得有什么需要改善的…

leetcode121买卖股票的最佳时机

给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。 如果你最多只允许完成一笔交易&#xff08;即买入和卖出一支股票&#xff09;&#xff0c;设计一个算法来计算你所能获取的最大利润。 注意你不能在买入股票前卖出股票。 示例 1: 输入: [7,1,5,3,6,…

epoll的内核实现

epoll是由一组系统调用组成。 int epoll_create(int size); int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout); select/poll的缺点在于&#xff1…

leetcode322 零钱兑换

给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1。 示例 1: 输入: coins [1, 2, 5], amount 11 输出: 3 解释: 11 5 5 1 示例 2: 输入: coins [2],…

leetcode538 把二叉搜索树转换成累加树

给定一个二叉搜索树&#xff08;Binary Search Tree&#xff09;&#xff0c;把它转换成为累加树&#xff08;Greater Tree)&#xff0c;使得每个节点的值是原来的节点值加上所有大于它的节点值之和。 对于每一个点来说&#xff0c;自己的父&#xff0c;和自己父的右子树都是大…

leetcode15 三数之和

给定一个包含 n 个整数的数组 nums&#xff0c;判断 nums 中是否存在三个元素 a&#xff0c;b&#xff0c;c &#xff0c;使得 a b c 0 &#xff1f;找出所有满足条件且不重复的三元组。 注意&#xff1a;答案中不可以包含重复的三元组。 例如, 给定数组 nums [-1, 0, 1,…

leetcode19. 删除链表的倒数第N个节点

给定一个链表&#xff0c;删除链表的倒数第 n 个节点&#xff0c;并且返回链表的头结点。 示例&#xff1a; 给定一个链表: 1->2->3->4->5, 和 n 2. 当删除了倒数第二个节点后&#xff0c;链表变为 1->2->3->5. 说明&#xff1a; 给定的 n 保证是有效…

python模块(5)-Matplotlib 简易使用教程

Matplotlib简易使用教程0.matplotlib的安装1.导入相关库2.画布初始化2.1 隐式创建2.2 显示创建2.3 设置画布大小2.4 plt.figure()常用参数3.plt. 能够绘制图像类型3.1等高线3.2 箭头arrow4.简单绘制小demodemo1.曲线图demo2-柱状、饼状、曲线子图5.plt.plot()--设置曲线颜色,粗…

leetcode20 有效的括号

给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。 左括号必须以正确的顺序闭合。 注意空字符串可被认为是有效字符串。 示…

leetcode1 两数之和

给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;你不能重复利用这个数组中同样的元素。 示例: 给定 nums [2, 7, 11, 15], t…

leetcode3 无重复字符最长子串

给定一个字符串&#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 示例 1: 输入: "abcabcbb" 输出: 3 解释: 因为无重复字符的最长子串是 "abc"&#xff0c;所以其长度为 3。 示例 2: 输入: "bbbbb" 输出: 1 解释: 因为无重复字符…

Linux(6)-命令行的使用,history,shell脚本

命令行的使用,shell脚本1.终端shell,man1.1 Ctrlr--匹配查找历史命令1.2 history [n] --列出历史命令1.3&#xff01;--执行历史命令2.shell 编程2.1 shell脚本2.2 注释2.3 指明所用的shell2.4 支持函数2.5 使用变量2.6 解析命令行参数2.7 if, for, case, while2.8 shell脚本中…

程序以及论文

本人长期承接大学计算机专业的毕业设计和论文的编写。 主要开发语言C&#xff0c;C &#xff08;windows或linux平台皆可&#xff09;&#xff0c;php&#xff0c;c#&#xff0c;VC 。 课题内容可以是 管理系统&#xff0c;可以是 网站设计开发 可以是 网络聊天 可以是 应用…

Github(1)-概览,初始化仓库

Github网页-本地git1.github网页1.1 主要界面1.1.1github主页1.1.2仓库主页1.1.3 个人页面1.2 注册github账号1.3 新建平台仓库2.git-本地仓库2.1 git本地仓库的三个区域2.2 创建一个本地仓库GitHub 本质上是一个代码托管平台&#xff0c;它提供的是基于 Git 的代码托管服务。G…

leetcode16 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数&#xff0c;使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。 例如&#xff0c;给定数组 nums [-1&#xff0c;2&#xff0c;1&#xff0c;-4], 和 target 1…

leecode11 盛水最多的容器

给定 n 个非负整数 a1&#xff0c;a2&#xff0c;...&#xff0c;an&#xff0c;每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线&#xff0c;垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多…

leecode26 删除排序数组中的重复项

给定一个排序数组&#xff0c;你需要在原地删除重复出现的元素&#xff0c;使得每个元素只出现一次&#xff0c;返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。 示例 1: 给定数组 nums [1,1,2…

leecode53 最大子序列和

给定一个整数数组 nums &#xff0c;找到一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 示例: 输入: [-2,1,-3,4,-1,2,1,-5,4], 输出: 6 解释: 连续子数组 [4,-1,2,1] 的和最大&#xff0c;为 6。 思路&#xff1a;…

Github(3)-本地文件管理

使用github 托管代码简单使用教程--本地文件管理1.基本概念2.本地文件管理2.1 git add2.2 git status2.3 git commit2.3 git log2.5 git reset --hard 版本回退2.6 git reflog2.7 git diff2.8 git checkout --file 工作区文件恢复2.9 git rm 删除版本库里的文件廖雪峰老师博文学…