多线程

文章目录

  • 多线程
  • ⼀、并发、并⾏、进程、线程概念
    • 并发与并⾏
    • 线程与进程
      • 线程调度:
  • 二、创建线程
    • 继承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. Siri不行了?微软小冰或许是未来的方向
  2. chcon命令 selinux 配置等
  3. c语言 临时文件作用,c语言函数mktemp()产生唯一临时文件名实例源码介绍
  4. centos 使用 beyond compare 对比工具
  5. 3.调用empty而不是检查size()是否为0
  6. Hibernate备忘录
  7. mysql复制主从集群搭建
  8. PMP读书笔记(第10章)
  9. java----Servlet的生命周期
  10. python 去除panda安装包_沉淀,再出发:python中的pandas包
  11. break语句与continue语句的区别
  12. android插件依赖和aar依赖,Android Studio添加aar依赖的两种方式
  13. svn up出现类似svn: Error converting entry in directory ‘.‘ to UTF-8问题解决
  14. 试试这些方法,误删文件怎么恢复?
  15. DNS 工作原理是什么,域名劫持、域名欺骗、域名污染又是什么
  16. I came, I saw, I hacked Automated Generation of Process-independent Attacks for ICS
  17. 51单片机驱动ds12887c语言,DS12887(时钟日历芯片) c语言驱动程序
  18. 51单片机-RGB灯带
  19. 从零开始VCS+Verdi 安装过程
  20. ipad如何连接以及管理云服务器,类似于在电脑上操作Finallshell

热门文章

  1. 最早的手动式计算机工具是,计算工具发展史
  2. Java 循环-万年历(日历)
  3. Linux教程之bash
  4. Neo4j图数据库高性能入库方式
  5. ORA-27101: shared memory realm does not exist
  6. 如何还原 Active Directory 中已删除的对象
  7. 8大主流OA办公软件比拼,传统VS新秀你PICK谁?
  8. 蓝牙耳机怎么选才不容易踩雷?双11高性价比高续航蓝牙耳机测评
  9. 温州市劳动和社保局信息系统集成招标3000万
  10. 新媒体运营:如何一招实现主动引流,快速获得用户增长? 黎想