01、进程、线程的概念
- 进程是程序运行的最小单位
- 线程由多个进程组成
- 线程是一条程序的执行路径
02、启动线程的三种方式
- 继承Thread类
- 实现Runable接口
- 通过线程池获取线程Executors.newCachedThread()
03、线程的基本方法
- Thread.sleep(500) 睡眠500ms,不会释放lock,不依赖于同步器synchronized,sleep不需要被唤醒
- Object.wait() 释放锁,而且会加入等待队列,需要依赖synchronized关键字,需要被唤醒(不指定时间需要被别人中断)
- Object.notify() 唤醒等待的某一个线程,但不释放锁 Object.notifyAll() 唤醒等待的所有线程
- Thread.yield() 使该线程中断一次,并参与下次cpu竞争
- Thread.join() t1.join(),t1线程运行完之后才继续运行当前线程
- Thread.interrupt() 提醒是否中断该线程,会抛出InterruptException(),可以捕获该异常并决定如何处理
- 补充
04、线程的状态转换(图中Object.join()应该为Thread.join())
05、synchronized锁的知识点
- 同线程可重入
- 对象锁声明对象时推荐用final修饰,防止指向对象的引用改变
- 锁升级:——偏向锁——》自旋锁——》重量级锁(OS);线程少操作时间时间短使用自旋锁,反之使用重量级锁。
- 锁的优化:1、锁细化,只锁住关键代码。2、锁粗化,小锁太多,直接加一把大锁
06、volatile关键字(实际开发中很少使用)
- 保证线程可见性,不能保证原子性(线程之间的变量改变相互可见)
- 禁止指令重排序
- 单列模式双重判断时如果有自增操作需要加volatile
07、CAS (无锁优化 自旋)
- compare and set 比较并赋值 cas(V,Excepted,NewValue)
cas(V,Excepted,NewValue) if V==E V=new otherwise try again or fail
- Atomic开头的类使用了CAS,列如AtomicInteger的i.incrementAndGet(); //count1++ 操作就是原子的,使用了unsafe累支持
- CAS是cpu原语支持,是原子性的,不会被打断
- ABA问题(在CAS过程中A变为B再变为A):加版本号解决
08、多线程自增解决方案(三种)
- synchronized
- AtomicXXX
- LongAdder (使用分段锁线程数多的时候快)
09、其他类型的锁(新锁用到了cas)
- ReentrantLock()重入锁:可以替代 synchronized,相比于synchronized 1、支持尝试锁定,不管锁定与否,方法都将继续执行:lock.tryLock(5, TimeUnit.SECONDS); 2、可以打断:lock.lockInterruptibly(); 3、支持实现公平锁:ReentrantLock lock=new ReentrantLock(true); //参数为true表示为公平锁
Lock lock = new ReentrantLock(); public void m2() { try { //加锁 lock.lock(); System.out.println("m2 ..."); } finally { //注意要在finally中主动释放锁 lock.unlock(); } }
- CyclicBarrier(栅栏):当含barrier.await();的线程超过20时触发后面的线程
public static void main(String[] args) {
//(栅栏)当含barrier.await();的线程超过20时触发后面的线程
CyclicBarrier barrier = new CyclicBarrier(20, () -> System.out.println("满人"));
for(int i=0; i<100; i++) {
new Thread(()->{
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
System.out.println("over!");
}
- phaser(多层栅栏):CyclicBarrier的升级实现
- ReadWriteLock读写锁:加锁后允许其他线程读操作,不允许写操作
static Lock lock = new ReentrantLock();
private static int value;
//读写锁
static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//读锁相当于共享锁
static Lock readLock = readWriteLock.readLock();
//写锁相当于排它锁
static Lock writeLock = readWriteLock.writeLock();
public static void read(Lock lock) {
try {
lock.lock();
Thread.sleep(1000);
System.out.println("read over!");
//模拟读取操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void write(Lock lock, int v) {
try {
lock.lock();
Thread.sleep(1000);
value = v;
System.out.println("write over!");
//模拟写操作
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
//用互斥锁需要20S执行完
//Runnable readR = ()-> read(lock);
//用读写锁只需要2S左右
Runnable readR = ()-> read(readLock);
//Runnable writeR = ()->write(lock, new Random().nextInt());
Runnable writeR = ()->write(writeLock, new Random().nextInt());
for(int i=0; i<18; i++) new Thread(readR).start();
for(int i=0; i<2; i++) new Thread(writeR).start();
}
-
Semaphore用于限流。允许N个线程同时运行
//Semaphore用于限流。允许N个线程同时运行
public class T11_TestSemaphore {
public static void main(String[] args) {
//Semaphore s = new Semaphore(2);
Semaphore s = new Semaphore(2, true);
//允许一个线程同时执行
//Semaphore s = new Semaphore(1);
new Thread(()->{
try {
//s-1>0则继续进行
s.acquire();
System.out.println("T1 running...");
Thread.sleep(200);
System.out.println("T1 running...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//s+=1
s.release();
}
}).start();
new Thread(()->{
try {
s.acquire();
System.out.println("T2 running...");
Thread.sleep(200);
System.out.println("T2 running...");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
- Exchanger用于线程之间的数据通信,两个线程交换某个值
//Exchanger用于线程之间的数据通信,两个线程交换某个值
public class T12_TestExchanger {
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();
}
}
-
LockSupport:用于停止某个线程和叫醒某个线程,跟CountDownLatch用法类似
public static void main(String[] args) {
Thread t = new Thread(()->{
for (int i = 0; i < 10; i++) {
System.out.println(i);
if(i == 5) {
//使该线程停止运行
LockSupport.park();
}
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//线程继续运行 unpark可以先于park调用,两者各调用一次,线程不会停止。
// unpark调用一次,park调用两次,线程会停止
LockSupport.unpark(t);
/*try {
TimeUnit.SECONDS.sleep(8);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("after 8 senconds!");
LockSupport.unpark(t);*/
}
-
CountDownLatch:(门栓)相当于一个线程安全的计数器。用于控制线程继续向下执行。只有当latch==0时线程才继续向下执行
private static void usingCountDownLatch() { Thread[] threads = new Thread[100]; //(门栓)相当于一个线程安全的计数器。用于控制线程继续向下执行。只有当latch==0时线程才继续向下执行 CountDownLatch latch = new CountDownLatch(threads.length); for(int i=0; i<threads.length; i++) { threads[i] = new Thread(()->{ int result = 0; for(int j=0; j<10000; j++) result += j; //计数-1 latch.countDown(); }); } for (int i = 0; i < threads.length; i++) { threads[i].start(); } try { //latch==0则继续向下执行 latch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("end latch"); }