线程是操做系统可以进行运算调度的最小单位,它被包含在进程中,是进程中的实际运做单位。程序员能够经过它进行多处理器编程。你能够经过使用多线程对运算密集的任务提速。好比,若是一个线程完成一个任务须要100毫秒,那么用十个线程完成此任务只需10毫秒。Java在语言层面对多线程提供了卓越的支持,他是一个很好的卖点。java
当一个网站遇到并发量很大的问题时,普通的系统很快就会达到性能瓶颈,而使用多线程能够轻松的解决性能问题。程序员
There are two ways to create a new thread of execution. One is to declare a class to be a subclass of Thread. This subclass should override the run method of class Thread. An instance of the subclass can then be allocated and started. For example, a thread that computes primes larger than a stated value could be written as follows数据库
The other way to create a thread is to declare a class that implements the Runnable interface. That class then implements the run method. An instance of the class can then be allocated, passed as an argument when creating Thread, and started. The same example in this other style looks like the following编程
上面的两段引用出自Oracle官方文档,将这两段英文翻译过来意思就是说实现线程的方式有两个种,第一种是继承Thread
类的方式,另外一种就是实现Runnable
接口。缓存
/**
* 用Thread方式实现线程
*/
public class ThreadStyle extends Thread {
public static void main(String[] args) {
ThreadStyle thread = new ThreadStyle();
thread.start();
}
@Override
public void run() {
System.out.println("用Thread类实现线程");
}
}
复制代码
/**
* Runnable方式建立线程
*/
public class RunnableStyle implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new RunnableStyle());
thread.start();
}
@Override
public void run() {
System.out.println("用Runnable方式实现线程");
}
}
复制代码
Runnable
接口方式能够下降代码耦合度Thread
类后就没法再继承别的类,下降了类的扩展性Thread
类须要每实现一个线程类就进行一次建立,形成了较大的资源开销。总结:综上所述,实现线程采用Runnable
接口的方式比较好。安全
/**
* 同时使用Runnable和Thread两种方式实现多线程
*/
public class BothRunnableThread {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我来自Runnable");
}
}) {
@Override
public void run() {
System.out.println("我来自Thread");
}
}.start();
}
}
复制代码
运行结果bash
@Override
public void run() {
if (target != null) {
target.run();
}
}
复制代码
实现Runnable
接口方式会要求重写run()
方法,因此会执行其中的三行代码,其中target
是一个private Runnable target;
,由于后面又覆盖了一次Thread类的run
方法,因此if
判断也就消失了就会直接执行本身在run
方法中的打印语句。多线程
总结:建立线程的方式只有构造Thread
类一种方法,可是实现Thread
类中的run
方法有两种方式。并发
/**
* 对比start和run这两种启动线程的方式
*/
public class StartAndRunMethod {
public static void main(String[] args) {
Runnable runnable = () ->{
System.out.println(Thread.currentThread().getName());
};
runnable.run();
new Thread(runnable).start();
}
}
复制代码
运行结果dom
start()
方法
调用
start()
方法意味着向JVM发起通知,若是有空能够来我这里执行一下么,本质也就是经过调用start()
方法请求JVM运行此线程,可是调用该方法以后不必定就会当即运行,而是须要等到JVM有空执行时才会执行。
public synchronized void start() {
//进行线程状态的检查,默认值是0
if (threadStatus != 0)
throw new IllegalThreadStateException();
//加入线程组
group.add(this);
boolean started = false;
try {
//执行线程的方法,是一个native方法
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
复制代码
总结:执行start()
要经历的步骤
start0()
方法@Override
public void run() {
//判断传入的Runnable是否为空
if (target != null) {
//不为空则启动
target.run();
}
}
复制代码
从这里能够看出上面直接调用run()
方法为何会在主线程中执行,这是由于target
是空的因此不会启动,这样就和调用一个普通的方法没有区别了。
在Java中中止线程的最好方式是使用interrupt,可是这样仅仅会对须要中止的线程进行通知而不是直接停掉,线程是否的中止的权利属于须要被中止的线程(什么时候中止以及是否中止),这就须要请求中止方和被中止方都遵循一种编码规范。
run()
方法中的代码运行完毕(最多见)/**
* run方法内没有sleep或wait方法时,中止线程
*/
public class RightWayStopThreadWithoutSleep implements Runnable {
@Override
public void run() {
int num = 0;
//没有收到通知时进行循环操做
while (!Thread.currentThread().isInterrupted() && num <= Integer.MAX_VALUE / 2){
if (num % 10000 == 0){
System.out.println(num + "是10000的倍数");
}
num++;
}
System.out.println("任务运行结束了");
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithoutSleep());
thread.start();
Thread.sleep(2000);
//发起通知
thread.interrupt();
}
}
复制代码
/**
* 带有sleep的中断线程的中止方法
*/
public class RightWayStopThreadWhthSleep {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () ->{
int num = 0;
try {
while (num <= 300 && !Thread.currentThread().isInterrupted()){
if (num % 100 == 0){
System.out.println(num + "是100的倍数");
}
num++;
}
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(500);
thread.interrupt();
}
}
复制代码
/**
* 若是在每次循环中都会sleep或wait,须要如何中止线程
*/
public class RightWayStopThreadWithSleepEveryLoop implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadWithSleepEveryLoop());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
@Override
public void run() {
try {
int num = 0;
while (num <= 30){
if (num % 10 == 0){
System.out.println(num + "是10的倍数");
}
num++;
Thread.sleep(50);
}
System.out.println("任务完成了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
注意:若是每次迭代都有阻塞状态,这样就不须要判断是否收到中断请求,由于在sleep
过程当中会对中断进行响应
while
内try/catch
的问题/**
* 若是while里面放try/catch,会致使中断失效
*/
public class CantInterrupt {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () ->{
int num = 0;
while (num <= 10000){
if (num % 100 == 0 && !Thread.currentThread().isInterrupted()){
System.out.println(num + "是100的倍数");
}
num++;
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(5000);
thread.interrupt();
}
}
复制代码
注意:在while
循环中加入try/catch
没法中止线程,由于在try/catch
中异常被捕获后仍是不知足跳出循环的条件,interrupt
标记位被清除也就没法检查到被中断的迹象,因此会继续执行线程
原则:
抛出式
/**
* 最佳实践:catch住InterruptedException后优先选择在方法签名中抛出异常,
* 那么在run()方法中就会强制try/catch
*/
public class RightWayStopThreadInProd implements Runnable {
@Override
public void run() {
try {
while (true){
System.out.println("go");
throwInMethod();
}
} catch (InterruptedException e) {
//保存日志
//中止程序
System.out.println("保存日志、中止程序");
e.printStackTrace();
}
}
private void throwInMethod() throws InterruptedException {
Thread.sleep(2000);
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
复制代码
注意:在方法中遇到异常应该首先选择抛出,异常由run
方法进行处理,这样能够增长代码的健壮性
恢复中断式
/**
* 最佳实践2:在catch语句中调用Thread.currentThread.interrupt()
* 来恢复中断状态,以便于在后续的执行中依然可以检查到刚才发生了中断
*/
public class RightWayStopThreadInProd2 implements Runnable {
@Override
public void run() {
while (true) {
if (Thread.currentThread().isInterrupted()){
System.out.println("Interrupted,程序运行结束");
break;
}
reInterrupt();
}
}
private void reInterrupt() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStopThreadInProd2());
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
复制代码
注意:若是在调用的方法中不抛出异常的话也能够在catch块再次调用Thread.currentThread().interrupt();
,这样能够从新设置中断表示已经有中断发生,从而让run
方法感知
stop
方法中止/**
* 错误的中止方法:用stop中止线程,会致使线程运行一半忽然中止,这样没有办法完成一个基本单位(一个连队)的操做,
* 会形成脏数据(有的连队多领取或少领取装备)
*/
public class StopThread implements Runnable {
@Override
public void run() {
/**
* 模拟指挥军队:一个5个连队,每一个连队10人,以连队为单位发放弹药,叫到号
* 的士兵去领取
*/
for (int i = 0; i < 5; i++) {
System.out.println("连队" + i + "开始领取装备");
for (int j = 0; j < 10; j++) {
System.out.println(j);
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("连队" + i + "领取完毕");
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new StopThread());
thread.start();
//1s以后战争爆发须要奔赴战场
Thread.sleep(1000);
//中止领取
thread.stop();
}
}
复制代码
volatile
设置boolean
标记位/**
* 演示用volatile的局限part2 陷入阻塞时volatile没法中止
* 此例中生产者的生产速度很快,可是消费者的消费速度很慢,因此阻塞队列满了之后,
* 生产者会阻塞,生产者会等待消费者进一步消费
*/
public class WrongWayVolatileCantStop {
public static void main(String[] args) throws InterruptedException {
BlockingQueue<Integer> storage = new ArrayBlockingQueue<>(10);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()){
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不须要更多数据了");
//一旦消费者不须要更多数据了,咱们应当让消费者也停下来,可是实际状况。。。
producer.canceled = true;
System.out.println(producer.canceled);
}
}
class Producer implements Runnable {
public volatile boolean canceled = false;
BlockingQueue<Integer> storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 100 == 0) {
//是100的倍数时,将num放入阻塞队列
storage.put(num);
System.out.println(num + "是100的倍数,被放入阻塞队列");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者中止运行");
}
}
}
class Consumer {
BlockingQueue<Integer> storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
复制代码
注意:若是线程长时间阻塞,这种方法就会失效
对上面方式的修复
/**
* 用中断修复刚才一直等待的问题
*/
public class WrongWayVolatileFixed {
public static void main(String[] args) throws InterruptedException {
WrongWayVolatileFixed body = new WrongWayVolatileFixed();
ArrayBlockingQueue storage = new ArrayBlockingQueue(10);
Producer producer = body.new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(1000);
Consumer consumer = body.new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不须要更多数据了");
producerThread.interrupt();
}
class Producer implements Runnable {
BlockingQueue storage;
public Producer(BlockingQueue storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !Thread.currentThread().isInterrupted()) {
//若是num是100的倍数,就将他添加到阻塞队列
if (num % 100 == 0) {
storage.put(num);
System.out.println(num + "是100的倍数,被放入阻塞队列");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者线程阻塞");
}
}
}
class Consumer {
BlockingQueue storage;
public Consumer(BlockingQueue storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if (Math.random() > 0.95) {
return false;
}
return true;
}
}
}
复制代码
答:须要根据状况的不一样而采起不一样的方法
若是线程阻塞是由于调用了sleep()、wait()或join()
致使的,能够经过抛出InterruptException
异常来唤醒,可是不能响应InterruptException
异常则没法经过这种方法进行唤醒。 可是咱们能够利用其余能够响应中断的方法,好比: ReentrantLock.lockInterruptibly()
,关闭套接字使线程当即返回等方法来达到目的。 因此如何处理不可中断的阻塞要视状况而定。
start()
方法start()
方法就会进入Runnable状态。synchronized
修饰的代码后,而且该锁已经被其它线程拿走了就会进入阻塞状态。timeout
参数的wait()
方法,须要等待唤醒不然不会醒来run()
方法被意外终止。注意:通常而言把Blocked(被阻塞)、Waiting(等待)、Timed_Waiting(计时等待)
都称为阻塞,而不只仅是Blocked
。
做用:wait()
方法会让线程进入等待状态,若是想让线程继续执行必须知足一下四种方式中的一种
notify()
方法,本线程正好被唤醒notifyAll()
方法,全部线程都会被唤醒wait(long timout)
达到了参数的时间,若是传入0会进入永久等待interrupt()
方法进行唤醒/**
* 展现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 {
object.wait(); //等待期间若是遇到中断会抛出InterruptedException
} 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 {
new Thread1().start();
Thread.sleep(200);
new Thread2().start();
}
}
复制代码
总结:wait()方法会释放对象锁,只有释放了对象锁其余线程才能够进入synchronized
代码块
/**
* 3个线程,线程1和线程2首先被阻塞,线程3去唤醒线程1和线程2
* 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()+"获得对象锁");
try {
System.out.println(Thread.currentThread().getName()+"等待下一次开始");
resourceA.wait();
System.out.println(Thread.currentThread().getName()+"立刻运行结束了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
WaitNotifyAll waitNotifyAll = new WaitNotifyAll();
Thread threadA = new Thread(waitNotifyAll);
Thread threadB = new Thread(waitNotifyAll);
Thread threadC = new Thread(new Runnable() {
@Override
public void run() {
synchronized (resourceA){
resourceA.notifyAll();
System.out.println("线程C已经成功notify了");
}
}
});
threadA.start();
threadB.start();
Thread.sleep(200);
threadC.start();
}
}
复制代码
总结:notify
只会唤醒等待线程中的一个而notifyAll
则会唤醒全部等待线程,在线程启动时必定要等到线程进入等待状态以后再进行唤醒
/**
* 证实wait只释放当前那把锁
*/
public class WaitNotifyReleaseOwnMonitor {
private static Object resourceA = new Object();
private static Object resourceB = new Object();
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
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.");
resourceA.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
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();
}
}
复制代码
总结:wait()
只释放当前monitor
synchronized
时就会获取到对象锁,若是执行了
wait
方法后就会进入到等待集,在等待集中若是对某个线程执行了
notify
操做它就会再次回到入口集,若是使用的是
notifyAll
那么等待集中的所有线程都会进入到入口集。
/**
* 用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);
Thread thread1 = new Thread(producer);
Thread thread2 = new Thread(consumer);
thread1.start();
thread2.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 {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
storage.add(new Date());
System.out.println("生产出了一个商品,仓库中有:" + storage.size() + "个商品");
notify();
}
public synchronized void take() {
while (storage.size() == 0) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("取走了:" + storage.poll() + ",还剩下" + storage.size() + "个商品");
notify();
}
}
复制代码
总结:生产者生产到10的时候就会进入wait
状态,不然就会进行生产,消费者将队列中的商品消费到0时就会进入wait
状态,就这就会消费,当生产者生产出了商品就会notify
消费者,将其唤醒。反之消费者就会notify
唤醒生产者
/**
* 两个线程交替打印0到100奇偶数
*/
public class WaitNotifyPrintOddEvenSyn {
private static int count;
private static final Object lock = new Object();
//新建2个线程
//1个只处理偶数,第2个处理奇数(用位运算)
//用synchronized进行通讯
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
while (count < 100){
synchronized (lock){
if ((count & 1) == 0){
System.out.println(Thread.currentThread().getName() + ":" + count);
count++;
}
}
}
}
},"偶数").start();
new Thread(new Runnable() {
@Override
public void run() {
while (count < 100){
synchronized (lock){
if ((count & 1) != 0){
System.out.println(Thread.currentThread().getName() + ":" + count);
count++;
}
}
}
}
},"奇数").start();
}
}
复制代码
总结:这样实现有可能形成同一个线程老是拿到对象锁,可是if
判断只会进一次,这样就影响了效率,可使用wait/notify方式解决
/**
* 两个线程交替打印0到100的奇偶数,使用wait/notify
*/
public class WaitNotifyPrintOddEvenWait {
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{
private static int count;
private static Object lock = new Object();
@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();
}
}
}
}
}
}
}
复制代码
总结:拿到锁就打印,打印完就唤醒若是知足条件就等待,这样能够提升程序的效率
做用:让线程进入阻塞状态,睡眠时不会占用cpu资源。
注意:sleep
方法不会释放synchronized
和lock
锁
/**
* 展现线程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();
}
}
复制代码
相同:
不一样:
做用:由于新的线程加入了咱们,因此咱们要等他执行完再出发 用法:main等待thread1执行完毕,主线程等待子线程。
/**
* 演示join用法,注意语句输出顺序是否会变化
*/
public class Join {
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "已经执行完毕");
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "已经执行完毕");
}
});
thread1.start();
thread2.start();
System.out.println("开始等待子线程运行完毕");
// thread1.join();
// thread2.join();
System.out.println("全部子线程执行完毕");
}
}
复制代码
/**
* 演示join期间被中断的效果
*/
public class JoinInterrupt {
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
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("子线程已经运行完毕");
}
}
复制代码
join源码
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
方法默认传入的millis
是0,此时会进入永久等待,可是因为JVM的缘由在Thread
类中每一个方法结束都会有一个notify
操做,因此join
才不须要手动进行唤醒。
由于join
方法的底层是wait
因此使用以下代码能够与join
等价
synchronized (thread1){
thread1.wait();
}
复制代码
为Thread
类的对象加锁,在结束时会自动进行释放,因此能够达到join
的效果。
做用:释放个人CPU时间片,但不会释放锁也不会进入阻塞。
定位:JVM不保证遵循yield
sleep期间线程进入阻塞状态因此不会再被调度。而yield只是暂时做出让步但还能够处于竞争状态。
线程id是不可修改的,主线程的id从1开始
private static synchronized long nextThreadID() {
return ++threadSeqNumber;
}
复制代码
线程的id是先++后返回,因此主线程的id为1
查看子线程的id
/**
* Id从1开始,JVM运行起来以后,咱们本身建立的线程Id早已不是0
*/
public class Id {
public static void main(String[] args) {
Thread thread = new Thread();
System.out.println("主线程的ID:" + Thread.currentThread().getId());
System.out.println("子线程的ID:" + thread.getId());
}
}
复制代码
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private static synchronized int nextThreadNum() {
return threadInitNumber++;
}
复制代码
经过Thread
类的构造方法能够发现,若是不传入线程的名字就会默认在Thread-
后面添加一个从0开始的数字,由于有synchronized
因此不会出现线程重名的状况。
做用:给用户线程提供服务(一共有用户线程和守护线程两大类)
若是程序中有用户线程,JVM不会中止工做,可是若是程序中只有守护线程,那么守护线程也就没有了守护的对象,因此只有守护线程的状况下JVM会中止工做。例如:垃圾处理器就是守护线程。
守护线程的3个特性
守护线程和用户线程的区别
由于都是线程因此总体没什么区别
应该把用户线程设置为守护线程吗?
不该该。若是把用户线程设置为守护线程了,那么在执行任务时JVM发现此时没有用户线程,这样就会中止虚拟机,从而致使数据不一致的状况。
能够经过设置优先级来增长某个线程的运行次数,优先级最高能够设置为10,默认是5,最低是1。
注意:程序设计不该该依赖于优先级
在程序的运行中有不少异常是不可预料的,若是在返回以前不被拦截而是直接返回给用户的话这样可能会引起安全性的问题。
使用UncaughtExceptionHandler
/**
* 单线程,抛出,处理,有异常堆栈
* 多线程状况下子线程发生异常,会有什么不一样?
*/
public class ExceptionInChildThread implements Runnable {
public static void main(String[] args) {
new Thread(new ExceptionInChildThread()).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
复制代码
当子线程抛出异常时,不会对主线程的运行产生影响。在真实的生产环境中由于有大量日志的产生,可能会忽略子线程中出现的问题。
/**
* 1.不加try catch时抛出4个异常,都带线程名
* 2.若是加了try catch,但愿能够捕获到第一个线程的异常并处理,线程234不该该再运行,
* 但愿看到打印出的Caught Exception
* 3.执行时发现,根本没有Caught Exception,线程234依然运行,而且还抛出异常
*
* 说明线程的异常不能用传统方法捕获
*/
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
复制代码
/**
* 1.不加try catch时抛出4个异常,都带线程名
* 2.若是加了try catch,但愿能够捕获到第一个线程的异常并处理,线程234不该该再运行,
* 但愿看到打印出的Caught Exception
* 3.执行时发现,根本没有Caught Exception,线程234依然运行,而且还抛出异常
*
* 说明线程的异常不能用传统方法捕获
*/
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
} catch (RuntimeException e) {
System.out.println("Caught Exception");
e.printStackTrace();
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
复制代码
run()
方法中进行try-catch
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("Caught Exception");
}
}
复制代码
实现方案
建立MyUncaughtExceptionHandler
/**
* 实现本身的UncaughtExceptionHandler
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private String name;
public MyUncaughtExceptionHandler(String name) {
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING, "线程异常,终止啦 : " + t.getName());
System.out.println(name + " 捕获了" + t.getName() + "的"+ e +"异常");
}
}
复制代码
使用MyUncaughtExceptionHandler
/**
* 使用本身建立的UncaughtExceptionHandler
*/
public class UseOwnUncaughtExceptionHandler implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread.setDefaultUncaughtExceptionHandler(new
MyUncaughtExceptionHandler("捕获器1"));
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
复制代码
线程是一把双刃剑,他在提升程序执行效率的同时也会存在一些弊端,好比线程安全问题,这会致使数据发生错乱,还有就是性能问题,好比服务响应慢、吞吐率低、资源开销大。使用线程的目的就是为了让程序更好的运行,若是这些问题不解决就本末倒置了,在这里学习一下如何解决吧!
当多个线程访问一个对象时,若是不用考虑这些线程在运行时环境下的调度和交替执行,也不须要进行额外的同步,或者在调用方在进行任何其余的协调操做,调用这个对象的行为均可以得到正确的结果,那么这个对象就是线程安全的。
线程安全问题主要分为以下两种状况
/**
* 第一种状况:运行结果出错
* 演示计数不许确(减小),找出具体出错的位置
*/
public class MultiThreadError implements Runnable {
private int index = 0;
static MultiThreadError instance = new MultiThreadError();
@Override
public void run() {
for (int i = 0; i < 10000; i++) {
index++;
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(instance.index);
}
}
复制代码
i+1
的操做后线程进行了切换,线程2不知道线程1已经执行了
+1
操做,依然执行+1,加完以后又切换到线程1,此时线程1的加操做已经完成了,i变成了2,线程2再次执行后,i的值也变成了2,这就是致使i出现少加的状况的缘由。
/**
* 第一种状况:运行结果出错
* 演示计数不许确(减小),找出具体出错的位置
*/
public class MultiThreadError implements Runnable {
private int index = 0;
static AtomicInteger realIndex = new AtomicInteger();
static AtomicInteger wrongCount = new AtomicInteger();
static volatile CyclicBarrier cyclicBarrier1 = new CyclicBarrier(2);
static volatile CyclicBarrier cyclicBarrier2 = new CyclicBarrier(2);
static MultiThreadError instance = new MultiThreadError();
final boolean[] marked = new boolean[10000000];
@Override
public void run() {
marked[0] = true;
for (int i = 0; i < 10000; i++) {
try {
cyclicBarrier2.reset();
cyclicBarrier1.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
index++;
try {
cyclicBarrier1.reset();
cyclicBarrier2.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
realIndex.incrementAndGet();
synchronized (instance){
if (marked[index] && marked[index-1]){
System.out.println("发生了错误" + index);
wrongCount.incrementAndGet();
}
marked[index] = true;
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(instance);
Thread thread2 = new Thread(instance);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("表面上的结果是:" + instance.index);
System.out.println("真正运行的次数是:" + realIndex.get());
System.out.println("错误的次数是:" + wrongCount.get());
}
}
复制代码
/**
* 第二种线程安全问题,演示死锁
*/
public class MultiThreadError2 implements Runnable {
int flag;
static Object o1 = new Object();
static Object o2 = new Object();
public static void main(String[] args) {
MultiThreadError2 r1 = new MultiThreadError2();
MultiThreadError2 r2 = new MultiThreadError2();
r1.flag = 1;
r2.flag = 0;
new Thread(r1).start();
new Thread(r2).start();
}
@Override
public void run() {
System.out.println("flag : " + flag);
if (flag == 1){
synchronized (o1){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o2){ //想拿到o2却始终拿不到
System.out.println("1");
}
}
}
if (flag == 0){
synchronized (o2){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (o1){ //想拿到o1却始终拿不到
System.out.println("0");
}
}
}
}
}
复制代码
什么是发布
对象可让超出范围以内的类进行使用,好比在方法结束后return 一个对象,从而让外部可使用这个对象,这就是对象的发布。
什么是逸出
逸出是指将对象发布到了不应发布的地方,好比:
private
的对象(private正常只能在本类中使用)this
赋值,隐式逸出——注册监听事件,构造函数中运行线程return私有对象致使的逸出
/**
* 发布逸出
*/
public class MultiThreadError3 {
private Map<String, String> states;
public MultiThreadError3(){
states = new HashMap<>();
states.put("1", "周一");
states.put("2", "周二");
states.put("3", "周三");
states.put("4", "周四");
}
public Map<String, String> getStates(){
return states;
}
public static void main(String[] args) {
MultiThreadError3 multiThreadError3 = new MultiThreadError3();
Map<String, String> states = multiThreadError3.getStates();
System.out.println(states.get("1"));
states.remove("1");
System.out.println(states.get("1"));
}
}
复制代码
private
的本意就是不但愿外部访问到,能够经过
return
以后能够从外部对数据进行修改,这就很危险了!
构造方法未初始化完成就赋值致使逸出
/**
* 初始化未完毕就this赋值
*/
public class MultiThreadError4 {
static Point point;
public static void main(String[] args) throws InterruptedException {
new PointMaker().start();
Thread.sleep(10);
if (point != null){
System.out.println(point);
}
}
}
class Point{
private final int x, y;
public Point(int x, int y) throws InterruptedException {
this.x = x;
MultiThreadError4.point = this;
Thread.sleep(100);
this.y = y;
}
@Override
public String toString() {
return x + "," + y;
}
}
class PointMaker extends Thread{
@Override
public void run() {
try {
new Point(1, 1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
复制代码
sleep
的时间增长到100ms以上便不会发生逸出
注册监听器致使逸出
/**
* 观察者模式
*/
public class MultiThreadError5 {
int count;
public MultiThreadError5(MySource source){
source.registerListener(new EventListener() {
@Override
public void onEvent(Event e) {
System.out.println("\n我获得的数字是:" + count);
}
});
for (int i = 0; i < 10000; i++) {
System.out.print(i);
}
count = 100;
}
static class MySource {
private EventListener listener;
void registerListener(EventListener eventListener) {
this.listener = eventListener;
}
void eventCome(Event e){
if (listener != null){
listener.onEvent(e);
}else{
System.out.println("还未初始化完毕");
}
}
}
interface EventListener {
void onEvent(Event e);
}
interface Event {
}
public static void main(String[] args) {
MySource mySource = new MySource();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
mySource.eventCome(new Event() {
});
}
}).start();
MultiThreadError5 multiThreadError5 = new MultiThreadError5(mySource);
}
}
复制代码
在构造方法中使用线程致使逸出
/**
* 构造函数中新建线程
*/
public class MultiThreadError6 {
private Map<String, String> states;
public MultiThreadError6() {
new Thread(new Runnable() {
@Override
public void run() {
states = new HashMap<>();
states.put("1", "周一");
states.put("2", "周二");
states.put("3", "周三");
states.put("4", "周四");
}
}).start();
}
public Map<String, String> getStates() {
return states;
}
public static void main(String[] args) {
MultiThreadError6 multiThreadError6 = new MultiThreadError6();
Map<String, String> states = multiThreadError6.getStates();
System.out.println(states.get("1"));
}
}
复制代码
/**
* 发布逸出
*/
public class MultiThreadError3 {
private Map<String, String> states;
public MultiThreadError3(){
states = new HashMap<>();
states.put("1", "周一");
states.put("2", "周二");
states.put("3", "周三");
states.put("4", "周四");
}
public Map<String, String> getStates(){
return states;
}
public Map<String, String> getStatesImproved(){
return new HashMap<>(states); //建立一个states副本
}
public static void main(String[] args) {
MultiThreadError3 multiThreadError3 = new MultiThreadError3();
Map<String, String> states = multiThreadError3.getStates();
// System.out.println(states.get("1"));
// states.remove("1");
// System.out.println(states.get("1"));
System.out.println(multiThreadError3.getStatesImproved().get("1"));
multiThreadError3.getStatesImproved().remove("1");
System.out.println(multiThreadError3.getStatesImproved().get("1"));
}
}
复制代码
/**
* 用工厂模式解决监听器注册问题
*/
public class MultiThreadError7 {
int count;
private EventListener listener;
private MultiThreadError7(MySource source){
listener = new EventListener() {
@Override
public void onEvent(MultiThreadError7.Event e) {
System.out.println("\n我获得的数字是:" + count);
}
};
for (int i = 0; i < 10000; i++) {
System.out.print(i);
}
count = 100;
}
public static MultiThreadError7 getInstance(MySource source){
MultiThreadError7 safeListener = new MultiThreadError7(source);
source.registerListener(safeListener.listener);
return safeListener;
}
static class MySource {
private MultiThreadError7.EventListener listener;
void registerListener(MultiThreadError7.EventListener eventListener) {
this.listener = eventListener;
}
void eventCome(MultiThreadError7.Event e){
if (listener != null){
listener.onEvent(e);
}else{
System.out.println("还未初始化完毕");
}
}
}
interface EventListener {
void onEvent(MultiThreadError7.Event e);
}
interface Event {
}
public static void main(String[] args) {
MultiThreadError7.MySource mySource = new MultiThreadError7.MySource();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
mySource.eventCome(new MultiThreadError7.Event() {
});
}
}).start();
MultiThreadError7 multiThreadError7 = new MultiThreadError7(mySource);
}
}
复制代码
HashMap
在并发中可能会出现问题)调度:上下文切换
上下文切换能够认为是内核在CPU上进行的如下活动:(1)挂起一个进程将进程中的状态(上下文)存储在内存中的某处。(2)在内存中检索下一个进程的状态将它在CPU寄存器中恢复。(3)跳转到程序计数器所指向的位置以恢复该线程
缓存开销:CPU从新缓存
什么时候会致使密集的上下文切换:频繁竞争锁或者由于IO频繁致使阻塞
协做:内存同步