多线程核心(1):Thread和Object类中的重要方法

 


1 方法概览

方法名

简介

Thread

sleep相关

 
join

等待其他线程执行完毕

yield相关

放弃已经获取到的CPU资源

currentThread

获取当前执行线程的引用

start/run相关

启动线程相关

interrupt相关

中断线程

stop/suspend/resume相关已废弃

Object

wait/notify/notifyAll相关

让线程暂时休息和唤醒(需要被synchronized保住)


/**
 * 演示打印main, Thread-0, Thread-1
 */
public class CurrentThread implements Runnable {
    public static void main(String[] args) {
        CurrentThread tread = new CurrentThread();
        tread.run();
        new Thread(tread).start();
        new Thread(tread).start();
    }
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

2 wait/notify/notifyAll

2.1 注意点

wait/notify/notifyAll 需要放在synchronized快里面

https://blog.csdn.net/sarafina527/article/details/89173738

public class Main {
    private final static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                lock.wait();
            } catch (InterruptedException ignored) {
            }
        }).start();
    }
}
//Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
//	at java.base/java.lang.Object.wait(Native Method)
//	at java.base/java.lang.Object.wait(Object.java:326)
//	at UseOwnUncaughtExceptionHandler.lambda$main$0(UseOwnUncaughtExceptionHandler.java:7)
//	at java.base/java.lang.Thread.run(Thread.java:830)
public class Main {
    private final static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " exec wait start.");
                try {
                    lock.wait();
                } catch (InterruptedException ignored) {
                }
                System.out.println(Thread.currentThread().getName() + " exec wait end.");
            }
        }, "AAA").start();
        TimeUnit.SECONDS.sleep(1);
        synchronized (lock) { // 这里锁的对象要和wait哪个一致,不然会报异常或者是效果不是我想要的
            lock.notify();
        }
        TimeUnit.SECONDS.sleep(1);
        System.out.println("main exec success.");
    }
}
//AAA exec wait start.
//AAA exec wait end.
//main exec success.

调用notify()后,并不会立即释放锁,而是继续执行当前代码,直到synchronized中的代码全部执行完毕,才会释放对象锁 

https://www.cnblogs.com/benshan/p/3551987.html

public class Main {
    private final static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " login synchronized.");
                try {
                    System.out.println(Thread.currentThread().getName() + " exec wait.");
                    lock.wait();
                    System.out.println(Thread.currentThread().getName() + " awaken.");
                } catch (InterruptedException ignored) {
                }
            }
            System.out.println(Thread.currentThread().getName() + " logout synchronized.\n");
        }, "AAA").start();
        TimeUnit.SECONDS.sleep(1); // 等AAA先执行
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName() + " exec notify.");
            lock.notify(); // 虽然这里就执行了notify,但是程序要走出synchronized,AAA线程才会被唤醒
            TimeUnit.SECONDS.sleep(5);
            System.out.println(Thread.currentThread().getName() + " logout synchronized.");
        }
        TimeUnit.SECONDS.sleep(5);
        System.out.println("main exec success.");
    }
}

2.2 基本用法

作用/用法:阻塞阶段、唤醒阶段、遇到中断

/**
 * 展示wait和notify的基本用法
 *      1. 研究代码执行顺序
 *      2. 证明wait释放锁
 */
public class Wait {
    public static Object object = new Object();
    static class Thread1 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                System.out.println(Thread.currentThread().getName() + "开始执行了");
                try {
                    //释放monitor锁
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁。");
            }
        }
    }
    static class Thread2 extends Thread {
        @Override
        public void run() {
            synchronized (object) {
                object.notify();
                System.out.println("线程" + Thread.currentThread().getName() + "调用了notify()");
            }
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        thread1.start();
        Thread.sleep(200);
        thread2.start();
    }
}
/**
 * 3个线程,线程1和线程2首先被阻塞,线程3唤醒它们。notify, notifyAll。 start先执行不代表线程先启动。
 */
