多线程

文章目录

  • 多线程
  • ⼀、并发、并⾏、进程、线程概念
    • 并发与并⾏
    • 线程与进程
      • 线程调度:
  • 二、创建线程
    • 继承Thread类
    • 实现Runnable接⼝
  • 二、线程常⽤⽅法
    • 线程的优先级
    • 线程的休眠
    • 线程的让步
  • 三、守护线程
  • 四、线程⽣命周期
    • 五种基本状态
    • 多线程状态之间的转换
  • 五、线程安全
  • 六、死锁
  • 七、线程池
    • 概述
    • 线程池的使⽤
  • 八、线程安全集合
    • CopyOnWriteArrayList

⼀、并发、并⾏、进程、线程概念

并发与并⾏

  • 并发:指两个或多个事件在同⼀个时间段内发⽣。
  • 并⾏:指两个或多个事件在同⼀时刻发⽣(同时发⽣)。
    在操作系统中,安装了多个程序,并⾏指的是在⼀段时间内宏观上有多个程序同时运⾏,这在单 CPU系统中,每⼀时刻只能有⼀道程序执⾏,即微观上这些程序是分时的交替运⾏,只不过是给⼈的感觉是同时运⾏,那是因为分时交替运⾏的时间是⾮常短的。⽽在多个 CPU 系统中,则这些可以并发执⾏的程序便可以分配到多个处理器上(CPU),实现多任务并⾏执⾏,即利⽤每个处理器来处理⼀个可以并发执⾏的程序,这样多个程序便可以同时执⾏。⽬前电脑市场上说的多核 CPU,便是多核处理器,核 越多,并⾏处理的程序越多,能⼤⼤的提⾼电脑运⾏的效率。

线程与进程

  • 进程:是指⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间,⼀个应⽤程序可以同时运⾏多个进程;进程也是程序的⼀次执⾏过程,是系统运⾏程序的基本单位;系统运⾏⼀个程序即是⼀个进程从创建、运⾏到消亡的过程。
  • 线:线程是进程中的⼀个执⾏单元,负责当前进程中程序的执⾏,⼀个进程中⾄少有⼀个线程。
    ⼀个进程中是可以有多个线程的,这个应⽤程序也可以称之为多线程程序。
    简⽽⾔之:⼀个程序运⾏后⾄少有⼀个进程,⼀个进程中可以包含多个线程

线程调度:

  • 分时调度
    所有线程轮流使⽤ CPU 的使⽤权,平均分配每个线程占⽤ CPU 的时间。
  • 抢占式调度
    优先让优先级⾼的线程使⽤ CPU,如果线程的优先级相同,那么会随机选择⼀个(线程随机性),Java使⽤的为抢占式调度。

二、创建线程

继承Thread类

  1. 定义Thread类的⼦类,并重写该类的run()⽅法,该run()⽅法的⽅法体就代表了线程需要完成的任务,因此把run()⽅法称为线程执⾏体。
  2. 创建Thread⼦类的实例,即创建了线程对象
  3. 调⽤线程对象的start()⽅法来启动该线程

示例:

public class MyThread extends Thread {//定义指定线程名称的构造⽅法
public MyThread(String name) {//调⽤⽗类的String参数的构造⽅法,指定线程的名称
super(name);
}
/**
* 重写run⽅法,完成该线程执⾏的逻辑
*/
@Override
public void run() {for (int i = 0; i < 200; i++) {System.out.println(getName()+":"+i);
}
}
}

测试:

public class Demo1 {public static void main(String[] args) {//创建⾃定义线程对象
MyThread mt = new MyThread("新建的线程");
//开启新线程
mt.start();
//在主⽅法中执⾏for循环
for (int i = 0; i < 10; i++) {System.out.println("主线程:"+i);
}
}
}

实现Runnable接⼝

  1. 定义Runnable接⼝的实现类,并重写该接⼝的run()⽅法,该run()⽅法的⽅法体同样是该线程的线程执⾏体。
  2. 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
  3. 调⽤线程对象的start()⽅法来启动线程。

示例:

public class MyRunnable implements Runnable{@Overridepublic void run() {for (int i = 0; i < 200; i++) {System.out.println(Thread.currentThread().getName()+" "+i);}}
}

测试:

public static void main(String[] args) {         //创建⾃定义类对象  线程任务
对象MyRunnable mr = new MyRunnable();//创建线程对象Thread t = new Thread(mr, "新建的线程");t.start();for (int i = 0; i < 20; i++) {System.out.println("主线程" + i);
}}

