-
进程和线程是什么?
-
什么是并行和并发?
并发:同一时段,多个任务在单个cpu上交替执行
并行:同一时段,多个任务在多个cpu上同时执行
@Slf4j(topic = "c")
public class Test1 {
// thread方式运行线程
public static void main(String[] args) {
Thread t1 = new Thread(()->{
log.debug("t1 start...");
},"t1");
t1.start();
log.debug("main线程执行");
}
}
@Slf4j(topic = "c")
public class Test2 {
public static void main(String[] args) {
// Runable执行线程
Runnable r1 = () -> {
log.debug("runable start...");
};
Thread t1 = new Thread(r1);
t1.setName("runable");
t1.start();
}
}
@Slf4j(topic = "c")
public class Test3 {
public static void main(String[] args) throws Exception{
// callable
FutureTask task = new FutureTask(new Callable() {
@Override
public Integer call() throws Exception {
log.debug("callable start..");
Thread.sleep(2000);
return 100;
}
});
Thread t1 = new Thread(task);
t1.setName("callable");
t1.start();
Object result = task.get();
log.debug("{}",result);
}
}
3种创建区别?
thread单继承有局限性 Runnable和callble多实现 callble有返回值有异常
1.2 线程的运行原理
- 任务调度器会将cpu的时间片交给某个类,然后虚拟机会将类的方法全放在方法区中,调用第一个方法时,会在线程栈中分配线程栈帧 ,程序计数器是线程私有的,它会记录当前改执行哪行代码
cpu因为某些原因不再执行当前的线程,转而执行另外一个线程的代码
- 线程cpu时间片用完
- 垃圾回收
- 有优先级更高的线程需要执行
- 线程调用了sleep,yield,wait,join,lock等方法
当线程上下文切换时,线程的状态会由操作系统保存,并恢复另外一个线程的状态,java中对应的概念就是程序计数器,他会记住下一条指令的地址,是线程私有的。
- 线程状态包括继承计数器,虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址。
run 与 start 有什么区别?
可以发现 调用t1.run 其实还是main线程执行的,而start则会新起一个线程执行。
2.sleep休眠、interrupt打断休眠的线程- sleep会使线程进入阻塞状态
@Slf4j(topic = "c.test5")
public class Test5 {
public static void main(String[] args) {
Thread t1 = new Thread(() ->
{
try {
log.debug("t1 sleep ...");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// interrupt 打断正在休眠的线程并抛出异常
try {
t1.start();
log.debug("main interrupt");
TimeUnit.SECONDS.sleep(1);
t1.interrupt();
log.debug("t1 status:{}", t1.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3.yield、setPriority
-
将线程的某一段时间的cpu执行权让出去,让线程进行可运行状态,与sleep不同的是 任务调度器还是会分时间片给yield线程
-
Priority设置线程优先级
- 等待线程结束 join(long time)最多等待多少s
@Slf4j(topic = "c.test7")
public class Test7 {
private static Integer sum = 0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
try {
Thread.sleep(1000);
log.debug("t1 start ....");
sum = 10;
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
log.debug("main start...");
// 如果不等待t1线程执行完 那么sum值会一直为0
t1.join();
System.out.println(sum);
}
}
5.interrupt
@Slf4j(topic = "c.test8")
public class Test8 {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("t1 start ,...");
while (true) {
// 获取当前线程打断状态
boolean interrupted = Thread.currentThread().isInterrupted();
if (interrupted) {
break;
}
}
}, "t1");
t1.start();
Thread.sleep(1000);
// 如果当前线程执行了wait join sleep方法 并不会把打断状态设置为ture 而是会抛出异常
t1.interrupt();
log.debug("main .....");
}
}
1.4 二阶段终止模式
-
二阶段终止模式是指线程1如何优雅的停止线程2
-
需求如图
@Slf4j(topic = "c.test9")
public class Test9 {
public static void main(String[] args) throws InterruptedException {
TwoPhaseCommit twoPhaseCommit = new TwoPhaseCommit();
twoPhaseCommit.start();
Thread.sleep(6500);
twoPhaseCommit.stop();
}
}
// 两阶段终止线程
@Slf4j(topic = "c.twoPhaseCommit")
class TwoPhaseCommit {
private Thread monit;
// 开始线程方法
public void start() {
monit = new Thread(() -> {
while (true) {
boolean interrupted = monit.isInterrupted();
if (interrupted) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(2000);
log.debug("收集日志....");
} catch (InterruptedException e) {
e.printStackTrace();
// 当线程调用sleep时 不会将打断标记设置为ture
monit.interrupt();
}
}
}, "t1");
monit.start();
}
// 停止线程
public void stop() {
monit.interrupt();
}
}
-
park 打断当前线程不会清空打断状态
@Slf4j(topic = "c.test10") public class Test10 { public static void main(String[] args) throws InterruptedException { test3(); } private static void test3() throws InterruptedException { Thread t1 = new Thread(() -> { log.debug("park run"); LockSupport.park(); log.debug("unpark..."); // interrupted作用:先将打断标记状态ture返回 再清除打断标记 log.debug("线程打断标志:{}", Thread.interrupted()); LockSupport.park(); log.debug("unpark..."); }, "t1"); t1.start(); sleep(1); t1.interrupt(); } } -
守护线程
只有其它非守护线程结束了,即使守护线程有没有执行完 也会强行停止
@Slf4j(topic = "c.test11") public class Test11 { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(() -> { while (true) { if (Thread.currentThread().isInterrupted()) { break; } } log.debug("结束"); }, "t1"); t1.setDaemon(true); t1.start(); Thread.sleep(1000); log.debug("主线程结束。。。。"); } }
-
cpu层面 5种
-
java层面的6种
- 问题:两个线程对初始值为0对静态变量一个做自增 一个做自减 分别循环5000次 结果会为0吗?
不会,因为再将源码编译成字节码后,count++或者count–其实是有4步操作,如果这几条指令按顺序执行肯定没问题,但多线程由于分时系统,会产生指令的交错。
- 解决:线程1执行时上锁,线程2进入阻塞状态,线程1没有释放锁 线程2不可能执行。
@Slf4j(topic = "c.test1")
public class Test1 {
static int sum = 0;
static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
for (int i = 1; i <= 5000; i++) {
synchronized (lock) {
sum++;
}
}
}, "t1");
Thread t2 = new Thread(() -> {
for (int i = 1; i <= 5000; i++) {
synchronized (lock) {
sum--;
}
}
}, "t2");
t2.start();
t1.start();
t2.join();
t1.join();
System.out.println(sum);
}
}
2.1 变量共享是否会产生安全问题
-
多个线程共享同一变量会导致线程安全 不共享则不会 (后面会引入原子类解决这一问题)
-
经典案例 卖票
@Slf4j(topic = "c.test3") public class Test3 { public static void main(String[] args) throws InterruptedException { TickWindows tickWindows = new TickWindows(1000); ListamountList = new Vector<>(); List threadList = new ArrayList<>(); // 开启线程 for (int i = 0; i < 2000; i++) { Thread thread = new Thread(() -> { int amount = tickWindows.sell(random(5)); amountList.add(amount); }); threadList.add(thread); thread.start(); } // 等待2000个线程结束 for (Thread thread1 : threadList) { thread1.join(); } log.debug("余票:{}" +tickWindows.getCount()); log.debug("剩下:{}张票" + amountList.stream().mapToInt(a-> a).sum()); } public static int random(int amount) { Random random = new Random(); return random.nextInt(amount) + 1; } } class TickWindows { private int count; public TickWindows(int count) { this.count = count; } // 获取剩余票 public int getCount() { return count; } // 售票 public synchronized int sell(int amount) { if (this.count >= amount) { this.count -= amount; return amount; } else { return 0; } } }
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1mQOEaZ-1653143116023)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205211944375.png)]
2.3 Monitor概念java对象在内存中是由两部分组成 第一部分是对象头,另一部门是对象中的成员变量
Java对象头-
klass word :它是一个指针 指向了对象存储的class 通过class能够知道它是什么类型的
mark word 详细信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8CMyKMea-1653143116025)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205202011985.png)]
- 当给对象加上synchronized时,会给这个对象关联一个monitor,通过指针指向monitor地址,并设置当前线程为owner当其他线程来获取当前对象的锁时,会被加入到entrylist中等待。
-
轻量级锁:如果一个对象有多个线程访问,并且多线程访问的时间是错开的,那么可以使用轻量级锁来优化
-
重量级锁竞争的时候,可以使用自旋来进行优化,如果当前线程自旋成功(其他持锁线程释放锁),这时线程避免了阻塞
轻量级锁在没有竞争时(自已线程)仍需要执行cas操作(耗性能)
在java6中引入偏向锁,只会在第一次去通过cas将线程id设置到markword头中,之后发现线程id是自已表示没有竞争,不会去执行cas操作,以后只要不发生竞争,这个对象就归当前线程所有
-
偏向状态
-
撤销
当调用wait/notify 也会出现撤销
- 批量重偏向
-
批量撤销
-
api介绍
@Slf4j(topic = "c.wait")
public class WaitNotify {
final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
log.debug("t1 执行");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("t1 执行任务");
}, "t1").start();
new Thread(() -> {
log.debug("t2 执行");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
log.debug("t2 执行任务");
}, "t2").start();
Thread.sleep(2000);
log.debug("main 唤醒线程");
synchronized (lock) {
// lock.notify(); // 随机唤醒某个线程
lock.notifyAll(); // 唤醒所有在waitset中的线程
}
}
}
- wait和sleep的区别?
- sleep是thread的静态方法,wait是object的方法
- wait需要配合synchoronized使用,而sleep不需要
- wait会释放锁进入waitset等待,而sleep不会释放锁
- 具体实现
// 下载百度首页数据
public class Download {
public static List download() throws IOException {
HttpURLConnection url = (HttpURLConnection) new URL("https://www.baidu.com/").openConnection();
List list = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(url.getInputStream(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
list.add(line);
}
}
return list;
}
}
@Slf4j(topic = "c.test4")
public class Test4 {
public static void main(String[] args) {
GuardedObject object = new GuardedObject();
new Thread(() -> {
log.debug("等待结果");
List o = (List) object.get(2000);
log.debug("结果是:{}", o);
}, "t1").start();
new Thread(() -> {
log.debug("执行下载");
Sleeper.sleep(1);
object.complete(null);
}, "t2").start();
}
}
// 增强超时效果
class GuardedObject {
private Object response;
public Object get(int timeout) {
synchronized (this) {
// 开始时间
long startTime = System.currentTimeMillis(); // 09:00:00
// 经历时间
long passedTime = 0;
while (response == null) {
long waitTime = timeout - passedTime; // 可能存在虚假唤醒
// 经历时间 大于等待时间 退出循环
if (waitTime <= 0) {
break;
}
try {
this.wait(waitTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
passedTime = System.currentTimeMillis() - startTime; // 02
}
return response;
}
}
public void complete(Object response) {
synchronized (this) {
// 给成员变量赋值
this.response = response;
// 唤醒wait线程
this.notify();
}
}
}
-
join源码
底层其实就是使用了wait的保护性暂停模式
-
扩展保护性暂停-解偶等待和生产
@Slf4j(topic = "c.test5") public class Test5 { public static void main(String[] args) { for (int i = 0; i < 3; i++) { new People().start(); } Sleeper.sleep(1); for (Integer id : MailBox.getIdList()) { new Postman(id, "内容" + id).start(); } } } @Slf4j(topic = "c.People") class People extends Thread { @Override public void run() { GuardedObjects obj = MailBox.createObject(); log.debug("开始收信{}", obj.getId()); Object mail = obj.get(5000); log.debug("收到信id:{},内容:{}", obj.getId(), mail); } } @Slf4j(topic = "c.Postman") class Postman extends Thread { private int mailId; private String mail; public Postman(int mailId, String mail) { this.mailId = mailId; this.mail = mail; } @Override public void run() { GuardedObjects guardedObjects = MailBox.getGuardedObjects(mailId); log.debug("开始送信id:{},内容:{}", mailId, mail); guardedObjects.complete(mail); } } class MailBox { private static Mapmap = new Hashtable<>(); private static int id = 1; // 产生唯一id private static synchronized int generateId() { id++; return id; } // 创建GuardedObjects对象 public static GuardedObjects createObject() { GuardedObjects obj = new GuardedObjects(generateId()); map.put(obj.getId(), obj); return obj; } // 获取到所有到id public static Set getIdList() { return map.keySet(); } // 通过id获取到GuardedObjects对象 public static GuardedObjects getGuardedObjects(int id) { return map.remove(id); } } class GuardedObjects { private int id; public void setId(int id) { this.id = id; } public int getId() { return id; } public GuardedObjects(int id) { this.id = id; } private Object response; public Object get(int timeout) { synchronized (this) { // 开始时间 long startTime = System.currentTimeMillis(); // 09:00:00 // 经历时间 long passedTime = 0; while (response == null) { long waitTime = timeout - passedTime; // 可能存在虚假唤醒 // 经历时间 大于等待时间 退出循环 if (waitTime <= 0) { break; } try { this.wait(waitTime); } catch (InterruptedException e) { e.printStackTrace(); } passedTime = System.currentTimeMillis() - startTime; // 02 } return response; } } public void complete(Object response) { synchronized (this) { // 给成员变量赋值 this.response = response; // 唤醒wait线程 this.notifyAll(); } } }
-
java实现
@Slf4j(topic = "c.test6") public class Test6 { public static void main(String[] args) { MessageQueue queue = new MessageQueue(2); for (int i = 0; i < 3; i++) { // 3个生产者 int id = i; new Thread(() -> { queue.put(new Message(id, "值" + id)); }, "生产者").start(); } new Thread(() -> { while (true){ Sleeper.sleep(1); Message task = queue.task(); } }, "消费者").start(); } } @Slf4j(topic = "c.message") class MessageQueue { private LinkedListlist = new LinkedList<>(); private int capacity; public MessageQueue(int capacity) { this.capacity = capacity; } // 消费消息 public Message task() { synchronized (list) { // 如果集合为空进入等待 如果不为空 返回集合中第一个 while (list.isEmpty()) { try { log.debug("队列为空,消费者线程等待"); list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } Message message = list.removeFirst(); log.debug("消费者消费消息id:{},value:{}", message.getId(), message.getValue()); list.notifyAll(); return message; } } // 存入消息 public void put(Message message) { synchronized (list) { while (list.size() == capacity) { try { log.debug("队列已达到最大容量,生产者线程等待"); list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } list.addLast(message); log.debug("生产者生产消息"); list.notifyAll(); } } } final class Message { private int id; private Object value; public Message(int id, Object value) { this.id = id; this.value = value; } public int getId() { return id; } public Object getValue() { return value; } @Override public String toString() { return "Message{" + "id=" + id + ", value=" + value + '}'; } }
-
代码实现
@Slf4j(topic = "c.test7") public class Test7 { public static void main(String[] args) { Thread t1 = new Thread(() -> { log.debug("t1 start..."); Sleeper.sleep(2); log.debug("park..."); LockSupport.park(); log.debug("recover..."); }, "t1"); t1.start(); Sleeper.sleep(3); log.debug("main ..."); LockSupport.unpark(t1); } } -
debug发现 使用park后 t1线程的状态是wait
-
park和object中的wait/notify/notifyall 有什么区别?
wait/notify和notifyall需要object monitor一起使用,而park不需要
park可以指定线程进行阻塞或唤醒,notify是随机或者全部
park可以先unpark 而wait不能先notify
-
park/unpark原理
-
调用park
-
调用unpark
-
先调用unpark 先调用park的情况
-
代码实现
@Slf4j(topic = "c.test8") public class Test8 { public static void main(String[] args) { final Object obj = new Object(); new Thread(() -> { synchronized (obj) { log.debug("t1...."); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("start...."); }, "t1").start(); new Thread(() -> { synchronized (obj) { log.debug("t1...."); try { obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("start...."); }, "t2").start(); synchronized (obj){ Sleeper.sleep(1); log.debug("main start..."); obj.notifyAll(); } } } -
debug可以发现。当t1,t2线程调用wait方法时 会使线程进入waitting状态,
-
当主线程调用notifyall方法时 t1,t2线程进入blocked状态
-
当主线程代码全部执行完后 在waitset中的线程会去竞争锁
@Slf4j(topic = "c.test9")
public class Test9 {
public static void main(String[] args) {
Room room = new Room();
new Thread(() -> {
room.sleep();
}, "t1").start();
new Thread(() -> {
room.study();
}, "t2").start();
}
}
@Slf4j(topic = "c.room")
class Room {
private static final Object sleep = new Object();
private static final Object study = new Object();
public void sleep() {
synchronized (sleep) {
log.debug("sleep....");
Sleeper.sleep(1);
}
}
public void study() {
synchronized (study) {
log.debug("study....");
Sleeper.sleep(1);
}
}
}
-
细粒度锁
并发度高,但同一个线程同时获取多把锁可能会产生死锁问题
@Slf4j(topic = "c.test10")
public class Test10 {
public static void main(String[] args) {
test1();
}
private static void test1() {
Object A = new Object();
Object B = new Object();
Thread t1 = new Thread(() -> {
synchronized (A) {
log.debug("t1 线程获取到A锁");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
log.debug("t1线程尝试获取B锁");
log.debug("t1获取到b锁 进行其他操作");
}
}
}, "t1");
Thread t2 = new Thread(() -> {
synchronized (B) {
log.debug("t2线程获取到B锁");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
log.debug("t2线程尝试获取A锁");
log.debug("t2获取到a锁 进行其他操作");
}
}
}, "t2");
t1.start();
t2.start();
}
}
- t2获取到b锁后尝试去获取a锁 但a1锁已经被t1获取,t1想去获取b锁,但b锁一起被t2获取 导致死锁
- 使用jconsole命令可以查看死锁情况
-
活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束
@Slf4j(topic = "c.test11") public class Test11 { static volatile int count = 10; public static void main(String[] args) { new Thread(() -> { // 期望count为0时退出循环 while (count > 0) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count--; log.debug("count:{}", count); } }, "t1").start(); new Thread(() -> { // 期望count超过20时退出循环 while (count < 20) { try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } count++; log.debug("count:{}", count); } }, "t2").start(); } } -
解决办法:增加某个线程的睡眠时间 让线程交错执行
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MfmwrRav-1653143116033)(images/image-20220521153611295.png)]
3.1 ReentrantLock-
相对于synchoronized它具备如下特点
-
可中断
-
可设置超时时间
-
可设置为公平锁
-
支持多个变量 对waitSet更加细粒度多划分
-
- 可重入是指同一个线程如果首次获取到了这把锁,那么它就是这把锁的拥有者,它有权利再次获取到这把锁
@Slf4j(topic = "c.test12")
public class Test12 {
private static ReentrantLock lock = new ReentrantLock();
// 实现可重入
public static void main(String[] args) {
try {
lock.lock();
log.debug("实现可重入");
method1();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void method1() {
try {
lock.lock();
log.debug("m1 重入");
method2();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void method2() {
try {
lock.lock();
log.debug("m2 重入");
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
2.可打断
@Slf4j(topic = "c.test13")
public class Test13 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("尝试获取锁");
try {
// 如果没有竞争,可以获取到lock锁
// 如果有竞争就会进入阻塞队列,但是其他线程可以打断
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("没有获取到锁,返回");
return;
}
try {
log.debug("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
// 主线程获取锁
lock.lock();
t1.start();
// 打断t1线程获取锁
Sleeper.sleep(1);
t1.interrupt();
}
}
3.锁超时
@Slf4j(topic = "c.test14")
public class Test14 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
log.debug("尝试获取锁");
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
log.debug("获取锁失败");
return;
}
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("获取锁失败");
return;
}
try {
log.debug("获取到锁");
} finally {
lock.unlock();
}
}, "t1");
log.debug("获取到锁");
lock.lock();
t1.start();
Sleeper.sleep(1);
log.debug("释放锁");
lock.unlock();
}
}
4.哲学家就餐问题 使用renntrantlock解决
@Slf4j(topic = "c.test15")
public class Test15 {
public static void main(String[] args) {
Chopstick c1 = new Chopstick("1");
Chopstick c2 = new Chopstick("2");
Chopstick c3 = new Chopstick("3");
Chopstick c4 = new Chopstick("4");
Chopstick c5 = new Chopstick("5");
new Philosopher("t1",c1,c2).start();
new Philosopher("t2",c2,c3).start();
new Philosopher("t3",c3,c4).start();
new Philosopher("t4",c4,c5).start();
new Philosopher("t5",c5,c1).start();
}
}
@Slf4j(topic = "c.Philosopher")
class Philosopher extends Thread {
Chopstick left;
Chopstick right;
public Philosopher(String name, Chopstick left, Chopstick right) {
super(name);
this.left = left;
this.right = right;
}
@Override
public void run() {
while (true) {
// 尝试获取左手筷子
if (left.tryLock()) {
try {
// 尝试获取右手筷子
if (right.tryLock()) {
try {
eat();
} finally {
// 获取失败 释放
right.unlock();
}
}
} finally {
// 获取失败
left.unlock();
}
}
}
}
private void eat() {
log.debug("eat....");
Sleeper.sleep(1);
}
}
class Chopstick extends ReentrantLock {
private String name;
public Chopstick(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "筷子{" +
"name='" + name + ''' +
'}';
}
}
5.公平锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KxZZh6ul-1653143116033)(https://gitee.com/liubozzz/image/raw/20220520liubo/img/202205211931628.png)]
6.条件变量 7.同步模式之顺序控制-
要求t1/t2线程 先打印t2线程
或者使用wait/notify
@Slf4j(topic = "c.test16") public class Test16 { public static void main(String[] args) { Thread t1 = new Thread(() -> { LockSupport.park(); log.debug("t1"); }, "T1"); new Thread(() -> { log.debug("t2"); LockSupport.unpark(t1); }, "T2").start(); t1.start(); } } -
交替输出 要求线程1打印a 线程2打印b 线程3打印c 循环5次
-
使用wait和notify解决
-
使用await和single方法解决
@Slf4j(topic = "c.test17") public class Test17 { public static void main(String[] args) { AwaitSignal lock = new AwaitSignal(5); Condition condition1 = lock.newCondition(); Condition condition2 = lock.newCondition(); Condition condition3 = lock.newCondition(); new Thread(() -> { lock.println("a", condition1, condition2); }, "t1").start(); new Thread(() -> { lock.println("b", condition2, condition3); }, "t2").start(); new Thread(() -> { lock.println("c", condition3, condition1); }, "t3").start(); Sleeper.sleep(1); try { lock.lock(); condition1.signal(); } finally { lock.unlock(); } } } class AwaitSignal extends ReentrantLock { public void println(String str, Condition current, Condition next) { for (int i = 0; i < loopNumber; i++) { try { lock(); current.await(); System.out.print(str); // 唤醒下一个线程 next.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { unlock(); } } } private int loopNumber; public AwaitSignal(int loopNumber) { this.loopNumber = loopNumber; } } -
使用park/unpark解决
@Slf4j(topic = "c.test18") public class Test18 { static Thread t1; static Thread t2; static Thread t3; public static void main(String[] args) { ParkUnPark park = new ParkUnPark(5); t1 = new Thread(() -> { park.println("a", t2); }, "t1"); t2 = new Thread(() -> { park.println("b", t3); }, "t2"); t3 = new Thread(() -> { park.println("c", t1); }, "t3"); t1.start(); t2.start(); t3.start(); Sleeper.sleep(1); LockSupport.unpark(t1); } } class ParkUnPark extends ReentrantLock { public void println(String str, Thread thread) { for (int i = 0; i < loopNumber; i++) { LockSupport.park(); System.out.print(str); LockSupport.unpark(thread); } } private int loopNumber; public ParkUnPark(int loopNumber) { this.loopNumber = loopNumber; } }
-