public class WaitNotifyAll implements Runnable {
    private static final Object resourceA = new Object();
    @Override
    public void run() {
        synchronized (resourceA) {
            System.out.println(Thread.currentThread().getName()+" got resourceA lock.");
            try {
                System.out.println(Thread.currentThread().getName()+" waits to start.");
                resourceA.wait();
                System.out.println(Thread.currentThread().getName()+"'s waiting to end.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args){
        Runnable r = new WaitNotifyAll();
        Thread threadA = new Thread(r);
        Thread threadB = new Thread(r);
        
        Thread threadC = new Thread(() -> {
            synchronized (resourceA) {
                resourceA.notifyAll();
                //resourceA.notify();
                System.out.println("ThreadC notified.");
            }
        });
        threadA.start();
        threadB.start();
        //Thread.sleep(200);
        threadC.start();
    }
}

wait只释放当前的那把锁

/**
 * 证明wait只释放当前的那把锁(只释放当前monitor)
 */
public class WaitNotifyReleaseOwnMonitor {
    private static Object resourceA = new Object();
    private static Object resourceB = new Object();
    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (resourceA) {
                System.out.println("ThreadA got resourceA lock.");
                synchronized (resourceB) {
                    System.out.println("ThreadA got resourceB lock.");
                    try {
                        System.out.println("ThreadA releases resourceA lock.");
                        //释放A
                        resourceA.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (resourceA) {
                System.out.println("ThreadB got resourceA lock.");
                System.out.println("ThreadB tries to resourceB lock.");
                synchronized (resourceB) {
                    System.out.println("ThreadB got resourceB lock.");
                }
            }
        });
        thread1.start();
        thread2.start();
    }
}

2.3 wait(timeoutMillis)

https://www.cnblogs.com/zerodsLearnJava/p/12852891.html

public class Main {
    private final static Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName() + " exec wait start.");
                try {
                    /**
                     * 没有其他线程执行lock.notify(),并且能再次获得monitor锁就等5s,不能获取就一直等
                     * 有其他线程执行lock.notify(),不用等5s
                     */
                    lock.wait(5000L); //
                } catch (InterruptedException ignored) {
                }
                System.out.println(Thread.currentThread().getName() + " exec wait end.");
            }
        }, "AAA").start();

        TimeUnit.SECONDS.sleep(1);
        synchronized (lock) {
            //lock.notify(); // lock.wait(5000L); 不用等5s就能向下执行
            TimeUnit.SECONDS.sleep(100); // 不释放锁,lock.wait(5000L); 时间到了也不会继续往下执行的
        }
        TimeUnit.SECONDS.sleep(1);
        System.out.println("main exec success.");
    }
}

2.4 手写生产者消费者设计模式

 

/**
 * 用wait/notify来实现生产者消费者模式
 */
public class ProducerConsumerModel {
    public static void main(String[] args) {
        EventStorage eventStorage = new EventStorage();

        Producer producer = new Producer(eventStorage);
        Consumer consumer = new Consumer(eventStorage);
        new Thread(producer).start();
        new Thread(consumer).start();
    }
}

class Producer implements Runnable {
    private EventStorage storage;
    public Producer(EventStorage storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.put();
        }
    }
}

class Consumer implements Runnable {
    private EventStorage storage;
    public Consumer(EventStorage storage) {
        this.storage = storage;
    }
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            storage.take();
        }
    }
}

//实现效果类似于阻塞队列
class EventStorage {
    private int maxSize;
    private LinkedList<Date> storage;
    public EventStorage() {
        maxSize = 10;
        storage = new LinkedList<>();
    }
    // 生产者 添加
    public synchronized void put() {
        while (storage.size() == maxSize) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        storage.add(new Date());
        System.out.println("仓库里有了" + storage.size() + "个产品。");
        this.notify();
    }
    // 消费者 消费
    public synchronized void take() {
        while (storage.size() == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("拿到了" + storage.poll() + ",现在仓库还剩下" + storage.size());
        this.notify();
    }
}

2.5 两个线程交替打印0~100的奇偶数

