目录
[六种(Java API角度)](#六种(Java API角度))
同步
@Slf4j(topic = "c.TestJoin")
public class TestJoin {
static int r = 0;
static int r1 = 0;
static int r2 = 0;
public static void main(String[] args) throws InterruptedException {
test2();
}
public static void test3() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(2);
r1 = 10;
});
long start = System.currentTimeMillis();
t1.start();
// 线程执行结束会导致 join 结束
log.debug("join begin");
t1.join(3000);
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
private static void test2() throws InterruptedException {
Thread t1 = new Thread(() -> {
sleep(1);
r1 = 10;
});
Thread t2 = new Thread(() -> {
sleep(2);
r2 = 20;
});
t1.start();
t2.start();
long start = System.currentTimeMillis();
log.debug("join begin");
t1.join();
log.debug("t1 join end");
t2.join();
log.debug("t2 join end");
long end = System.currentTimeMillis();
log.debug("r1: {} r2: {} cost: {}", r1, r2, end - start);
}
private static void test1() throws InterruptedException {
log.debug("开始");
Thread t1 = new Thread(() -> {
log.debug("开始");
sleep(1);
log.debug("结束");
r = 10;
});
t1.start();
t1.join();
log.debug("结果为:{}", r);
log.debug("结束");
}
}
多线程的join只需要等待最长的那个线程就可以了,因为他们都是并行执行的。如果是t1先的话,那么就是t1在处理1秒的同时,t2也在处理一秒,然后t2执行join,那么还只剩下1s处理,那么t1和t2也只需要等待多1s就能够继续执行主线程的。
如果是t2先那么在处理2s的同时,t1已经先处理了1s并且进入了等待状态,等待t2处理完那么就可以执行主线程了。在t2的join end的同时,t1处理完,并且也会执行join end。
简单来说就是两个线程开启,如果下面两个都是join,那么实际上两个线程都会各自执行,互不干扰,直到最后一个线程执行完,那么才会执行join下面的代码。
test3案例,实际上就是限时等待,如果超过时间那么就不等了。如果没有超过时间,那么结束join还是以处理完线程的任务时间为主,而不是最大的等待时间
两阶段终止模式
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination tpt = new TwoPhaseTermination();
tpt.start();
Thread.sleep(3500);
tpt.stop();
}
}
@Slf4j
class TwoPhaseTermination{
private Thread monitor;
// 启动监控线程
public void start(){
monitor = new Thread(() -> {
while(true){
Thread thread = Thread.currentThread();
if(thread.isInterrupted()){
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000); // 情况一 在睡眠时被打断 打断标记是false
log.debug("监控记录"); // 情况二
} catch (InterruptedException e) {
e.printStackTrace();
// 如果是睡眠中被打断 , 就重新设置打断标记
thread.interrupt();
}
}
});
monitor.start();
}
// 停止监控线程
public void stop() {
monitor.interrupt();
}
}
打断park线程
@Slf4j
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
log.debug("park...");
LockSupport.park();
log.debug("unpark");
log.debug("打断状态: {}",Thread.currentThread().isInterrupted());
},"t1");
t1.start();
Thread.sleep(1000);
t1.interrupt();
}
}
守护线程
守护线程其实就是Daemon也就是在其他非守护线程运行完之后,无论守护线程是否还有任务需要执行都会强制停止。
-
垃圾回收器是守护线程
-
tomcat的Acceptor和Poller
@Slf4j
public class Test {
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("结束");
}
}
线程状态
五种(操作系统角度)
①初始:new线程的时候,只创建了,还没和操作系统关联
②可运行:执行了start
③运行:线程可以使用cpu的时候
④阻塞:不能被cpu调度器使用的时候
⑤终止:线程生命周期结束的时候
六种(Java API角度)
NEW:初始化
RUNNABLE:包括了运行、可运行和阻塞,通常表示正在运行
WAITING:没有时间限制
TIMED_WAITING:有时限的等待
BLOCKED:阻塞
TERMINATED:终止
烧水案例
@Slf4j
public class Test {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
log.debug("洗水壶");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("烧开水");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"老王");
Thread t2 = new Thread(() ->{
log.debug("洗茶壶");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("洗茶杯");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("拿茶叶");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.debug("泡茶");
}, "小王");
t1.start();
t2.start();
}
}
共享模型之管程
@Slf4j
public class Test {
static int counter = 0 ;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() ->{
for (int i = 0; i < 5000; i++) {
counter ++ ;
}
});
Thread t2= new Thread(() ->{
for (int i = 0; i < 5000; i++) {
counter -- ;
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter); // 并不是输出0
}
}
临界区
实际上就是多个线程访问的代码里面有共享资源,那么这段代码就是临界区
竞态条件
如果在临界区中多线程执行发生执行指令序列不同导致结果无法预测的状况就是竞态条件