继承Thread 和实现Runnable的区别
如果⼀个类继承Thread,则不适合资源共享。但是如果实现了Runable接⼝的话,则很容易的实现资源共享。总结:实现Runnable接⼝⽐继承Thread类所具有的优势:

  1. 适合多个相同的程序代码的线程去共享同⼀个资源。
  2. 可以避免java中的单继承的局限性。
  3. 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独⽴。

二、线程常⽤⽅法

⽅法名 描述
public static void sleep(long millis) 当前线程主动休眠 millis 毫秒。
public static void yield() 当前线程主动放弃时间⽚,回到就绪状态,竞争下⼀次时间⽚。
public final void join() 允许其他线程加⼊到当前线程中。
public void setPriority(int) 线程优先级为1-10,默认为5,优先级越⾼,表示获取CPU机会越多。(最小1、最大10)
public void setDaemon(boolean) 设置为守护线程线程有两类:⽤户线程(前台线程)、守护线程(后台线程)

线程的优先级

  • 我们可以通过传递参数给线程的 setPriority() 来设置线程的优先级别
  • 调整线程优先级:Java线程有优先级,优先级⾼的线程会获得较多的运⾏机会。优先级 : 只能反映 线程 的 中或者是 紧急程度 , 不能决定 是否⼀定先执⾏Java线程的优先级⽤整数表示,取值范围是1~10,Thread类有以下三个静态常量:
static int MAX_PRIORITY
线程可以具有的最⾼优先级,取值为10。
static int MIN_PRIORITY
线程可以具有的最低优先级,取值为1。
static int NORM_PRIORITY
分配给线程的默认优先级,取值为5。

示例:

/**
* 优先级
*
*/
public class PriorityThread extends Thread{@Override
public void run() {for(int i=0;i<50;i++) {System.out.println(Thread.currentThread().getName()+"============"+i);
}
}
}

测试:

public class TestPriority {public static void main(String[] args) {PriorityThread p1=new PriorityThread();
p1.setName("p1");
PriorityThread p2=new PriorityThread();
p2.setName("p2");
PriorityThread p3=new PriorityThread();
p3.setName("p3");
p1.setPriority(1);
p3.setPriority(10);
//启动
p1.start();
p2.start();
p3.start();
}
}

线程的休眠

使⽤线程的 sleep() 可以使线程休眠指定的毫秒数,在休眠结束的时候继续执⾏线程
示例:

public class SleepThread extends Thread {@Overridepublic void run() {String[] names = new String[]{"zs", "ls", "ww", "z6"};int index = (int) (Math.random() * 4);for (int i = 3; i > 0; i--) {System.out.println(i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("倒计时:" + i);}System.out.println("抽中学员为:" + names[index]);}
}

测试:

    public static void main(String[] args) {new SleepThread().start();}

线程的让步

  • Thread.yield() ⽅法作⽤是:暂停当前正在执⾏的线程对象(及放弃当前拥有的cup资源),并执⾏其他线程。
  • yield() 做的是让当前运⾏线程回到可运⾏状态,以允许具有相同优先级的其他线程获得运⾏机会。因此,使⽤ yield() 的⽬的是让相同优先级的线程之间能适当的轮转执⾏。但是,实际中⽆法保证 yield() 达到让步⽬的,因为让步的线程还有可能被线程调度程序再次选中。
  • 案例:创建两个线程A,B,分别各打印1000次,从1开始每次增加1,其中B⼀个线程,每打印⼀次,就yield⼀次,观察实验结果.

示例:

class Task1 implements Runnable {@Overridepublic void run() {for (int i = 0; i < 200; i++) {System.out.println("A:" + i);}}
}class Task2 implements Runnable {@Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println("B:" + i);Thread.yield();}}
}public class Demo {public static void main(String[] args) {new Thread(new Task2()).start();new Thread(new Task1()).start();}
}

三、守护线程

守护线程.setDaemon(true):设置守护线程
线程有两类:⽤户线程(前台线程)、守护线程(后台线程)
如果程序中所有前台线程都执⾏完毕了,后台线程会⾃动结束
垃圾回收器线程属于守护线程
例如:

public class DeamonThread extends Thread {@Overridepublic void run() {for (int i = 0; i < 50; i++) {System.out.println(Thread.currentThread().getName() + "----------" + i);try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}
}

测试:

public static void main(String[] args) {//创建线程(默认前台线程)DeamonThread d1 = new DeamonThread();//设置线程为守护线程d1.setDaemon(true);//主线程结束便结束了d1.start();for (int i = 0; i < 10; i++) {System.out.println("主线程:----------" + i);try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}

四、线程⽣命周期

五种基本状态

当线程被创建并启动以后,它既不是⼀启动就进⼊了执⾏状态,也不是⼀直处于执⾏状态。

  1. 新建状态(New)
    当线程对象对创建后,即进⼊了新建状态,如: Thread t = new MyThread();
  2. 就绪状态(Runnable)
    当调⽤线程对象的start()⽅法( t.start(); ),线程即进⼊就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执⾏,并不是说执⾏了 t.start() 此线程⽴即就会执⾏;
  3. 运⾏状态(Running)
    当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执⾏,即进⼊到运⾏状态。注:就 绪状态是进⼊到运⾏状态的唯⼀⼊⼝,也就是说,线程要想进⼊运⾏状态执⾏,⾸先必须处于就绪状态中;
  4. 阻塞状态(Blocked)
    处于运⾏状态中的线程由于某种原因,暂时放弃对CPU的使⽤权,停⽌执⾏,此时进⼊阻塞状态,直到其进⼊到就绪状态,才 有机会再次被CPU调⽤以进⼊到运⾏状态。根据阻塞产⽣的原因不同,阻塞状态⼜可以分为三种:

    • 等待阻塞:运⾏状态中的线程执⾏wait()⽅法,使本线程进⼊到等待阻塞状态;
    • 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占⽤),它会进⼊同步阻塞状态;
    • 其他阻塞 – 通过调⽤线程的sleep()或join()或发出了I/O请求时,线程会进⼊到阻塞状态。当sleep()状态超时、join()等待线程终⽌或者超时、或者I/O处理完毕时,线程重新转⼊就绪状态。
  5. 死亡状态(Dead)
    线程执⾏完了或者因异常退出了run()⽅法,该线程结束⽣命周期。

多线程状态之间的转换

就绪状态转换为运⾏状态:当此线程得到处理器资源;
运⾏状态转换为就绪状态:当此线程主动调⽤yield()⽅法或在运⾏过程中失去处理器资源。
运⾏状态转换为死亡状态:当此线程线程执⾏体执⾏完毕或发⽣了异常。
此处需要特别注意的是:当调⽤线程的yield()⽅法时,线程从运⾏状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有⼀定的随机性,因此,可能会出现A线程调⽤了yield()⽅法后,接下来CPU仍然调度了A线程的情况。

五、线程安全

  1. 线程不安全:

    • 当多线程并发访问临界资源时,如果破坏原⼦操作,可能会造成数据不⼀致。
    • 临界资源:共享资源(同⼀对象),⼀次仅允许⼀个线程使⽤,才可保证其正确性。
    • 原⼦操作:不可分割的多步操作,被视作⼀个整体,其顺序和步骤不可打乱或缺省。

示例:

 private int number = 20;//每个窗⼝卖票的操作//窗⼝永远开启@Overridepublic void run() {while (true) {//有票可以卖//出票操作if (number > 0) {//使⽤sleep模拟⼀下出票时间 //模拟⼀下出票的时间try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在卖票:" + number--);}}}
public static void main(String[] args) throws Exception {TicketRunnable t = new TicketRunnable();Thread t1 = new Thread(t, "窗⼝1");Thread t2 = new Thread(t, "窗⼝2");Thread t3 = new Thread(t, "窗⼝3");//3个窗⼝同时卖票t1.start();t2.start();t3.start();}

为了保证每个线程都能正常执⾏原⼦操作,Java引⼊了线程同步机制。那么怎么去使⽤呢?有三种⽅式完成同步操作:

  1. 同步代码块。
  2. 同步⽅法。
  3. 锁机制。

同步代码块

语法:
synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原⼦操作)
}

同步锁:
对象的同步锁只是⼀个概念,可以想象为在对象上标记了⼀个锁.

  1. 锁对象 可以是任意类型。
  2. 多个线程对象 要使⽤同⼀把锁。
    注意:在任何时候,最多允许⼀个线程拥有同步锁,谁拿到锁就进⼊代码块,其他的线程只能在外等着(BLOCKED)。

示例:

