JUC多线程编程总结
JUC:java.util.concurrent多线程工具包的使用
测试demo:https://gitee.com/aqq/mytest.git
1、守护线程
java中有两类线程:用户线程、守护线程
只有当前JVM中存在用户线程没结束,守护线程就会全部工作,只有当最后一个用户线程结束之后,守护线程随着JVM结束工作,比如JVM中的垃圾回收(GC)就是守护线程。
Thread thread = new Thread();
// 设定 thread 为 守护线程,default false(非守护线程)
thread.setDaemon(true)
//判断某个线程是否是守护线程用isDaemon
thread.isDaemon();
注意:
1、thread.setDaemon(true)必须在thread.start()之前,否则会抛出IllegalThreadStateException异常,不能把正在运行的线程设置为守护线程
2、在Daemon线程中产生的新线程也是守护线程
3、不要任务所有的线程都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑
2、Synchronized的使用
模拟三个售票员卖30张票的场景
/**
* 模拟 三个售票员卖30张票的情景
* 1、首先定义资源,以及资源属性
*
*/
public class SaleTickets {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(new SaleThread(tickets), "AA");
Thread thread2 = new Thread(new SaleThread(tickets), "BB");
Thread thread3 = new Thread(new SaleThread(tickets), "CC");
thread1.start();
thread2.start();
thread3.start();
}
}
class SaleThread implements Runnable{
private Tickets tickets;
public SaleThread(Tickets tickets) {
this.tickets = tickets;
}
3、Lock的使用
ReentrantLock(可重入锁)的使用:同样是模拟三个售票员卖30张票的场景
调用lock.lock()方法,必须要有释放锁的调用(lock.unlock()),最好写在finally中
public class LSaleTickets {
public static void main(String[] args) {
Tickets tickets = new Tickets();
Thread thread1 = new Thread(new SaleThread(tickets), "AA");
Thread thread2 = new Thread(new SaleThread(tickets), "BB");
Thread thread3 = new Thread(new SaleThread(tickets), "CC");
thread1.start();
thread2.start();
thread3.start();
}
}
/**
* 创建资源对象
**/
class Tickets{
//票数量
private int numbers = 30;
//创建可重入锁
private final ReentrantLock lock = new ReentrantLock();
//买票
public void sale(){
lock.lock();
try{
//判断是否有票
if (numbers > 0) {
System.out.println(Thread.currentThread().getName() + "卖出:1张,剩余:" + (numbers--) + "张");
}
}finally {
lock.unlock();
}
}
}
//定义卖票的线程
class SaleThread implements Runnable{
private Tickets tickets;
public SaleThread(Tickets tickets) {
this.tickets = tickets;
}
4、线程间通信,虚假唤醒
线程间通信,主要是通过wait()和notifyAll()方法,或者Condition类的await()和signalAll()来控制线程的等待和唤醒,从而实现线程间的通信
案例1:通过synchronized关键字和wait和notify方法实现一个+1,一个-1的操作
package com.vike.juctest.sync;
/**
* 模拟线程间通信
* if(num!=0){
* this.wait();//在哪里睡,就在哪里唤醒
* }
* 采用if的话会存在虚假唤醒的问题,因为if条件只判断一次,应该为while
* while(num!=0){
* this.wait();
* }
*/
public class ThreadTransf {
public static void main(String[] args) {
Share share = new Share();
new Thread(()->{
for (int i =0;i<10;i++){
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"AA").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"BB").start();
new Thread(()->{
for (int i =0;i<10;i++){
try {
share.incr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"CC").start();
new Thread(()->{
for (int i=0;i<10;i++){
try {
share.decr();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"DD").start();
}
}
/**
* 创建资源类
*/
class Share{
private int number = 0;
/**
* +1 方法
*/
public synchronized void incr() throws InterruptedException {
while (number != 0) {
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"::数据+1后::"+number);
this.notifyAll();
}
/**
* -1 方法
*/
public synchronized