第二十五天 多线程-常用方法&线程池【悟空教程】

第二十五天 多线程-常用方法&线程池【悟空教程】

第25天 多线程

第1章 多线程常用方法

1.1 多线程常用方法

1.1.1 常规方法:

  • public final String getName() 返回该线程的名称。
  • public final void setName(String name)
  • public static Thread currentThread() 返回的就是当前的执行线程
  • public void run()
  • public void start()
  • 重写public String toString()

继承Thread方法:

public class MyThread extends Thread {

//重写 run

@Override

public void run() {

for (int i = 0; i < 1000 ; i++) {

System.out.println(getName() + " ==== " + i );

}

}

}

/*

* 多线程 简单方法:

* String getName() 返回该线程的名称。

* void setName(String name) 设置线程名称

* Thread currentThread() , 返回的就是当前的执行线程.

*/

public class Demo {

public static void main(String[] args) {

// 创建 MyThread 对象

MyThread mt1 = new MyThread();

MyThread mt2 = new MyThread();

mt1.setName("姚明");

mt2.setName("郭敬明");

mt1.start();

mt2.start();

}

}

实现Runnable接口

public class MyRunnable implements Runnable {

@Override

public void run() {

for (int i = 1000; i >= 0; i--) {

// 获取当前方法的执行线程

Thread currentThread = Thread.currentThread(); // 执行当前的方法的线程对象.

System.out.println(currentThread.getName() + " ==== " + i);

}

}

}

public class Demo {

public static void main(String[] args) {

//创建 子类对象

MyRunnable myRunnable = new MyRunnable();

//创建线程对象

Thread thread = new Thread(myRunnable);

Thread thread2 = new Thread(myRunnable);

thread.setName("XXX");

thread2.setName("AAA");

// 开启线程

thread.start();

thread2.start();

Thread currentThread = Thread.currentThread();

currentThread.setName("^(* ̄(oo) ̄)^");

System.out.println(currentThread.getName());

}

}

1.1.2 线程操作方法:

1.1.2.1 优先级

public final void setPriority(int newPriority) 设置优先级,取值:1-10

public final int getPriority() 获取优先级

/*

public final void setPriority(int newPriority) 设置优先级,取值:1-10

public final int getPriority() 获取优先级

*/

public class Demo2 {

public static void main(String[] args) {

Thread currentThread = Thread.currentThread();

System.out.println(currentThread);

int priority = currentThread.getPriority();

System.out.println(priority); // 默认是 5 ,最小 1 ,最大10;

MyThread myThread = new MyThread();

myThread.setPriority(10);

myThread.start();

for (int i = 0; i < 1000 ; i++) {

System.out.println("main ---" + i);

}

}

}

1.1.2.2 线程加入&守护线程

public final void join() throws InterruptedException 线程加入(插队,加入方法)

public final void setDaemon(boolean on) 设置守护线程(坦克大战,守护main)