  • 偶线程:0
  • 奇线程:1
  • 偶线程:2
/**
 * 两个线程交替打印0~100的奇偶数,用synchronized关键字实现
 */
public class WaitNotifyPrintOddEvenSyn {
    private static int count;
    private static final Object lock = new Object();
    //新建2个线程
    //1个只处理偶数,第二个只处理奇数(用位运算)
    //用synchronized来通信
    public static void main(String[] args) {

        new Thread(() -> {
            while (count < 100) {
                synchronized (lock) {
                    if ((count & 1) == 0) {
                        System.out.println(Thread.currentThread().getName() + ":" + count++);
                    }
                }
            }
        }, "偶数").start();

        new Thread(() -> {
            while (count < 100) {
                synchronized (lock) {
                    if ((count & 1) == 1) {
                        System.out.println(Thread.currentThread().getName() + ":" + count++);
                    }
                }
            }
        }, "奇数").start();
    }
}

第一种方法虽然能实现效果,但是有很多不必要的运算 

/**
 * 两个线程交替打印0~100的奇偶数,用wait和notify
 */
public class WaitNotifyPrintOddEveWait {
    private static int count = 0;
    private static final Object lock = new Object();
    public static void main(String[] args) throws InterruptedException {
        new Thread(new TurningRunner(), "偶数").start();
        //Thread.sleep(100);
        new Thread(new TurningRunner(), "奇数").start();
    }
    //1. 拿到锁,我们就打印
    //2. 打印完,唤醒其他线程,自己就休眠
    static class TurningRunner implements Runnable {
        @Override
        public void run() {
            while (count <= 100) {
                synchronized (lock) {
                    //拿到锁就打印
                    System.out.println(Thread.currentThread().getName() + ":" + count++);
                    lock.notify();
                    if (count <= 100) {
                        try {
                            //如果任务还没结束,就让出当前的锁,并休眠
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

3 线程休眠sleep方法

3.1 sleep用法

  • 作用:我只想让线程在预期的时间执行,其他时候不要占用CPU资源
  • 不释放锁:包括synchronizedlock (和wait不同)
/**
 * 展示线程sleep的时候不释放synchronized的monitor,等sleep时间到了以后,正常结束后才释放锁
 */
public class SleepDontReleaseMonitor implements Runnable {
    public static void main(String[] args) {
        SleepDontReleaseMonitor sleepDontReleaseMonitor = new SleepDontReleaseMonitor();
        new Thread(sleepDontReleaseMonitor).start();
        new Thread(sleepDontReleaseMonitor).start();
    }
    @Override
    public void run() {
        syn();
    }
    private synchronized void syn() {
        System.out.println("线程" + Thread.currentThread().getName() + "获取到了monitor。");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "退出了同步代码块");
    }
}
/**
 * 演示sleep不释放lock(lock需要手动释放)
 */
public class SleepDontReleaseLock implements Runnable {
    private static final Lock lock = new ReentrantLock();
    @Override
    public void run() {
        lock.lock();
        System.out.println("线程" + Thread.currentThread().getName() + "获取到了锁");
        try {
            Thread.sleep(5000);
            System.out.println("线程" + Thread.currentThread().getName() + "已经苏醒");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) {
        SleepDontReleaseLock sleepDontReleaseLock = new SleepDontReleaseLock();
        new Thread(sleepDontReleaseLock).start();
        new Thread(sleepDontReleaseLock).start();
    }
}

3.2 sleep方法响应中断

  1. 抛出InterruptedException
  2. 清除中断状态
/**
 * 每个1秒钟输出当前时间,被中断,观察。
 *      1.Thread.sleep()
 *      2.TimeUnit.SECONDS.sleep()
 */
public class SleepInterrupted implements Runnable{
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new SleepInterrupted());
        thread.start();
        Thread.sleep(6500);
        thread.interrupt();
    }
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(new Date());
            try {
                //sleep:3个小时25分钟1秒
                TimeUnit.HOURS.sleep(3);
                TimeUnit.MINUTES.sleep(25);
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println("我被中断了!");
                e.printStackTrace();
            }
        }
    }
}

TimeUnit:https://www.cnblogs.com/zhaoyanjun/p/5486726.html 

一句话总结

  • sleep方法可以让线程进入Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态
public class Main {
    public static void main(String[] args) {
        Thread myThread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
                //false 清除中断状态
                System.out.println(Thread.currentThread().isInterrupted());
            }
        });
        myThread.start();
        //false
        System.out.println(myThread.isInterrupted());
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThread.interrupt();
    }
}

3.3 sleep方法—常见面试问题

wait/notify、sleep异同(方法属于哪个对象?线程状态怎么切换?)

相同

