1. 零零碎碎的知识开篇
Java程序默认有两个线程,一个main一个GC(垃圾回收的守护线程)
Java能自己开线程吗???
其实是不能的,java开启线程调用的native start0()方法,由C++开线程。Java程序是运行在JVM上的,不能直接操作硬件。
2. 线程的六种状态
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
3. wait() 和 sleep()的区别
- 原理不同。wait()是Object类的方法,用于线程通信。sleep()是Thread的静态方法,用于控制流程,让线程睡觉,把执行的机会让给其他线程。
- 使用的区域不同。wait()用于同步方法或同步代码块儿中。sleep()随处都可睡。
- 对锁的处理也不同。wait()的时候就会把锁给释放了,而sleep()则会和锁一起睡觉,不会释放锁。
4. synchronized 和 Lock 的区别
- synchronized 是内置的关键字;Lock是java的接口
- synchronized 无法判断锁的状态;Lock可以判断锁的状态
- synchronized 会自动释放锁;Lock需要手动释放锁
- synchronized 会当线程1获得锁并且阻塞的话,线程2会一直等待;Lock不一定会一直等待下去,可以尝试获得锁,获取不到,就不再等待
- synchronized 可重入,不可中断,非公平;Lock 可重入,可判断,可公平
- synchronized 适合少量代码的同步问题;Lock适合大量同步代码的问题
记忆过程:根据Lock的使用过程,先创建锁(可以想到1,5),加锁释放锁(想到2,3),46点再使劲记一下吧。。。
5. 生产者消费者问题
这个知识点主要需要理解,线程之间的通信,每个线程都有自己的等待条件,执行业务,再通知其他线程,我愿称之,《等待,业务,唤醒》
synchronized 用的是 wait() 和notifyall()
Lock 用的是 Condition 下的 await()和signalAll() (注:signal() 也有妙用,在多个Condition时,可用于定向的唤醒)
重要的要记住:在对等待条件进行判断的时候,用的时while()而不是if()
官方文档中说,用while()来防止虚假唤醒问题,因为while()能一直检查条件,而if()只检查一次
public class ProducerAndConsumer01 {
//在两个线程下,正常用if判断等待的条件是可以做到的
//但是若增加到多个线程,则会出现异常情况。
//此时if应该改为while,while是多次判断,if是单次判断,所以if会有虚假唤醒的问题
//就是用if判断的话,唤醒后线程会从wait之后的代码开始运行,
//但是不会重新判断if条件,直接继续运行if代码块之后的代码,
//而如果使用while的话,也会从wait之后的代码运行,
//但是唤醒后会重新判断循环条件,如果不成立再执行while代码块之后的代码块,成立的话继续wait。
//生产者和消费者问题 等待,业务,唤醒
public static void main(String[] args) {
Item item = new Item();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
item.increase();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
item.decrease();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
item.increase();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C").start();
new Thread(()->{
try {
for (int i = 0; i < 10; i++) {
item.decrease();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D").start();
}
}
class Item{
private int num = 0;
public synchronized void increase() throws InterruptedException {
while (num != 0){
this.wait();
}
num++;
System.out.println(Thread.currentThread().getName() + "->" + num);
this.notifyAll();
}
public synchronized void decrease() throws InterruptedException {
while(num == 0){
this.wait();
}
num--;
System.out.println(Thread.currentThread().getName() + "->" + num);
this.notifyAll();
}
}
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerAndConsumer03 {
public static void main(String[] args) {
Data03 data = new Data03();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
}).start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
}).start();
}
}
class Data03{
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
private int num = 0;
public void printA(){
lock.lock();
try {
while (num != 0){
//等待
condition1.await();
}
//业务
System.out.println("大家好,正在打印AAA");
num++;
condition2.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num != 1){
//等待
condition2.await();
}
//处理业务
System.out.println("大噶好,正在打印BBB");
num++;
//唤醒
condition3.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num != 2){
//等待
condition3.await();
}
//处理业务
System.out.println("雷猴啊,正在打印CCC");
num = 0;
//唤醒
condition1.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
6. 8锁问题
- 针对synchronized,不同的对象,锁不同,执行同步方法时,不互相干扰。
- 针对static synchronized,锁住的是类,区别于非静态的锁,这两个锁互相不干扰
- 普通方法不涉及锁的问题,执行不受影响
7. 线程不安全的集合
- List接口下的集合都不安全,解决方法如下
List<String> list = Collections.synchronizedList(new ArrayList<String>());
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
//还有一个过时的Vector也可以解决,Vector add方法是利用的synchronized同步的
- 解决Set的线程安全问题
Set<String> set = Collections.synchronizedSet(new HashSet<String>());
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
HashSet的底层是HashMap,调用的add方法是HashMap的put方法,HashSet利用的是HashMap中key不能重复的性质
- 解决Map的线程不安全问题
Map<String,String> map = Collections.synchronizedMap(new HashMap<>());
ConcurrentHashMap<String,String> map = new ConcurrentHashMap<>();
CopyOnWrite 是计算机程序设计的一种优化策略
在写入时复制,避免重复
8. 实现Callable接口执行多线程方法
- Runnable和Callable的区别
- 它们都是函数式接口
- Callable有返回值,Runnable没有
- Callable方法是call,Runable是run
泛型的类型就是返回值类型
FatureTask实现了Runnable接口,而FatureTask的构造器中需要传入Callable的对象,这样,将FatureTask传入Thread时,便将Callable和Thread勾搭了起来
利用FatureTask的get方法获取返回值,可能发生阻塞,而且还会被缓存
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Call {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask).start();
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<String>{
@Override
public String call() throws Exception {
System.out.println("亲爱哒,这是call()方法哦");
return "好哒,俺知道了";
}
}
9. 常用的辅助类
public class CountDownLatch {
public static void main(String[] args) throws InterruptedException {
java.util.concurrent.CountDownLatch countDownLatch = new java.util.concurrent.CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(()->{
//相当于是 -1 操作
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + " :我减!");
}).start();
}
//这里是在计数器减到0的时候,才能唤醒,否则一直等着
countDownLatch.await();
System.out.println("啦啦啦,看来这个CountDownLatch,你了解一点儿了");
}
}
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Cyclicbarrier {
public static void main(String[] args) {
//构造函数中,有7,到7召唤神龙
//7的意思是,你创建了7个线程才能执行
//到7的时候会自动唤醒 cyclicBarrier 的 await
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("召唤神龙成功");
});
for (int i = 0; i < 7; i++) {
int temp = i;
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "拿到了第" + temp + "颗龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
}
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class SemaphorE {
public static void main(String[] args) {
//3个信号量,require就减一个,release就加一个
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 9; i++) {
new Thread(()->{
try {
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "占用了信号");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
System.out.println(Thread.currentThread().getName() + "释放了信号");
}
}).start();
}
}
}