多线程与高并发

###线程的概念:
1.线程 cpu执行或调度的基本单位;(进程里最小的一个执行单元)(程序里不同的执行路径)
2.进程 cpu资源分配的基本单位;

###线程的基本方法:
1.sleep:当前线程睡眠一段时间,将CPU时间片让给别的线程;
2.yield:让出当前cpu,返回就绪状态,进入等待队列,任然有可能继续被cpu选中继续执行;
3.join:T1中调用T2.join(),停止T1的执行,等待T2执行完毕,再继续执行T1;

###创建线程的五种方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import java.util.concurrent.*;

public class HowToCreateThread {

/**
* 继承Thread类
*/
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("Hello MyThread!");
}
}

/**
* 实现Runnable接口
*/
public static class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("Hello MyRunnable!");
}
}

/**
* 实现Callable接口,带有泛型返回值
*/
public static class MyCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("Hello MyCallable");
return "success";
}
}



public static void main(String[] args) throws ExecutionException, InterruptedException {
new MyThread().start();
new Thread(new MyRunnable()).start();
new Thread(() -> System.out.println("Hello MyLambda!")).start();

// 使用线程池
ExecutorService service = Executors.newCachedThreadPool();
service.execute(() -> {
System.out.println("Hello ThreadPool");
});

// 下面两种:Future Callable and FutureTask
// 使用线程池执行Callable实现类,返回Future对象,把返回值放在Future里
Future<String> future = service.submit(new MyCallable());
String str = future.get();
System.out.println(str);
service.shutdown();

// new Thread是不能直接传Runnable的,可以构建FutureTask对象
// FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable, Future<V>接口
// 也就是FutureTask既可以作为Runnable供Thread执行,又可以作为Future来接收参数
FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
Thread thread = new Thread(futureTask);
thread.start();
System.out.println(futureTask.get());

// 本质上其实都是new Thread对象执行start方法;
}
}

Java的6种线程状态:

1.NEW: 线程刚刚创建,还没有启动,调用start()方法之前;
2.RUNNABLE:可运行状态,由线程调度器安排执行;在此状态下有两种表现,READY(就绪状态)(当前没有分配到时间片,当线程被
调度器选中,CPU为其分配时间片进行线程任务执行),RUNNABLE(已经分配到时间片,正在执行线程任务,可以调用Thread.yield方法
进入READY状态),当发生了线程上下文切换,就有线程被挂起进入了READY(就绪状态);
3.WAITING:等待被执行唤醒;(注意:JUC的锁都是基于CAS来实现的,例如ReentrantLock().lock()方法进入的是waiting状态)
4.TIME WAITING:隔一段时间后自动唤醒;
5.BLOCKED:被阻塞,正在等待锁;(只有在Synchronized没有获取到锁资源的时候才会进入该状态)
6.TERMINATED:线程结束;(Thread.join()等待线程执行结束,线程状态进入terminated)

###Sleep,Wait,Notify
1.Sleep与Wait的区别,Sleep不释放锁;
2.Wait线程挂起,释放锁,且必须配合Synchronized使用;
3.notify唤醒其他线程,且必须配合Synchronized使用;

###.线程的打断(interrupt):

1
2
3
public void interrupt()            //t.interrupt() 打断t线程(设置t线程某给标志位f=true,并不是打断线程的运行)
public boolean isInterrupted() //t.isInterrupted() 查询打断标志位是否被设置(是不是曾经被打断过)
public static boolean interrupted()//Thread.interrupted() 查看“当前”线程是否被打断,如果被打断,恢复标志位

interrupt和sleep() wait() join()

sleep()方法在睡眠的时候,不到时间是没有办法叫醒的,这个时候可以用interrupt设置标志位,然后呢必须得catch InterruptedException来进行处理,决定继续睡或者是别的逻辑,(自动进行中断标志复位)

interrupt是否能中断正在竞争锁的线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

public class Interrupt_and_sync {

private static Object o = new Object();

public static void main(String[] args) {
Thread t1 = new Thread(()-> {
synchronized (o) {
SleepHelper.sleepSeconds(10);
}
});

t1.start();

SleepHelper.sleepSeconds(1);

Thread t2 = new Thread(()-> {
synchronized (o) {

}
System.out.println("t2 end!");
});

t2.start();

t2.interrupt();
}
}

interrupt()不能打断正在竞争锁的线程synchronized(),同时interrupt()也不能打断ReentrantLock的lock()方法;

如果想打断正在竞争锁的线程,使用ReentrantLock的lockInterruptibly()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import java.util.concurrent.locks.ReentrantLock;

/**
* interrupt与lockInterruptibly()
*/
public class Interrupt_and_lockInterruptibly {

private static ReentrantLock lock = new ReentrantLock();

public static void main(String[] args) {
Thread t1 = new Thread(()-> {
lock.lock();
try {
SleepHelper.sleepSeconds(10);
} finally {
lock.unlock();
}
System.out.println("t1 end!");
});

t1.start();

SleepHelper.sleepSeconds(1);


Thread t2 = new Thread(()-> {
System.out.println("t2 start!");
try {
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
System.out.println("t2 end!");
});

t2.start();

SleepHelper.sleepSeconds(1);

t2.interrupt();

}
}

由以上述结论其实可以看出:ReentrantLock()的lock()方法虽然也是使线程进入waiting状态,但是还是和Thread.wait()方法还是有所区别的,Thread.wait()进入的waiting状态可以被interrupt打断,但是lock()方法不行,想要被打断,需要
使用ReentrantLock()的lockInterruptibly();

###如何优雅的结束线程

  1. 自然结束(能自然结束就尽量自然结束);
  2. stop();(这三个方法已经废弃,主要是因为stop方法是直接结束线程,不能够执行善后操作,
    很容易造成数据不一致)
  3. suspend():让线程停止,但是线程任然持有锁资源,容易造成死锁; resume();恢复停止的线程继续执行
  4. volatile标志(是一个volatile属性,线程执行过程中使用while循环检查该属性,判断线程是否结束执行)
    1. 不适合某些场景(比如还没有同步的时候,线程做了阻塞操作,没有办法循环回去)
    2. 打断时间也不是特别精确,比如一个阻塞容器,容量为5的时候结束生产者,
      但是,由于volatile同步线程标志位的时间控制不是很精确,有可能生产者还继续生产一段儿时间
  5. interrupt() and isInterrupted(比较优雅)

###线程间数据交换的方式
1.使用Exchanger来实现两个线程交换数据,两个线程在同一个时间点阻塞着才能完成数据交换;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class MyTestExchanger {

static Exchanger<String> exchanger = new Exchanger<>();

public static void main(String[] args) {
new Thread(()->{
String s = "T1";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);

}, "t1").start();

new Thread(()->{
String s = "T2";
try {
s = exchanger.exchange(s);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " " + s);

}, "t2").start();
}
}
2.使用SynchronousQueue来实现数据传递,a线程向SynchronousQueue中put数据,b线程从Queue中取数据,如果没有线程取数据,
a线程会一直阻塞住;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class MySynchronusQueue { // 容量为0
public static void main(String[] args) throws InterruptedException {
BlockingQueue<String> strs = new SynchronousQueue<>();

new Thread(()->{
try {
System.out.println(strs.take());
} catch (Exception e) {
e.printStackTrace();
}
}).start();

new Thread(()->{
try {
strs.put("aaa");
} catch (Exception e) {
e.printStackTrace();
}
}).start();

// strs.put("aaa"); //阻塞等待其他线程取数据
// strs.put("bbb");
// strs.add("aaa");
System.out.println(strs.size());
}
}