  • 阻塞
  • 响应中断

不同

  • 同步方法中
  • 释放锁
  • 指定时间
  • 所属类

4 等待其他线程join方法

  • 作用:因为新的线程加入了我们,所以我们要等他执行完再出发
  • 用法main等待thread1执行完毕
/**
 * 演示join,注意语句输出顺序,会变化。
 */
public class Join {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "执行完毕");
        });

        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "执行完毕");
        });
        thread.start();
        thread2.start();
        System.out.println("开始等待子线程运行完毕");
        //主线程(main)等待着两个子线程执行完,在往下执行
        thread.join();
        thread2.join();
        System.out.println("所有子线程执行完毕");
    }
}
/**
 * 演示join期间被中断的效果
 */
public class JoinInterrupt {
    public static void main(String[] args) {
        Thread mainThread = Thread.currentThread();

        Thread thread1 = new Thread(() -> {
            try {
                mainThread.interrupt();
                Thread.sleep(5000);
                System.out.println("Thread1 finished.");
            } catch (InterruptedException e) {
                System.out.println("子线程中断");
            }
        });
        thread1.start();
        System.out.println("等待子线程运行完毕");

        try {
            thread1.join();
        } catch (InterruptedException e) {//!!!这里的异常是主线程抛出的
            System.out.println(Thread.currentThread().getName()+"主线程中断了");
            thread1.interrupt();
        }
        System.out.println("子线程已运行完毕");
    }
}
/**
 * 1.先join再mainThread.getState()
 * 2.通过debugger看线程join前后状态的对比
 */
public class JoinThreadState {
    public static void main(String[] args) throws InterruptedException {
        Thread mainThread = Thread.currentThread();
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(3000);
                System.out.println(mainThread.getState());
                System.out.println("Thread-0运行结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        thread.start();
        System.out.println("等待子线程运行完毕");
        thread.join();
        System.out.println("子线程运行完毕");
    }
}

!!! 每一个Thread类在run方运行结束后会自动执行notify类似的操作 (Thread.wait() 不建议这样使用 )

//java.lang.Thread#join(long)
public final synchronized void join(long millis) throws InterruptedException {
	long base = System.currentTimeMillis();
	long now = 0;
	if (millis < 0) {
		throw new IllegalArgumentException("timeout value is negative");
	}
	if (millis == 0) {
		while (isAlive()) {
			wait(0);
		}
	} else {
		while (isAlive()) {
			long delay = millis - now;
			if (delay <= 0) {
				break;
			}
			wait(delay);
			now = System.currentTimeMillis() - base;
		}
	}
}

 

等效代码

/**
 * 通过join原理,分析出join的代替写法
 */
public class JoinPrinciple {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "执行完毕");
            //自动执行notify类似的操作
        });

        thread.start();
        System.out.println("开始等待子线程运行完毕");
        //thread.join();
        synchronized (thread) {
            thread.wait();
        }
        System.out.println("所有子线程执行完毕");
    }
}

CountDownLatch或CyclicBarrier类


5 线程让步yield方法

  • 作用:释放我的CPU时间片
  • 定位:JVM不保证遵循
  • yield和sleep区别:是否随时可能再次被调度


6 常见面试问题 

1. 为什么线程通信的方法wait(),notify()和notifyAll()被定义在Object类里?而sleep定义在Thread类里?

2. 用3种方式实现生产者模式

3. Java SE 8和Java 1.8和JDK8是什么关系,是同一个东西吗?

4. Join和sleep和wait期间线程的状态分别是什么?为什么?

https://blog.csdn.net/zfy163520/article/details/104947177 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值