Java多线程、并发、进程和锁的详细讲解
多线程
文章目录
- 多线程
- ⼀、并发、并⾏、进程、线程概念
- 并发与并⾏
- 线程与进程
- 线程调度:
- 二、创建线程
- 继承Thread类
- 实现Runnable接⼝
- 二、线程常⽤⽅法
- 线程的优先级
- 线程的休眠
- 线程的让步
- 三、守护线程
- 四、线程⽣命周期
- 五种基本状态
- 多线程状态之间的转换
- 五、线程安全
- 六、死锁
- 七、线程池
- 概述
- 线程池的使⽤
- 八、线程安全集合
- CopyOnWriteArrayList
⼀、并发、并⾏、进程、线程概念
并发与并⾏
- 并发:指两个或多个事件在同⼀个时间段内发⽣。
- 并⾏:指两个或多个事件在同⼀时刻发⽣(同时发⽣)。
在操作系统中,安装了多个程序,并⾏指的是在⼀段时间内宏观上有多个程序同时运⾏,这在单 CPU系统中,每⼀时刻只能有⼀道程序执⾏,即微观上这些程序是分时的交替运⾏,只不过是给⼈的感觉是同时运⾏,那是因为分时交替运⾏的时间是⾮常短的。⽽在多个 CPU 系统中,则这些可以并发执⾏的程序便可以分配到多个处理器上(CPU),实现多任务并⾏执⾏,即利⽤每个处理器来处理⼀个可以并发执⾏的程序,这样多个程序便可以同时执⾏。⽬前电脑市场上说的多核 CPU,便是多核处理器,核 越多,并⾏处理的程序越多,能⼤⼤的提⾼电脑运⾏的效率。
线程与进程
- 进程:是指⼀个内存中运⾏的应⽤程序,每个进程都有⼀个独⽴的内存空间,⼀个应⽤程序可以同时运⾏多个进程;进程也是程序的⼀次执⾏过程,是系统运⾏程序的基本单位;系统运⾏⼀个程序即是⼀个进程从创建、运⾏到消亡的过程。
- 线程:线程是进程中的⼀个执⾏单元,负责当前进程中程序的执⾏,⼀个进程中⾄少有⼀个线程。
⼀个进程中是可以有多个线程的,这个应⽤程序也可以称之为多线程程序。
简⽽⾔之:⼀个程序运⾏后⾄少有⼀个进程,⼀个进程中可以包含多个线程
线程调度:
- 分时调度
所有线程轮流使⽤ CPU 的使⽤权,平均分配每个线程占⽤ CPU 的时间。 - 抢占式调度
优先让优先级⾼的线程使⽤ CPU,如果线程的优先级相同,那么会随机选择⼀个(线程随机性),Java使⽤的为抢占式调度。
二、创建线程
继承Thread类
- 定义Thread类的⼦类,并重写该类的run()⽅法,该run()⽅法的⽅法体就代表了线程需要完成的任务,因此把run()⽅法称为线程执⾏体。
- 创建Thread⼦类的实例,即创建了线程对象
- 调⽤线程对象的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接⼝
- 定义Runnable接⼝的实现类,并重写该接⼝的run()⽅法,该run()⽅法的⽅法体同样是该线程的线程执⾏体。
- 创建Runnable实现类的实例,并以此实例作为Thread的target来创建Thread对象,该Thread对象才是真正的线程对象。
- 调⽤线程对象的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类所具有的优势:
- 适合多个相同的程序代码的线程去共享同⼀个资源。
- 可以避免java中的单继承的局限性。
- 增加程序的健壮性,实现解耦操作,代码可以被多个线程共享,代码和线程独⽴。
二、线程常⽤⽅法
⽅法名 | 描述 |
---|---|
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();}}}
四、线程⽣命周期
五种基本状态
当线程被创建并启动以后,它既不是⼀启动就进⼊了执⾏状态,也不是⼀直处于执⾏状态。
- 新建状态(New)
当线程对象对创建后,即进⼊了新建状态,如: Thread t = new MyThread(); - 就绪状态(Runnable)
当调⽤线程对象的start()⽅法( t.start(); ),线程即进⼊就绪状态。处于就绪状态的线程,只是说明此线程已经做好了准备,随时等待CPU调度执⾏,并不是说执⾏了 t.start() 此线程⽴即就会执⾏; - 运⾏状态(Running)
当CPU开始调度处于就绪状态的线程时,此时线程才得以真正执⾏,即进⼊到运⾏状态。注:就 绪状态是进⼊到运⾏状态的唯⼀⼊⼝,也就是说,线程要想进⼊运⾏状态执⾏,⾸先必须处于就绪状态中; - 阻塞状态(Blocked)
处于运⾏状态中的线程由于某种原因,暂时放弃对CPU的使⽤权,停⽌执⾏,此时进⼊阻塞状态,直到其进⼊到就绪状态,才 有机会再次被CPU调⽤以进⼊到运⾏状态。根据阻塞产⽣的原因不同,阻塞状态⼜可以分为三种:- 等待阻塞:运⾏状态中的线程执⾏wait()⽅法,使本线程进⼊到等待阻塞状态;
- 同步阻塞 – 线程在获取synchronized同步锁失败(因为锁被其它线程所占⽤),它会进⼊同步阻塞状态;
- 其他阻塞 – 通过调⽤线程的sleep()或join()或发出了I/O请求时,线程会进⼊到阻塞状态。当sleep()状态超时、join()等待线程终⽌或者超时、或者I/O处理完毕时,线程重新转⼊就绪状态。
- 死亡状态(Dead)
线程执⾏完了或者因异常退出了run()⽅法,该线程结束⽣命周期。
多线程状态之间的转换
就绪状态转换为运⾏状态:当此线程得到处理器资源;
运⾏状态转换为就绪状态:当此线程主动调⽤yield()⽅法或在运⾏过程中失去处理器资源。
运⾏状态转换为死亡状态:当此线程线程执⾏体执⾏完毕或发⽣了异常。
此处需要特别注意的是:当调⽤线程的yield()⽅法时,线程从运⾏状态转换为就绪状态,但接下来CPU调度就绪状态中的哪个线程具有⼀定的随机性,因此,可能会出现A线程调⽤了yield()⽅法后,接下来CPU仍然调度了A线程的情况。
五、线程安全
- 线程不安全:
- 当多线程并发访问临界资源时,如果破坏原⼦操作,可能会造成数据不⼀致。
- 临界资源:共享资源(同⼀对象),⼀次仅允许⼀个线程使⽤,才可保证其正确性。
- 原⼦操作:不可分割的多步操作,被视作⼀个整体,其顺序和步骤不可打乱或缺省。
示例:
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引⼊了线程同步机制。那么怎么去使⽤呢?有三种⽅式完成同步操作:
- 同步代码块。
- 同步⽅法。
- 锁机制。
同步代码块
语法:
synchronized(临界资源对象){ //对临界资源对象加锁
//代码(原⼦操作)
}
同步锁:
对象的同步锁只是⼀个概念,可以想象为在对象上标记了⼀个锁.
- 锁对象 可以是任意类型。
- 多个线程对象 要使⽤同⼀把锁。
注意:在任何时候,最多允许⼀个线程拥有同步锁,谁拿到锁就进⼊代码块,其他的线程只能在外等着(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();
七、线程池
概述
其实就是⼀个容纳多个线程的容器,其中的线程可以反复使⽤,省去了频繁创建线程对象的操作,⽆需反复创建线程⽽消耗过多资源。
合理利⽤线程池能够带来三个好处:
- 降低资源消耗。减少了创建和销毁线程的次数,每个⼯作线程都可以被重复利⽤,可执⾏多个任务。
- 提⾼响应速度。当任务到达时,任务可以不需要的等到线程创建就能⽴即执⾏。
- 提⾼线程的可管理性。可以根据系统的承受能⼒,调整线程池中⼯作线线程的数⽬,防⽌因为消耗过多的内存,⽽把服务器累趴下(每个线程需要⼤约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;
使⽤线程池中线程对象的步骤:
- 创建线程池对象。
- 创建Runnable接⼝⼦类对象。(task)
- 提交Runnable接⼝⼦类对象。(take task)
获取到了⼀个线程池ExecutorService 对象,定义了⼀个使⽤线程池对象的⽅法如下:public Future<?> submit(Runnable task) :获取线程池中的某⼀个线程对象,并执⾏Future接⼝:⽤来记录线程任务执⾏完毕后产⽣的结果。线程池创建与使⽤。 - 关闭线程池(⼀般不做)。
示例:
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多线程、并发、进程和锁的详细讲解相关推荐
- Java 多线程 并发编程
转载自 Java 多线程 并发编程 一.多线程 1.操作系统有两个容易混淆的概念,进程和线程. 进程:一个计算机程序的运行实例,包含了需要执行的指令:有自己的独立地址空间,包含程序内容和数据:不同进 ...
- 2021全新Java多线程并发入门到精通,一篇就能学会
目录 一, JAVA 多线程并发 1,JAVA 并发知识库 2,JAVA 线程实现/创建方式 (1) 继承 Thread 类 (2)实现 Runnable 接口. (3)ExecutorService ...
- Java多线程并发编程--Java并发包(JUC)
Java多线程并发–Java并发包(JUC) 前言 前一篇文章中,笔者已经介绍了Java多线程的一些基础知识,但是想要成为一名中高级Java程序员还必须懂得Java并发包(JUC)的知识点,而且JUC ...
- JAVA 多线程并发超详解
JAVA 多线程并发超详解(未完,下一篇文章还有) 1. JAVA 多线程并发 1.1.1. JAVA 并发知识库 1.1.2. JAVA 线程实现/创建方式 1.1.2.1. 继承 Thread 类 ...
- Java多线程并发技术
Java多线程并发技术 参考文献: http://blog.csdn.net/aboy123/article/details/38307539 http://blog.csdn.net/ghsau/a ...
- Java多线程并发编程
一.线程池 1.1.什么是线程池 线程池是一种多线程的处理方式,利用已有线程对象继续服务新的任务(按照一定的执行策略),而不是频繁地创建销毁线程对象,由此提高服务的吞吐能力,减少CPU的闲置时间.具体 ...
- delay在java中有什么用_DelayQueue怎么在Java多线程并发开发中使用
DelayQueue怎么在Java多线程并发开发中使用 发布时间:2020-12-05 17:29:31 来源:亿速云 阅读:56 作者:Leah 这篇文章给大家介绍DelayQueue怎么在Java ...
- java火箭应用_从火箭发场景来学习Java多线程并发闭锁对象
原标题:从火箭发场景来学习Java多线程并发闭锁对象 从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; ...
- java 闭锁_从火箭发场景来学习Java多线程并发闭锁对象
从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size-这种方式来实现.但是在多线程并发的情 ...
最新文章
- 【数据库】兴唐第二十八节课零散知识点汇总
- 史上最简单的SpringCloud教程 | 第五篇: 路由网关(zuul)
- Java Switch Statement
- sql脚本对比工具_Java开发中用到的数据库迁移工具(flyway)
- 设计撑百万并发的数据库架构
- 转载]Cyclone II JTAG ASP 配置下载程序
- Git CMD - diff: Show changes between commits, commit and working tree, etc
- 高可用Kubernetes集群原理介绍
- 计算机科学研究算法的局限性,计算机科学中若干难解问题的量子算法的分析.pdf...
- 【Spring配置文件】Spring定时器的使用及配置
- Object.keys()的用法
- Android高德混淆问题
- 常见的几种页面内容布局方式
- 第八次网页前端培训(JavaScript)
- Advanced Zip Password Recovery下载
- 马化腾从CFIDO到QQ(CFIDO BBS回忆录)
- 帝国cms:评论列表模板中调用热门文章或是推荐文章的js方法
- tensorflow代码学习:CTC 代码解析
- 40款非常酷的国外创意名片设计欣赏(下)
- 爬虫系列1:Requests+Xpath 爬取豆瓣电影TOP
热门文章
- 模拟电子技术经验公式-放大的概念和放大电路的主要性能指标
- 2021年8月27日10点29分 常见漏洞
- Python实现蒙特卡洛模拟
- [墨者安全]解析2019年国内DDOS第一季度报告
- argparse 或者 click中出现‘int‘ object is not iterable
- 多表代替密码破解初探
- 设计挂了吗?(设计已死? Is Design Dead?)译文
- MacOS 重置网络配置
- win10怎样查看计算机用户,Win10系统如何查看和保存全部用户账户信息?
- 使用PHPEMS在Windows系统上搭建考试平台的步骤