两种开始多线程方式
Runable接口
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("hello");
}
}
继承Thread并重写run方法
public class ThreadStyle extends Thread {
@Override
public void run() {
System.out.println("hello");
}
public static void main(String[] args) {
new ThreadStyle().start();
}
}
停止多线程
停止非阻塞的线程
一般情况下,线程退出可以使用while循环判断共享变量条件的方式,当线程内有阻塞操作时,可能导致线程无法运行到条件判断的地方而导致一直阻塞下去,这个时候就需要中断来帮助线程脱离阻塞。因此比较优雅的退出线程方式是结合共享变量和中断。
public class RightWayStop implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStop());
thread.start();
thread.sleep(100);
thread.interrupt();
}
@Override
public void run() {
int i =0;
while (!Thread.currentThread().interrupted()&&i<Integer.MAX_VALUE/2){
if(i%10000==0){
System.out.println(i);
}
i++;
if(i>=Integer.MAX_VALUE/2){
System.out.println("finished");
}
}
}
}
停止阻塞的线程
对于处于sleep,join等操作的线程,如果被调用interrupt()后,会抛出InterruptedException,然后线程的中断标志位会由true重置为false,因为线程为了处理异常已经重新处于就绪状态。
public class RightWayStop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = () -> {
int i = 0;
try {
Thread.sleep(200000);
} catch (InterruptedException e) {
System.out.println(e);
// e.printStackTrace();
}
};
Thread thread = new Thread(runnable);
thread.start();
Thread.sleep(1000);
thread.interrupt();
}
}
Thead.sleep
Thread.sleep一旦执行 会清除interupted状态
中断线程
处理中断的两种姿势
注意:
- 不要屏蔽中断
- volatile的boolean无法处理长时间阻塞的情况
优先选择在方法上抛出异常
用throws InterruptedException 标记你的方法,不采用try 语句块捕获异常,以便于该异常可以传递到顶层,让run方法可以捕获这一异常.
恢复中断
如果不想或无法传递InterruptedException(例如用run方法的时候,就不让该方法throws InterruptedException),那么应该选择在catch 子句中调用Thread.currentThread().interrupt() 来恢复设置中断状态,以便于在后续的执行依然能够检查到刚才发生了中断。
public class RightWayStop implements Runnable{
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new RightWayStop());
thread.start();
thread.sleep(3000);
thread.interrupt();
}
private void reInterupt(){
try {
System.out.println("aaa");
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
e.printStackTrace();
}
}
@Override
public void run() {
while (true) {
if(Thread.currentThread().isInterrupted()){
System.out.println("break");
break;
}
reInterupt();
}
}
}
Thread.interrupted() 和 thread.isInterrupted()
Thread.interrupted()返回当前线程是否被中断 并清除interrupt状态;
Thread对象.isInterrupted(); 返回该线程是否被中断;
synchronized
同一个时间只能执行一段代码块
抛出异常会直接释放锁
对象锁
this作为锁
public class RightWayStop {
public static void main(String[] args) throws InterruptedException {
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (this){
System.out.println("我是"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束:"+Thread.currentThread().getName());
}
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable);
thread.start();
thread1.start();
while (thread.isAlive()||thread1.isAlive()){
}
System.out.println("done");
}
}
对象作为锁
相同的锁对象(lock0或者lock1)才会synchronized运行
public class RightWayStop {
public static void main(String[] args) throws InterruptedException {
Object lock0 = new Object();
Object lock1 = new Object();
Runnable runnable = new Runnable() {
@Override
public void run() {
synchronized (lock0) {
System.out.println("(lock)我是:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("(lock)结束:" + Thread.currentThread().getName());
}
synchronized (lock1) {
System.out.println("(lock1)我是:" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("(lock1)结束:" + Thread.currentThread().getName());
}
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable);
thread.start();
thread1.start();
while (thread.isAlive() || thread1.isAlive()) {
}
System.out.println("done");
}
}
方法锁(非static)
synchronized 的方法锁对象默认为this
public class RightWayStop {
public synchronized void runMe() throws InterruptedException {
System.out.println("我是:" + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("结束:" + Thread.currentThread().getName());
}
public static void main(String[] args) throws InterruptedException {
Object lock0 = new Object();
Object lock1 = new Object();
RightWayStop rightWayStop = new RightWayStop();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
rightWayStop.runMe();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable);
thread.start();
thread1.start();
while (thread.isAlive() || thread1.isAlive()) {
}
System.out.println("done");
}
}
类锁
静态方法锁
synchronized静态方法上的所有Runnable的实例共用一把锁,普通的方法Runnable的实例不能锁
public class ClassLockThread implements Runnable{
private static ClassLockThread instance1 = new ClassLockThread();
private static ClassLockThread instance2 = new ClassLockThread();
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
}
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static synchronized void method() throws InterruptedException {
System.out.println("开始:"+Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("结束:"+Thread.currentThread().getName());
}
}
类锁代码块
public class ClassLockThread implements Runnable {
private static ClassLockThread instance1 = new ClassLockThread();
private static ClassLockThread instance2 = new ClassLockThread();
public static void main(String[] args) {
Thread thread1 = new Thread(instance1);
Thread thread2 = new Thread(instance2);
thread1.start();
thread2.start();
}
@Override
public void run() {
try {
method();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void method() throws InterruptedException {
synchronized (ClassLockThread.class) {
System.out.println("开始:" + Thread.currentThread().getName());
Thread.sleep(1000);
System.out.println("结束:" + Thread.currentThread().getName());
}
}
}
线程的状态
wait与notify
public class RightWayStop {
public static Object object = new Object();
static class Thread1 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println("thread1 start");
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread1 finished");
}
}
}
static class Thread2 extends Thread {
@Override
public void run() {
synchronized (object) {
System.out.println("thread2 start");
object.notify();
System.out.println("thread2 finished");
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
Thread.sleep(100);
thread2.start();
}
}
Sleep
sleep方法可以让线程进入Timed_Waiting状态,并且不占用CPU资源,但是不释放锁,直到规定时间后再执行,休眠期间如果被中断,会抛出异常并清除中断状态。
Sleep的替代方法
TimeUnit.SECONDS.sleep(1000);
Join
加入当前线程,并等待其运行完毕,再运行当前线程
/**
* 描述: 演示join,注意语句输出顺序,会变化。
*/
public class RightWayStop {
public static final Object object = new Object();
public static int i=0;
static class Thread1 extends Thread {
@Override
public void run() {
try {
Thread1.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
}
static class Thread2 extends Thread {
@Override
public void run() {
try {
Thread1.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println(Thread.currentThread().getName()+"执行完毕");
}
}
Join的中断写法
/**
* 描述: 演示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等价写法
Thread退出时会调用notifyAll
synchronized (thread1){
thread1.wait();
}
优先级
优先级不可靠,程序设计不应该依赖优先级
线程异常处理
1.在run方法里捕获异常 2.实现UncaughtExceptionHandler全局处理异常
public class ExceptionInChildThread implements Runnable {
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("catcher 1"));
new Thread(new ExceptionInChildThread()).start();
for (int i =0;i<1000;i++){
System.out.println(i);
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
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 anonymousLogger = Logger.getAnonymousLogger();
anonymousLogger.log(Level.WARNING,"线程异常"+t.getName(),e);
System.out.println(name+"补货了异常");
}
}
线程安全
《Java Concurrency In Practice》的作者Brian Goetz对“线程安全”有一个比较恰当的定义:“当多个线程访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那这个对象是线程安全的”。
并发底层原理
jvm内存结构
java内存模型
重排序
java对象模型
重排序
编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序,在不改变程序语义的前提下,尽可能减少寄存器的读取、存储次数,充分复用寄存器的存储值。
/**
* 描述: 演示重排序的现象 “直到达到某个条件才停止”,测试小概率事件
*/
public class OutOfOrderExecution {
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
for (; ; ) {
i++;
x = 0;
y = 0;
a = 0;
b = 0;
CountDownLatch latch = new CountDownLatch(3);
Thread one = new Thread(new Runnable() {
@Override
public void run() {
try {
latch.countDown();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
a = 1;
x = b;
}
});
Thread two = new Thread(new Runnable() {
@Override
public void run() {
try {
latch.countDown();
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
b = 1;
y = a;
}
});
two.start();
one.start();
latch.countDown();
one.join();
two.join();
String result = "第" + i + "次(" + x + "," + y + ")";
if (x == 0 && y == 0) {
System.out.println(result);
break;
} else {
System.out.println(result);
}
}
}
}
可见性
一个线程对共享变量值的修改,能够及时地被其他线程看到需要时间。
public class FieldVisibility {
int a = 1;
int b = 2;
public static void main(String[] args) {
FieldVisibility test = new FieldVisibility();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.change();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.print();
}
}).start();
}
private void print() {
System.out.println("b:"+b);
System.out.println("a:"+a);
}
private void change() {
a = 3;
b = a;
}
}
发生可见性问题时 a=1,b=3 原因是虽然ab两个值已经被修改但是a的修改尚未被第二个线程看到 使用volitile可以修复这种问题
可见性原因
所有的共享变量存在于主内存中,每个线程有自己的本地内存,而且线程读写共享数据也是通过本地内存交换的,所以才导致了可见性问题。
happens-before
如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作; 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作; volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作; 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C; 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作; 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生; 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行; 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;
volatile的作用
1.可见性 2.禁止重排序 但是volatile无法保证原子性
用保证synchronized 可见性
public class FieldVisibilityABCD {
int a = 1;
int b = 2;
int c = 2;
int d = 2;
private void change() {
a = 3;
b = 4;
c = 5;
synchronized (this) {
d = 6;
}
}
private void print() {
synchronized (this) {
int aa = a;
}
int bb = b;
int cc = c;
int dd = d;
System.out.println("b=" + b + ";a=" + a);
}
public static void main(String[] args) {
while (true) {
FieldVisibilityABCD test = new FieldVisibilityABCD();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.change();
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
test.print();
}
}).start();
}
}
}
原子性
原子性:一个操作不能被打断,要么全部执行完毕,要么不执行。
默认的原子操作
1.除了long和double之外的基本类型的赋值操作(因为long和double类型是64位的,所以它们的操作在32位机器上不算原子操作,而在64位的机器上是原子操作。)(实际上,商用虚拟机不大可能出现)
2.引用
3.java.util.concurrent.atomic.*
双重检查单例模式
synchronized并不防止synchronized内部的重排序,并且synchronized可以和外部的if(instance==null){同时运行所以需要 volatile
/**
* 线程 安全的双重检查单例
*/
public class Singleton6 {
private volatile static Singleton6 instance;
private Singleton6() {
}
public static Singleton6 getInstance() {
if (instance == null) {
synchronized (Singleton6.class) {
if (instance == null) {
instance = new Singleton6();
}
}
}
return instance;
}
}
/**
* 线程 安全的静态内部类单例
*/
public class Singleton7 {
private Singleton7() {
}
private static class SingletonInstance {
private static final Singleton7 INSTANCE = new Singleton7();
}
public static Singleton7 getInstance() {
return SingletonInstance.INSTANCE;
}
}
/**
*枚举单例模式(线程安全)
*/
public enum Singleton8 {
INSTANCE;
public void whatever() {
}
}
发现死锁
命令行
jps查找进程id
jstack 进程id
threadMXBean
public class DeadLock {
static Object Lock1 = new Object();
static Object Lock2 = new Object();
static class Thread1 extends Thread{
@Override
public void run() {
synchronized (Lock1){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Lock2){
}
}
}
}
static class Thread2 extends Thread{
@Override
public void run() {
synchronized (Lock2){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Lock1){
}
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
thread2.start();
Thread.sleep(1000);
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
for (int i = 0; i < deadlockedThreads.length ; i++) {
ThreadInfo threadInfo = threadMXBean.getThreadInfo(deadlockedThreads[i]);
String threadName = threadInfo.getThreadName();
System.out.println(threadName);
}
}
}
trylock
ublic class TryLock implements Runnable {
static int i = 0;
static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if(lock.tryLock(1000, TimeUnit.MILLISECONDS)) {
System.out.println(Thread.currentThread().getName()+"获取锁成功了");
try{
for (int j = 0; j < 1000; j++) {
i++;
}
}catch(Exception ex){
}finally{
//lock.unlock(); //释放锁
}
}else {
System.out.println(Thread.currentThread().getName()+"获取锁失败了");
//如果不能获取锁,则直接做其他事情
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread thread = new Thread(new TryLock());
Thread thread1 = new Thread(new TryLock());
thread.start();
thread1.start();
try {
thread.join();
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
System.out.println("done");
}
}