public class Ticket2 implements Runnable {private int ticket = 50;Object lock = new Object();//每个窗⼝卖票的操作//永远开启@Overridepublic void run() {while (true) {//有票可以卖synchronized (lock) {//synchronized (this) {//this ---当前对象if (ticket > 0) {//出票操作//使⽤sleep模拟⼀下出票时间try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在 卖票:" + ticket--);}}}}public static void main(String[] args) {Ticket2 ticket2 = new Ticket2();Thread t1 = new Thread(ticket2, "窗⼝1");Thread t2 = new Thread(ticket2, "窗⼝2");Thread t3 = new Thread(ticket2, "窗⼝3");
//3个窗⼝同时卖票t1.start();t2.start();t3.start();}}

同步⽅法 :使⽤synchronized修饰的⽅法,就叫做同步⽅法,保证A线程执⾏该⽅法的时候,其他线程只能在⽅法外等着。

语法:
synchronized 返回值类型 ⽅法名称(形参列表){ //对当前对象(this)加锁
// 代码(原⼦操作)
}

Lock

  • JDK5加⼊,与synchronized⽐较,显示定义,结构更灵活。
  • 提供更多实⽤性⽅法,功能更强⼤、性能更优越。

常⽤⽅法:

⽅法名 描述
void lock() 获取锁,如锁被占⽤,则等待。
boolean tryLock() 尝试获取锁(成功返回true。失败返回false,不阻塞)。
void unlock() 释放锁。

ReentrantLock:

