Thread常用方法
Thread.yield() 【静态方法】 //放弃本次cpu抢占权,只是本次。
Thread.sleep() 【静态方法】 // 休眠,不释放锁旗标,睡醒接着执行。
----------------------------------------------------------------------------------------
thread1.join() //加入【实例方法】 例如:在thread2的run()中有一个thread1.join(),此时thread2必须等到thread1执行完毕才能继续进行
thread1.interrupt():在其它线程调用此函数可以中断thread1,前提是线程1在sleep、join或者Object的实例方法wait的阻塞状态,通过抛出中断异常。
InterruptException:例如在main线程中执行thread1.interrupt(),如果此时thread1恰好处于线程阻塞状态,阻塞的thread1就会抛出Interruptexception,通过catch捕获可以继续执行,达到终端阻塞线程的目的。
线程同步
最简单的案例:假设有一个票池,售票员不断地去票池取票,直到票池中没有票为止。
思路:票池相当于共享资源,去票池取票一定是同步的,否则会发生不同的售票员取到同一张票,不符合实际情况,所以取票的过程是一个线程安全的过程,可以通过加锁实现。
package com.multithread.java;
public class TicketPool {
private int ticketNo = 100;
// saler从票池取票,当票号为0时说明票池没有票了
// 如果有票,取出该票号,并且票号减1
public synchronized int getTicketNo(){
int t = ticketNo;
if (t == 0) {
return 0;
}
ticketNo--;
return t;
}
}
public class Saler extends Thread {
private TicketPool pool;
private String name;
public Saler(TicketPool pool, String name) {
super();
this.pool = pool;
this.name = name;
}
@Override
public void run() {
while (true) {
int tickNo = pool.getTicketNo();
if (tickNo == 0) {
return;
}
System.out.println(name + " : " + tickNo);
}
}
}
最终测试代码如下:
// 3个售票员同时去票池抢票
public class App {
public static void main(String[] args) {
TicketPool pool = new TicketPool();
Saler bob = new Saler(pool, "bob");
Saler tom = new Saler(pool, "tom");
Saler tim = new Saler(pool, "tim");
bob.start();
tom.start();
tim.start();
}
}
生产者消费者模式
上述简单的例子主要是为了引出生产者消费者模式,此处有三个主要概念:资源池、生产者、消费者。
生产者不断向资源池中生产消息,消费者不断从资源池消费消息,为了保证资源池满了生产者不再生产,资源池中资源没了消费者不再消费消息,我们使用任意object对象的wait-notify机制,一般共享资源对象保证这一机制。
import java.util.LinkedList;
public class TicketPool {
private LinkedList<Integer> pool = new LinkedList<>();
private int MAX = 100;
// 生产者调用,表示向池中生产消息
public synchronized int add(Integer i) {
// 防止wait() 伪唤醒,故使用while
// 如果票池满了,生产者线程进入ticketPool锁旗标的等待队列,通过ticketPool.notify()唤醒
while (pool.size() >= MAX) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果票池没满,生产者向里面添加
pool.add(i);
this.notify(); // 通知等待队列中的某一个线程
return i;
}
// 消费者调用,表示消费池中的消息
public synchronized int remove(){
// 如果票池空了,则消费者线程进入等待队列
while (pool.isEmpty()) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.notify(); // 通知等待队列中的某一个线程
// 如果票池没空,消费者消费信息
return pool.removeFirst();
}
}
public class Consumer extends Thread {
private String name;
private TicketPool pool;
public Consumer(String name, TicketPool pool) {
this.name = name;
this.pool = pool;
}
public void run(){
while(true){
int i = pool.remove();
System.out.println(name + " remove : " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Producer extends Thread {
private static int index = 0;
private String name;
private TicketPool pool;
public Producer(String name, TicketPool pool) {
this.name = name;
this.pool = pool;
}
public void run() {
while (true) {
int i = pool.add(index++);
System.out.println(name + " add : " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
最终测试代码如下:
public class App {
public static void main(String[] args) {
TicketPool pool = new TicketPool();
Producer p1 = new Producer("p1", pool);
Producer p2 = new Producer("p2", pool);
Consumer c1 = new Consumer("c1", pool);
p1.setName("p1");
p2.setName("p2");
c1.setName("c1");
p1.start();
p2.start();
c1.start();
}
}
以上代码如果生产者过多,消费者过少,有可能会出现所有消费者都在等待队列,但是资源池已经满了,导致所有线程都进入了等待队列,形成了死锁。或者消费者过多,生产者过少,也会形成死锁。
LockSupport.park / unpark方法
最后,关于生产者消费者思想,还可以使用LockSupport.park()和unpark()方法。
import java.util.concurrent.locks.LockSupport;
/** 三种线程协作通信的方式:suspend/resume、wait/notify、park/unpark */
public class ProducerConsumerLearn {
/** 包子店 */
public static Object baozidian = null;
/** 正常的wait/notify */
public void waitNotifyTest() throws Exception {
// 启动线程
new Thread(() -> {
synchronized (this) {
while (baozidian == null) { // 如果没包子,则进入等待
try {
System.out.println("1、进入等待");
// Lambda表达式没有this指针
this.wait(); // this指的是 new ProducerConsumerLearn(),其成员变量baozidian作为共享资源
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("2、买到包子,回家");
}).start();
// 3秒之后,生产一个包子
Thread.sleep(3000L);
baozidian = new Object();
synchronized (this) {
this.notifyAll();
System.out.println("3、通知消费者");
}
}
/** 会导致程序永久等待的wait/notify */
public void waitNotifyDeadLockTest() throws Exception {
// 启动线程
new Thread(() -> {
if (baozidian == null) { // 如果没包子,则进入等待
try {
Thread.sleep(5000L);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
synchronized (this) {
try {
System.out.println("1、进入等待");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.println("2、买到包子,回家");
}).start();
// 3秒之后,生产一个包子
Thread.sleep(3000L);
baozidian = new Object();
// 消费者首先通知生产者
synchronized (this) {
this.notifyAll();
System.out.println("3、通知消费者");
}
}
/** 正常的park/unpark */
public void parkUnparkTest() throws Exception {
// 启动线程
Thread consumerThread = new Thread(() -> {
while (baozidian == null) { // 如果没包子,则进入等待
System.out.println("1、进入等待");
LockSupport.park();
}
System.out.println("2、买到包子,回家");
});
consumerThread.start();
// 3秒之后,生产一个包子
Thread.sleep(3000L);
baozidian = new Object();
LockSupport.unpark(consumerThread);
System.out.println("3、通知消费者");
}
/** 死锁的park/unpark */
public void parkUnparkDeadLockTest() throws Exception {
// 启动线程
Thread consumerThread = new Thread(() -> {
if (baozidian == null) { // 如果没包子,则进入等待
System.out.println("1、进入等待");
// 当前线程拿到锁,然后挂起
synchronized (this) {
LockSupport.park();
}
}
System.out.println("2、买到包子,回家");
});
consumerThread.start();
// 3秒之后,生产一个包子
Thread.sleep(3000L);
baozidian = new Object();
// 争取到锁以后,再恢复consumerThread
synchronized (this) {
LockSupport.unpark(consumerThread);
}
System.out.println("3、通知消费者");
}
public static void main(String[] args) throws Exception {
// wait/notify要求再同步关键字里面使用,免去了死锁的困扰,但是一定要先调用wait,再调用notify,否则永久等待了
// new ProducerConsumerLearn().waitNotifyTest();
// new ProducerConsumerLearn().waitNotifyDeadLockTest();
// park/unpark没有顺序要求,但是park并不会释放锁,所有再同步代码中使用要注意
// new ProducerConsumerLearn().parkUnparkTest();
new ProducerConsumerLearn().parkUnparkDeadLockTest();
}
}
Kafka消息队列
// todo