1、 join()、Thread.yield()
ThreadTest t1 = new ThreadTest("A");
ThreadTest t2 = new ThreadTest("B");
t1.start();
t1.join();
t2.start();
先执行t1线程,再执行t2线程。
ThreadTest t1 = new ThreadTest("A");
ThreadTest t2 = new ThreadTest("B");
ThreadTest t3 = new ThreadTest("C");
t1.start();
t2.start();
t1.join();
t3.start();
t1线程和t2线程交替执行,当t1线程执行完后开始t3线程,执行过程中和t2没有关系
多次实验可以看出,主线程在t1.join()方法处停止,并需要等待A线程执行完毕后才会执行t3.start(),
然而,并不影响B线程的执行。因此,可以得出结论,t.join()方法只会使主线程进入等待池并等待t线程执行完毕
后才会被唤醒。并不影响同一时刻处在运行状态的其他线程。
thread.join把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。
比如在线程B中调用了线程A的join()方法,直到线程A执行完毕后,才会继续执行线程B。
t.join(); // 调用join()方法,等待线程t执行完毕
t.join(1000); // 调用t线程,等待时间是1000毫秒
PS:join源码中,只会调用wait方法,并没有在结束时调用notify,这是因为线程在die的时候会自动调用
自身的notifyAll方法,来释放所有的资源和锁。
public class Join1 {
public static void main(String[] args) {
var ex = new Join1();
var t1 = new Thread(ex::work,"T1");
var t2 = new Thread(ex::work,"T2");
var t3 = new Thread(ex::work,"T3");
var t4 = new Thread(ex::work,"T4");
// 并行
// t2 -> t3 串行执行
// t1 -> t3 串行执行 先执行t1再执行t3
t1.start();
t2.start();
try {
t1.join();
} catch (InterruptedException e){
e.printStackTrace();
}
t3.start();
try {
t2.join();
} catch (InterruptedException e){
e.printStackTrace();
}
t4.start();
}
public static void test(String[] args) {
var ex = new Join1();
var t1 = new Thread(ex::work,"T1");
var t2 = new Thread(ex::work,"T2");
var t3 = new Thread(ex::work,"T3");
var t4 = new Thread(ex::work,"T4");
t1.start();
try {
t1.join();
} catch (InterruptedException e){
e.printStackTrace();
}
t2.start();
}
void work(){
String tn = Thread.currentThread().getName();
for (int i = 0; i < 5; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e){
e.printStackTrace();
}
System.out.printf("%s = %d%n",tn,i);
}
}
}
Thread.yield()
使当前线程从执行状态(运行状态)变为可执行态(就绪状态)。cpu会从众多的可执行态李选择,也就
是说,当前也就是刚刚的那个线程还是有可能会被再次执行到的,并不是说一定会执行其他线程而该线程再
下一次中不会执行到了。
Java线程中有一个Thread.yield()方法,很多人翻译成线程让步。顾名思义,就是说当一个线程使用了这
个方法之后,它就会把自己CPU执行的时间让掉,让自己或者其他的线程运行。
打个比方:现在很多人在排队上厕所,好不容易轮到这个人上厕所了,突然这个人说:“我要和大家来个
竞赛,看谁先抢到厕所!”,然后所有的人再同一起跑线冲向厕所,有可能是别人抢到了,也有可能他自己
抢到了。我们还知道县城有个优先级的问题,那么手里有优先权的这些人就一定能抢到厕所的位置吗?不
一定的,他们只是概率大些,也哟可能没特权的抢到了。
public class YieldTest extends Thread{
public YieldTest(String name){
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println("" + this.getName() + "-----" + i);
// 当i为30时,该线程就会把CPU时间让掉,让其他或者自己的线程执行(也就是谁先抢到谁执行)
if (i == 30){
this.yield();
}
}
}
public static void main(String[] args) {
YieldTest yt1 = new YieldTest("张三");
YieldTest yt2 = new YieldTest("李四");
yt1.start();
yt2.start();
}
}
2、 interrupt()打断
------方法一
public class Interrupt1 {
public static volatile boolean exit = false; // 退出标志
public static void main(String[] args) {
new Thread(){
public void run(){
System.out.println("线程启动了");
while (!exit){
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("线程结束了");
}
}.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e){
e.printStackTrace();
}
exit = true; // 5秒后更改退出标志的值,没有这段代码,线程就一直不能停止
}
}
------方法二
public class Interrupt2 {
public static void main(String[] args) {
var t = new Thread(() -> {
while (true){
try {
System.out.println(Thread.currentThread());
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e){
// System.out.println(Thread.currentThread().getName() + "被中断了");
break;
}
}
},"t1");
t.start();
try {
System.out.println(Thread.currentThread());
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e){
e.printStackTrace();
}
t.interrupt();
}
}
------方法三
public class Interrupt3 {
public static void main(String[] args) {
// 开启线程
var t = new Thread(Interrupt3::work,"T1");
t.start();
try {
TimeUnit.SECONDS.sleep(5);
t.interrupt();
System.out.println("线程是否被中断:" + t.isInterrupted()); // true
} catch (Exception e){
e.printStackTrace();
}
}
public static void work(){
Thread tt = Thread.currentThread();
String tn = tt.getName();
System.out.printf("%s启动了%n",tn);
System.out.printf("%s.isInterrupted() %s%n",tn,tt.isInterrupted());
while (!tt.isInterrupted()){
System.out.printf("%s.isInterrupted() %s%n",tn,tt.isInterrupted());
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e){
break;
}
}
System.out.printf("%s.isInterrupted() %s%n",tn,tt.isInterrupted());
System.out.printf("%s结束了...%n",tn);
}
}
public class Interrupt4 {
final Object lock = new Object();
public static void main(String[] args) {
var t = new Interrupt4();
for (int i = 0; i < 5; i++) new Thread(t::m,"T" + i).start();
try {
TimeUnit.SECONDS.sleep(5);
synchronized (t.lock){
// t.lock.notify();
t.lock.notifyAll();
}
} catch (Exception e){
e.printStackTrace();
}
}
void m(){
System.out.println(Thread.currentThread().getName() + "启动等待...");
try {
synchronized (lock){
lock.wait(); // 进入阻塞状态
}
System.out.println(Thread.currentThread().getName() + "被唤醒了...");
} catch (Exception e){
e.printStackTrace();
}
}
}
3、 原子工具类
在jdk8之前,针对原子操作,我们基本上可以通过上面提供的这些类来完成我们多线程下的原子操作,
不过在并发高的情况下,上面这些单一的CAS + 自旋操作的性能将会是一个问题,所以上诉这些类一般
用于低并发操作。
而针对这个问题,jdk8又引入了下面几个类:DoubleAdder,LongAdder,DoubleAccumulator,
LongAccumulator,这些类是对AtomicLong这些类的改进与增强,这些类都继承自Striped64这个类。
public class Atom1 {
AtomicInteger n = new AtomicInteger(50);
public static void main(String[] args) {
var t = new Atom1();
new Thread(t::minus,"T1").start();
new Thread(t::minus,"T2").start();
new Thread(t::minus,"T3").start();
new Thread(t::minus,"T4").start();
new Thread(t::minus,"T5").start();
new Thread(t::minus,"T6").start();
new Thread(t::minus,"T7").start();
new Thread(t::minus,"T8").start();
}
int num = 50;
void minus(){
while (true){
if (n.get() > 0){
n.decrementAndGet(); // 相当于--i;
System.out.printf("%s 售出一张票,剩余%d张票。%n",Thread.currentThread().getName(),n.get());
} else {
break;
}
/*
if (num > 0){
num --;
System.out.printf("%s 售出一张票,剩余%d张票。%n",Thread.currentThread().getName(),num);
} else {
break;
}
*/
try {
TimeUnit.SECONDS.sleep(1);
} catch (Exception e){
e.printStackTrace();
}
}
}
}
public class Atom2 {
AtomicBoolean flag = new AtomicBoolean(true);
public static void main(String[] args) {
var t = new Atom2();
for (int i = 0; i < 5; i++) {
new Thread(t::op,"T" + i).start();
}
}
void op(){
System.out.printf("%s 启动计算机中 %n",Thread.currentThread().getName());
String[] o = new String[]{"","+","-"};
Random rand = new Random();
StringBuffer str = new StringBuffer("1");
Pattern p = Pattern.compile("(\\d+|-\\d+)");
while (flag.get()){
for (int i = 2; i < 10; i++) {
str.append(String.format("%s%d",o[rand.nextInt(o.length)],i));
String s = str.toString(); // 123456789 = 100
Matcher m = p.matcher(s);
List<Integer> ln = new ArrayList<>();
while (m.find()){
ln.add(Integer.parseInt(m.group()));
}
int sum = ln.stream().reduce(0,Integer::sum);
if (sum == 100){
flag.set(false);
System.out.printf("[%s]:%s = 100%n",Thread.currentThread().getName(),s);
break;
} else {
str.delete(1,str.length());
}
}
System.out.printf("%s 结束 %n",Thread.currentThread().getName());
}
}
}
运行结果:98
public class Counter2 {
public volatile static Integer count = 0;
public static void main(String[] args) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(100);
for (int i = 0; i < 100; i++) {
new Thread(new Runnable() {
@Override
public void run() {
Counter2.inc();
latch.countDown();
}
}).start();
}
latch.await();
System.out.println("运行结果:" + Counter2.count);
}
public synchronized static void inc(){
try {
Thread.sleep(1); // 延迟1毫秒
}catch (InterruptedException e){ // catch住中断异常,防止程序中断
e.printStackTrace();
}
count++; // count值自加1
}
}
运行结果:100