  • Lock接⼝的实现类,与synchronized⼀样具有互斥锁功能。

示例:

public class MyList {//创建锁
private Lock lock = new ReentrantLock();
private String[] str = {"A","B","","",""};
private int count = 2;
public void add(String value){//当没有锁的时候,会出现覆盖的情况
str[count] = value;
try {Thread.sleep(100);
} catch (InterruptedException e) {e.printStackTrace();
}
count++;
System.out.println(Thread.currentThread().getName()+"添加了"+value);
// lock.lock();
// try {// str[count] = value;
// try {// Thread.sleep(100);
// } catch (InterruptedException e) {// e.printStackTrace();
// }
// count++;
// System.out.println(Thread.currentThread().getName()+"添加
了"+value);
// }finally {// lock.unlock();
// }
}
public String[] getStr(){return str;
}
}

测试:

public class TestMyList {public static void main(String[] args) throws InterruptedException {MyList myList = new MyList();
//
Thread t1 =new Thread(new Runnable() {@Override
public void run() {myList.add("hello");
}
});
t1.start();
Thread t2 = new Thread(new Runnable() {@Override
public void run() {myList.add("world");
}
});
t2.start();
t1.join();
t2.join();
String[] str = myList.getStr();
for (String s : str) {System.out.println("s:"+s);
}
}
}

六、死锁

多个线程同时被阻塞,它们中的⼀个或者全部都在等待某个资源被释放。由于线程被⽆限期地阻塞,因此程序不可能正常终⽌。
示例:

public class DeadLockDemo {private static Object lock1 = new Object();//锁1,资源1
private static Object lock2 = new Object();//锁2,资源2
public static void main(String[] args) {//启动⼀个线程
new Thread(new Runnable() {@Override
public void run() {synchronized(lock1){System.out.println(Thread.currentThread().getName()+"拿到
了锁1,资源1");
try {Thread.sleep(1000);
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"等待
锁2,资源2");
synchronized (lock2){System.out.println(Thread.currentThread().getName()+"拿到了锁2,资源2");
}
}
}
},"线程1").start();
//产⽣死锁的线程
// new Thread(new Runnable() {// @Override
// public void run() {// synchronized(lock2){// System.out.println(Thread.currentThread().getName()+"拿
//到了锁2,资源2");
// try {// Thread.sleep(1000);
// } catch (InterruptedException e) {// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"等
//待锁1,资源1");
// synchronized (lock1){//
//System.out.println(Thread.currentThread().getName()+"拿到了锁1,资源1");
// }
// }
// }
// },"线程2").start();
}
}

破坏死锁

//破坏死锁
new Thread(new Runnable() {@Override
public void run() {synchronized(lock1){System.out.println(Thread.currentThread().getName()+"拿到
了锁1,资源1");
try {Thread.sleep(1000);
} catch (InterruptedException e) {e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"等待
锁2,资源2");
synchronized (lock2){System.out.println(Thread.currentThread().getName()+"拿到了锁2,资源2");
}
}
}
},"线程2").start();

七、线程池

概述

其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线程对象的操作,⽆需反复创建线程⽽消耗过多资源。
合理利⽤线程池能够带来三个好处:

  1. 降低资源消耗。减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多个任务。
  2. 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
  3. 提⾼线程的可管理性。可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约1MB内存,线程开的越多,消耗的内存也就越⼤,最后死机)。

线程池的使⽤

Java⾥⾯线程池的顶级接⼝是 java.util.concurrent.Executor ,但是严格意义上讲 Executor并不是⼀个线程池,⽽只是⼀个执⾏线程的⼯具。真正的线程池接⼝是java.util.concurrent.ExecutorService 。
要配置⼀个线程池是⽐较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在java.util.concurrent.Executors 线程⼯⼚类⾥⾯提供了⼀些静态⼯⼚,⽣成⼀些常⽤的线程池。官⽅建议使⽤Executors⼯程类来创建线程池对象。
Java类库提供了许多静态⽅法来创建⼀个线程池:
Executors类中创建线程池的⽅法如下:
a、 newFixedThreadPool 创建⼀个固定⻓度的线程池,当到达线程最⼤数量时,线程池的规模将不再变化。
b、 newCachedThreadPool 创建⼀个可缓存的线程池,如果当前线程池的规模超出了处理需求,将回收空的线程;当需求增加时,会增加线程数量;线程池规模⽆限制。
c、 newSingleThreadPoolExecutor 创建⼀个单线程的Executor,确保任务对了,串⾏执⾏
d、 newScheduledThreadPool 创建⼀个固定⻓度的线程池,⽽且以延迟或者定时的⽅式来执⾏,类似Timer;
使⽤线程池中线程对象的步骤:

  1. 创建线程池对象。
  2. 创建Runnable接⼝⼦类对象。(task)
  3. 提交Runnable接⼝⼦类对象。(take task)
    获取到了⼀个线程池ExecutorService 对象,定义了⼀个使⽤线程池对象的⽅法如下:public Future<?> submit(Runnable task) :获取线程池中的某⼀个线程对象,并执⾏Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤。
  4. 关闭线程池(⼀般不做)。

示例:

class MyThread 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("教完后,教练回到了游泳池");
}
public static void main(String[] args) {// //创建⼀个包含固定数量的线程池对象
// ExecutorService executorService = Executors.newFixedThreadPool(2);
// //创建⼀个包含单条线程的线程池
// ExecutorService executorService =
Executors.newSingleThreadExecutor();
// //创建⼀个带缓冲区的线程池,会根据需求创建线程
// ExecutorService executorService = Executors.newCachedThreadPool();
ScheduledExecutorService scheduledExecutorService =
Executors.newScheduledThreadPool(10);
//创建Runnable实例对象
MyThread r = new MyThread();
//⾃⼰创建线程的⽅式
// Thread t = new Thread(r);
// t.start();
// //从线程池中获取线程对象,然后调⽤MyThread的run⽅法
// executorService.submit(r);
// //再获取⼀个线程对象,
// executorService.submit(r);
// executorService.submit(r);
// //注意:submit⽅法调⽤后,程序并不终⽌,因为线程次控制了线程的关闭
// //使⽤完,⼜归还到了线程池中,
//
// //关闭线程池
// executorService.shutdown();
for (int i = 0; i < 10; i++) {scheduledExecutorService.schedule(r,10, TimeUnit.SECONDS);//延迟10
秒执⾏
}
scheduledExecutorService.shutdown();;//执⾏到此处并不会⻢上关闭连接池
// while(!scheduledExecutorService.isTerminated()){//
// }
System.out.println("Main Thread finished at"+new Date());
}
}

八、线程安全集合

CopyOnWriteArrayList