public class T1 extends Thread {

@Override

public void run() {

for (int i = 0; i < 1000; i++) {

if (i == 200) {

// 加入另外一条线程

try {

T2 t2 = new T2();

t2.start();

t2.join(); // 加入方法, 就是插队.

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println(i + " ==== t1" );

}

}

}

public class T2 extends Thread {

@Override

public void run() {

for (int i = 0; i < 500 ; i++) {

System.out.println(" t2 ---- " + i );

}

}

}

/*

* join . 加入

* setDaemon(boolean on) 设置守护线程

*

*/

public class Demo3 {

public static void main(String[] args) {

T1 t1 = new T1();

t1.setDaemon(true); // t1 就是守护线程 了

t1.start();

for (int i = 0; i < 10; i++) {

System.out.println(i); // 主线程 一完成, 守护也就结束.

}

}

}

public void interrupt() 被中断的线程会报被中断异常,这时需要使用try/catch语句解决相关问题,线程后代码仍然可以继续执行

public final void stop() (已过时) 直接停止线程,线程后代码无法被执行

1.1.2.3 线程睡眠(到指定时间自动恢复)

public static void sleep(long millis) throws InterruptedException

/*

* public static void sleep(long millis) 睡眠 .

*/

public class Demo {

public static void main(String[] args) {

for (int i = 10; i > 0; i--) {

// 暂停

try {

Thread.sleep(10000); // 到指定的时间,自动醒过来.

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(i);

}

System.out.println("点火 ");

}

}

1.2 测试JVM多线程_垃圾回收线程

/*

* 获取垃圾回收线程的名字.

*/

public class Demo {

public static void main(String[] args) {

for (int i = 0; i < 100000; i++) {

new Person();

System.out.println(i);

}

}

}

public class Person {

public Person() {

System.out.println(" 我来了, HELLO==WORLD");

}

@Override

protected void finalize() throws Throwable {

System.out.println(" 我是 垃圾 了, 我挂了, ByeBye World ");

Thread currentThread = Thread.currentThread();

System.out.println(currentThread.getName());

}

}

1.3 等待唤醒机制

在开始讲解等待唤醒机制之前,有必要搞清一个概念——线程之间的通信:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。

等待唤醒机制所涉及到的方法:

  • wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
  • notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
  • notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。

其实,所谓唤醒的意思就是让 线程池中的线程具备执行资格。必须注意的是,这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。

仔细查看JavaAPI之后,发现这些方法 并不定义在 Thread中,也没定义在Runnable接口中,却被定义在了Object类中,为什么这些操作线程的方法定义在Object类中?

因为这些方法在使用时,必须要标明所属的锁,而锁又可以是任意对象。能被任意对象调用的方法一定定义在Object类中。

接下里,我们先从一个简单的示例入手:

如上图说示,输入线程向Resource中输入name ,sex , 输出线程从资源中输出,先要完成的任务是:

  • 1.当input发现Resource中没有数据时,开始输入,输入完成后,叫output来输出。如果发现有数据,就wait();
  • 2.当output发现Resource中没有数据时,就wait() ;当发现有数据时,就输出,然后,叫醒input来输入数据。

下面代码,模拟等待唤醒机制的实现:

  • 模拟资源类

public class Resource {

private String name;

private String sex;

private boolean flag = false;

public synchronized void set(String name, String sex) {

if (flag)

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

// 设置成员变量

this.name = name;

this.sex = sex;

// 设置之后,Resource中有值,将标记该为 true ,

flag = true;

// 唤醒output

this.notify();

}

public synchronized void out() {

if (!flag)

try {

wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

// 输出线程将数据输出

System.out.println("姓名: " + name + ",性别: " + sex);

// 改变标记,以便输入线程输入数据

flag = false;

// 唤醒input,进行数据输入

this.notify();

}

}

  • 输入线程任务类

public class Input implements Runnable {

private Resource r;

public Input(Resource r) {

this.r = r;

}

@Override

public void run() {

int count = 0;

while (true) {

if (count == 0) {

r.set("小明", "男生");

} else {

r.set("小花", "女生");

}

// 在两个数据之间进行切换

count = (count + 1) % 2;

}

}

}

  • 输出线程任务类

public class Output implements Runnable {

private Resource r;

public Output(Resource r) {

this.r = r;

}

@Override

public void run() {

while (true) {

r.out();

}

}

}

  • 测试类

public class ResourceDemo {

public static void main(String[] args) {

// 资源对象

Resource r = new Resource();

// 任务对象

Input in = new Input(r);

Output out = new Output(r);

// 线程对象

Thread t1 = new Thread(in);

Thread t2 = new Thread(out);

// 开启线程

t1.start();

t2.start();

}

}

1.4 线程生命周期

第2章 线程池

2.1 线程池概念

线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

我们详细的解释一下为什么要使用线程池?

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。

2.2 使用线程池方式--Runnable接口

通常,线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

  • Executors:线程池创建工厂类
    • public static ExecutorService newFixedThreadPool(int nThreads):返回线程池对象
  • ExecutorService:线程池类
    • Future<?> submit(Runnable task):获取线程池中的某一个线程对象,并执行
  • Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
  • 使用线程池中线程对象的步骤:
    • 创建线程池对象
    • 创建Runnable接口子类对象
    • 提交Runnable接口子类对象
    • 关闭线程池

代码演示:

public class ThreadPoolDemo {

public static void main(String[] args) {

//创建线程池对象

ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象

//创建Runnable实例对象

MyRunnable r = new MyRunnable();

//自己创建线程对象的方式

//Thread t = new Thread(r);

//t.start(); ---> 调用MyRunnable中的run()

//从线程池中获取线程对象,然后调用MyRunnable中的run()

service.submit(r);

//再获取个线程对象,调用MyRunnable中的run()

service.submit(r);

service.submit(r);

//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

//关闭线程池

//service.shutdown();

}

}

  • Runnable接口实现类

public class MyRunnable implements Runnable {

@Override

public void run() {

System.out.println("我要一个教练");

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("教练来了: " +Thread.currentThread().getName());

System.out.println("教我游泳,交完后,教练回到了游泳池");

}

}

2.3 使用线程池方式—Callable接口

  • Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
  • ExecutorService:线程池类
    • <T> Future<T> submit(Callable<T> task):获取线程池中的某一个线程对象,并执行线程中的call()方法
  • Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
  • 使用线程池中线程对象的步骤:
    • 创建线程池对象
    • 创建Callable接口子类对象
    • 提交Callable接口子类对象
    • 关闭线程池

代码演示:

public class ThreadPoolDemo {

public static void main(String[] args) {

//创建线程池对象

ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象

//创建Callable对象

MyCallable c = new MyCallable();

//从线程池中获取线程对象,然后调用MyRunnable中的run()

service.submit(c);

//再获取个教练

service.submit(c);

service.submit(c);

//注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中

//关闭线程池

//service.shutdown();

}

}

  • Callable接口实现类,call方法可抛出异常、返回线程任务执行完毕后的结果

public class MyCallable implements Callable {

@Override

public Object call() throws Exception {

System.out.println("我要一个教练:call");

Thread.sleep(2000);

System.out.println("教练来了: " +Thread.currentThread().getName());

System.out.println("教我游泳,交完后,教练回到了游泳池");

return null;

}

}

2.4 线程池练习:返回两个数相加的结果

要求:通过线程池中的线程对象,使用Callable接口完成两个数求和操作

  • Future接口:用来记录线程任务执行完毕后产生的结果。线程池创建与使用
    • V get() 获取Future对象中封装的数据结果

代码演示:

public class ThreadPoolDemo {

public static void main(String[] args) throws InterruptedException, ExecutionException {

//创建线程池对象

ExecutorService threadPool = Executors.newFixedThreadPool(2);

//创建一个Callable接口子类对象

//MyCallable c = new MyCallable();

MyCallable c = new MyCallable(100, 200);

MyCallable c2 = new MyCallable(10, 20);

//获取线程池中的线程,调用Callable接口子类对象中的call()方法, 完成求和操作

//<Integer> Future<Integer> submit(Callable<Integer> task)

// Future 结果对象

Future<Integer> result = threadPool.submit(c);

//此 Future 的 get 方法所返回的结果类型

Integer sum = result.get();

System.out.println("sum=" + sum);

//再演示

result = threadPool.submit(c2);

sum = result.get();

System.out.println("sum=" + sum);

//关闭线程池(可以不关闭)

}

}

  • Callable接口实现类

public class MyCallable implements Callable<Integer> {

//成员变量

int x = 5;

int y = 3;

//构造方法

public MyCallable(){

}

public MyCallable(int x, int y){

this.x = x;

this.y = y;

}

@Override

public Integer call() throws Exception {

return x+y;

}

}

第3章 多线程总结

  • 同步锁

多个线程想保证线程安全,必须要使用同一个锁对象

  • 同步代码块

synchronized (锁对象){

可能产生线程安全问题的代码

}

同步代码块的锁对象可以是任意的对象

  • 同步方法

public synchronized void method()

可能产生线程安全问题的代码

}

同步方法中的锁对象是 this

  • 静态同步方法

public synchronized void method()

可能产生线程安全问题的代码

}

静态同步方法中的锁对象是 类名.class

  • 多线程有几种实现方案,分别是哪几种?

a, 继承Thread类

b, 实现Runnable接口

c, 通过线程池,实现Callable接口

  • 同步有几种方式,分别是什么?

a,同步代码块

b,同步方法

静态同步方法

  • 启动一个线程是run()还是start()?它们的区别?

启动一个线程是start()

区别:

start: 启动线程,并调用线程中的run()方法

run : 执行该线程对象要执行的任务

  • sleep()和wait()方法的区别

sleep: 不释放锁对象, 释放CPU使用权

在休眠的时间内,不能唤醒

wait(): 释放锁对象, 释放CPU使用权

在等待的时间内,能唤醒

  • 为什么wait(),notify(),notifyAll()等方法都定义在Object类中

锁对象可以是任意类型的对象

第4章 本日自习作业:

4.1 知识点相关题

4.1.1 请描述并画出线程生命周期图,越详细越好

4.1.2 简单使用一次今天介绍的线程方法

4.1.3 使用线程池完成多线程卖票

4.1.4 启动一个线程是用run()还是start()?

启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

4.1.5 用实现runable接口的方式创建创建一个多线程买票小程序,使用卖票的例子引出线程安全问题(用sleep()方法)并用synchronized代码块解决线程安全问题。

答案:

public class Ticket implements Runnable {

//多个对象 共享同一个资源, 需要static修饰

private int ticket = 100;

//创建锁对象(可以是任意对象)

private Object obj = new Object();



@Override

public void run() {

while (true) {

synchronized ( obj ){

if (ticket >0) {

try {

Thread.sleep(100);

} catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println(Thread.currentThread().getName()+":正在卖第 "+ ticket-- +"张

票");

}

}

}

}

}

public class SellTicket {

public static void main(String[] args) {

//创建自定义类对象

Ticket ticket = new Ticket();

//创建线程对象

Thread t1 = new Thread(ticket, "窗口1");

Thread t2 = new Thread(ticket, "窗口2");

Thread t3 = new Thread(ticket, "窗口3");

//启动线程

t1.start();

t2.start();

t3.start();

}

}

4.1.6 能够理解死锁的概念。

4.1.7 理解什么是线程间通信

其实就是多个线程在操作同一个资源,但是操作的动作不同。如果一个进程中的所有线程都不需要相互传递数据就可以顺利完成,那么程序运行的性能自然是最好的,但是实际上,很少有现成能够在所有的时间都独立的进行操作,通常在以下两种情况下,线程之间需要进行通信。 多个线程都对共享资源资源进行访问,但不希望共享资源被破坏。 一个线程完成了任务,要通知其他的线程。

4.1.8 多线程有几种实现方法?同步有几种实现方法?

多线程有两种实现方法,分别是继承Thread类与实现Runnable接口

同步的实现方面有两种,分别是synchronized,wait与notify

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

4.2 代码题

4.2.1 使用线程池完成以下功能(线程池的线程个数为2):

1、通过一个线程获取一个0-100内的随机数并在将随机数返回到main方法中,在main方法中将该随机数添加到list集合中;

2、将该任务向线程池提交3次,每次生成随机数之前让线程休眠1000毫秒,然后打印“”“线程XXXX生成的随机数为:XXX”;

3、在main方法中打印集合的内容

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;

public class Demo {

public static void main(String[] args) throws InterruptedException, ExecutionException {

m2();

}

public static void m2() throws InterruptedException, ExecutionException{

//1、使用线程池工厂创建线程池对象

ExecutorService pool = Executors.newFixedThreadPool(2);

//2、向线程池中提交任务

MyCallable my = new MyCallable();

List<Integer> list = new ArrayList<Integer>();

for(int i=0;i<3;i++){

//循环向线程池中提交任务

Future<Integer> s1 = pool.submit(my);

//解析线程执行的结果

Integer num1 = s1.get();

list.add(num1);

}

System.out.println(list);

//5、关闭线程池(可关可不关)

pool.shutdown();

}

}

public class MyCallable implements Callable<Integer>{

@Override

public Integer call() throws Exception {

Thread thread = Thread.currentThread();

thread.sleep(1000);

int i = new Random().nextInt(100);

System.out.println("线程:"+thread.getName()+"生成的随机数为:"+i);

return i;

}

}

4.2.2 创建线程计算 1--100的和,并将结果返回给主线程

答案:

public class Test02 {

public static void main(String[] args) throws InterruptedException, ExecutionException {

ExecutorService es = Executors.newSingleThreadExecutor();

//创建的是Callable对象

Future<Integer> f = es.submit(new Callable<Integer>() {

 

@Override

public Integer call() throws Exception {

//求和并返回结果

int sum = 0;

for(int i = 1; i <= 100; i++){

sum += i;

}

return sum;

}

});

//获取结果

System.out.println(f.get());

es.shutdown();

}

}

4.2.3 写出一个锁嵌套的死锁代码

  • 定义锁对象类

public class MyLock {

public static final Object lockA = new Object();

public static final Object lockB = new Object();

}

  • 线程任务类

public class ThreadTask implements Runnable {

int x = new Random().nextInt(1);//0,1

//指定线程要执行的任务代码

@Override

public void run() {

while(true){

if (x%2 ==0) {

//情况一

synchronized (MyLock.lockA) {

System.out.println("if-LockA");

synchronized (MyLock.lockB) {

System.out.println("if-LockB");

System.out.println("if大口吃肉");

}

}

} else {

//情况二

synchronized (MyLock.lockB) {

System.out.println("else-LockB");

synchronized (MyLock.lockA) {

System.out.println("else-LockA");

System.out.println("else大口吃肉");

}

}

}

x++;

}

}

}

  • 测试类

public class ThreadDemo {

public static void main(String[] args) {

//创建线程任务类对象

ThreadTask task = new ThreadTask();

//创建两个线程

Thread t1 = new Thread(task);

Thread t2 = new Thread(task);

//启动线程

t1.start();

t2.start();

}

}

4.2.4 生产一个,消费一个;等待唤醒机制(单生产者单消费者)

答案:

public class Phone {

private String bland; //型号

private String color; //颜色

private boolean isNewPhone = false;//是否有新手机



//购买手机

public synchronized void get(){

//判断是否有新手机

if (!this.isNewPhone) {

//进来了,代表没有新手机

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

//代码执行到了这里, 说明此刻有新手机

//购买手机

System.out.println("购买了 :" + this.bland + "..." + this.color);

//更新新 手机的状态

this.isNewPhone = false;

//通知生产商, 没有新手机了, 要生产新手机

this.notify();

}



//生产手机

public synchronized void set(String bland, String color){

//判断是否有新手机

if (this.isNewPhone) {

//进来了,代表有手机

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}



//如果走到了这个位置, 说明当前没有新手机

//生产一部新手机

this.bland = bland;

this.color = color;

System.out.println("生产了:" + this.bland + "---" + this.color);



//更新手机的状态为 有新手机

this.isNewPhone = true;

//通知消费者,有新手机了,可以购买

this.notify();

}

}

/*

* 生产商

*/

public class SetPhone implements Runnable {

private Phone phone;

int num = 0;



public SetPhone(Phone phone){

this.phone = phone;

}



@Override

public void run() {

//生产手机

while(true){

//生产一部新手机

if (num%2==0) {

phone.set("Iphone6 Plus", "土豪金");

} else {

phone.set("金立语音王", "红色");

}

num++;

}

}

}

/*

* 消费者类(顾客)

*/

public class GetPhone implements Runnable {

private Phone phone;

public GetPhone(Phone phone){

this.phone = phone;

}



@Override

public void run() {

while(true){

phone.get();

}

}

}

/*

* 测试类

*/

 

public class PhoneTest {

public static void main(String[] args) {

//定义一个手机对象

Phone phone = new Phone();



SetPhone setPhone = new SetPhone(phone);

GetPhone getPhone = new GetPhone(phone);

//创建线程对象

Thread setThread = new Thread(setPhone);

Thread getThread = new Thread(getPhone);

//启动线程

setThread.start();

getThread.start();

}

}

4.2.5 现有红包个数共50个,模拟三个人在抢红包过程

每个人相当于一条线程(需要给三个命名),每次抢到的红包随机为1-10元,要求每人每次只能抢一个红包,抢红包的过程中需要睡眠300毫秒,并且抢完的人还可以继续参与抢红包.抢到红包后在控制台打印输出”XXX线程抢到第X个红包,红包金额为X元,还剩余X个红包...”,另外在所有红包抢完后提示”红包已抢完”,程序结束.(要求使用Thread类和Runnable中的一种方式去实现).

4.2.6 一共有1000份盒饭,可以在两个窗口领取,假设每次装盒饭的时间为3000毫秒请用线程模拟取饭过程并打印剩余盒饭数量(分别设置线程名称 窗口一,窗口二).

4.2.7 同时开启两个线程(并且设置线程名称),共同输出1-10之间的所有数字,并且将输出偶数的线程名称打印出来。

本文分享自微信公众号 - Java帮帮(javahelp)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-06-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏奔跑的蛙牛技术博客

并发知识6

javase8 为散列映射提供批操作,即使有其他线程在处理映射,这些操作也能安全的执行。 批操作会遍历映射, 处理遍历过程中找到的元素

9920
来自专栏java一日一条

50个常见的 Java 错误及避免方法(第二部分)

System.out.println("Whatdo you want to do?");

13230
来自专栏FreeBuf

Safari信息泄露漏洞分析

Javascript中的数组和数组对象一直都是编程人员优化的主要目标,一般来说,数组只会包含一些基本类型数据,比如说32位整数或字符等等。因此,每个引擎都会对这...

10920
来自专栏大内老A

关于CLR内存管理一些深层次的讨论[上篇]

半年之前,PM让我在部门内部进行一次关于“内存泄露”的专题分享,我为此准备了一份PPT。今天无意中将其翻出来,觉得里面提到的关于CLR下关于内存管理部分的内存还...

21080
来自专栏小怪聊职场

Java中实现多线程的3种方法介绍和比较

28790
来自专栏Python小屋

1000道Python题库系列分享13(22道填空题)

32320
来自专栏散尽浮华

Python介绍

Python概述 Python创始人是吉多.范罗苏姆。在1989年万圣节期间为打发时间而开发的。 目前Python在TIOBE排行榜第五位置 ? Python可...

703100
来自专栏小樱的经验随笔

对X86汇编的理解与入门

本文描述基本的32位X86汇编语言的一个子集,其中涉及汇编语言的最核心部分,包括寄存器结构,数据表示,基本的操作指令(包括数据传送指令、逻辑计算指令、算数运算指...

36930
来自专栏py+selenium

[笨方法学python]习题51自动化测试笔记

本节自动化测试部分看不大懂,自己每步都打印出来,帮助理解。(代码标红部分为自己加入调试为打印变量值所用)

31020
来自专栏Java后端技术栈

Redis常见的5种不同的数据类型详解

Redis除了可以存储键还可以存储常见的5种数据类型,分别是:String、List、Set、Hash、ZSet。对于Redis的命令有一部分是可以公用的,但是...

12910

扫码关注云+社区

领取腾讯云代金券