  • 线程安全的ArrayList,加强版读写分离。
  • 写有锁,读⽆锁,读写之间不阻塞,优于读写锁。
  • 写⼊时,先copy⼀个容器副本、再添加新元素,最后替换引⽤。
  • 使⽤⽅式与ArrayList⽆异。

示例:

public static void main(String[] args) {//1创建集合CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();//2使⽤多线程操作ExecutorService es = Executors.newFixedThreadPool(5);//3提交任务for (int i = 0; i < 5; i++) {es.submit(new Runnable() {@Overridepublic void run() {for (int j = 0; j < 10; j++) {list.add(Thread.currentThread().getName() + "...." + newRandom().nextInt(1000));}}});}//4关闭线程池es.shutdown();while (!es.isTerminated()) {}//5打印结果System.out.println("元素个数:" + list.size());for (String string : list) {System.out.println(string);}}

CopyOnWriteArrayList使⽤了⼀种叫写时复制的⽅法,当有新元素添加到CopyOnWriteArrayList时,先从原有的数组中拷⻉⼀份出来,然后在新的数组做写操作,写完之后,再将原来的数组引⽤指向到新数组。

Java多线程、并发、进程和锁的详细讲解相关推荐

  1. Java 多线程 并发编程

    转载自  Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进 ...

  2. 2021全新Java多线程并发入门到精通,一篇就能学会

    目录 一, JAVA 多线程并发 1,JAVA 并发知识库 2,JAVA 线程实现/创建方式 (1) 继承 Thread 类 (2)实现 Runnable 接口. (3)ExecutorService ...

  3. Java多线程并发编程--Java并发包(JUC)

    Java多线程并发–Java并发包(JUC) 前言 前一篇文章中,笔者已经介绍了Java多线程的一些基础知识,但是想要成为一名中高级Java程序员还必须懂得Java并发包(JUC)的知识点,而且JUC ...

  4. JAVA 多线程并发超详解

    JAVA 多线程并发超详解(未完,下一篇文章还有) 1. JAVA 多线程并发 1.1.1. JAVA 并发知识库 1.1.2. JAVA 线程实现/创建方式 1.1.2.1. 继承 Thread 类 ...

  5. Java多线程并发技术

    Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...

  6. Java多线程并发编程

    一.线程池 1.1.什么是线程池 线程池是一种多线程的处理方式,利用已有线程对象继续服务新的任务(按照一定的执行策略),而不是频繁地创建销毁线程对象,由此提高服务的吞吐能力,减少CPU的闲置时间.具体 ...

  7. delay在java中有什么用_DelayQueue怎么在Java多线程并发开发中使用

    DelayQueue怎么在Java多线程并发开发中使用 发布时间:2020-12-05 17:29:31 来源:亿速云 阅读:56 作者:Leah 这篇文章给大家介绍DelayQueue怎么在Java ...

  8. java火箭应用_从火箭发场景来学习Java多线程并发闭锁对象

    原标题:从火箭发场景来学习Java多线程并发闭锁对象 从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; ...

  9. java 闭锁_从火箭发场景来学习Java多线程并发闭锁对象

    从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size-这种方式来实现.但是在多线程并发的情 ...

最新文章

  1. 【数据库】兴唐第二十八节课零散知识点汇总
  2. 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)
  3. Java Switch Statement
  4. sql脚本对比工具_Java开发中用到的数据库迁移工具(flyway)
  5. 设计撑百万并发的数据库架构
  6. 转载]Cyclone II JTAG ASP 配置下载程序
  7. Git CMD - diff: Show changes between commits, commit and working tree, etc
  8. 高可用Kubernetes集群原理介绍
  9. 计算机科学研究算法的局限性,计算机科学中若干难解问题的量子算法的分析.pdf...
  10. 【Spring配置文件】Spring定时器的使用及配置
  11. Object.keys()的用法
  12. Android高德混淆问题
  13. 常见的几种页面内容布局方式
  14. 第八次网页前端培训(JavaScript)
  15. Advanced Zip Password Recovery下载
  16. 马化腾从CFIDO到QQ(CFIDO BBS回忆录)
  17. 帝国cms:评论列表模板中调用热门文章或是推荐文章的js方法
  18. tensorflow代码学习:CTC 代码解析
  19. 40款非常酷的国外创意名片设计欣赏(下)
  20. 爬虫系列1:Requests+Xpath 爬取豆瓣电影TOP

热门文章

  1. 模拟电子技术经验公式-放大的概念和放大电路的主要性能指标
  2. 2021年8月27日10点29分 常见漏洞
  3. Python实现蒙特卡洛模拟
  4. [墨者安全]解析2019年国内DDOS第一季度报告
  5. argparse 或者 click中出现‘int‘ object is not iterable
  6. 多表代替密码破解初探
  7. 设计挂了吗?(设计已死? Is Design Dead?)译文
  8. MacOS 重置网络配置
  9. win10怎样查看计算机用户,Win10系统如何查看和保存全部用户账户信息?
  10. 使用PHPEMS在Windows系统上搭建考试平台的步骤