- 1.什么是多线程
- 2.如何创建多线程
- 3.多线程问题
- 4.Volatile
- 5.CountDownLatch
- 6.CycliBarrier
- 7.Atomicxxx类
- 8.ReentranLock
- 9.CyclicBarrier
1.什么是多线程
2.如何创建多线程
三种方法创建多线程
1.一个类继承Thread 重写run方法
2.一个类实现Runnable接口
3.使用线程池
/**
* @author zhoubing
* @date 2020-04-27 22:33
*/
public class TestThread extends Thread {
@Override
public void run() {
System.out.println("hello world");
}
}
/**
* @author zhoubing
* @date 2020-04-27 22:34
*/
public class TestThread2 implements Runnable {
@Override
public void run() {
System.out.println("hello runnable");
}
}
3.多线程问题
多线程同时访问一个资源的时候,容易造成同步问题
采用的是synchronized方法同步
synchronized可以放在三个位置
1.静态方法上
锁的是字节码文件
2.成员方法上
锁的是对象
3.synchronized(object)
锁的是对象
在上锁的方法内部,可以调用没有上锁的方法,也可以调用上了同一把锁的方法(这就是可重入锁)
一个同步方法可以调用另外一个同步方法,一个线程已经拥有某个对象的锁,再次申请的时候仍然会得到该对象的锁.
也就是说synchronized获得的锁是可重入的
这里是继承中有可能发生的情形,子类调用父类的同步方法(也可以调用成功)
synchronized的底层实现
JDK早期的 重量级 - OS
后来的改进
锁升级的概念:
我就是厕所所长 (一 二)
sync (Object)
markword 记录这个线程ID (偏向锁)
如果线程争用:升级为 自旋锁
10次以后,
升级为重量级锁 - OS
执行时间短(加锁代码),线程数少,用自旋
执行时间长,线程数多,用系统锁
4.Volatile
volatile 关键字,使一个变量在多个线程间可见
A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道
使用volatile关键字,会让所有线程都会读到变量的修改值
在下面的代码中,running是存在于堆内存的t对象中
当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去
读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行
使用volatile,将会强制所有线程都去堆内存中读取running的值
可以阅读这篇文章进行更深入的理解
http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html
volatile并不能保证多个线程共同修改running变量时所带来的不一致问题,也就是说volatile不能替代synchronized
禁止指令重排序
volatile 引用类型(包括数组)只能保证引用本身的可见性,不能保证内部字段的可见性
5.CountDownLatch
等待多个线程完成,再往下走(到了五个之后 再往下走)
{
CountDownLatch countDownLatch = new CountDownLatch(5);
for (int i = 0; i < 10; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName());
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("passed...");
}
6.CycliBarrier
{
CyclicBarrier cyclicBarrier = new CyclicBarrier(5, ()->{
System.out.println("人满...发车...");
});
for (int i = 0; i < 20; i++) {
new Thread(()->{
System.out.println(Thread.currentThread().getName());
try {
//TimeUnit.SECONDS.sleep(1);
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}
}
7.Atomicxxx类
Atomic类主要用于高并发的时候计数等
AtomicInteger atomicInteger = new AtomicInteger(0);
就把它当做一个安全的容器使用即可
8.ReentranLock
可重入锁
Lock lock = new ReentrantLock();
int count=0;
void method(){
lock.lock();
try {
for (int i = 0; i < 100000; i++) {
count++;
}
} finally {
lock.unlock();
}
}
{
在method1中调用method2(有锁 也可以获取到锁)
void method1(){
lock.lock();
System.out.println("method1 start......");
try {
method2();
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println("method1 end......");
}
void method2(){
lock.lock();
System.out.println("method2 start......");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
System.out.println("method2 end......");
}
}
使用tryLock进行尝试锁定,不管锁定与否,方法都将继续执行
可以根据tryLock的返回值来判定是否锁定
也可以指定tryLock的时间,由于tryLock(time)抛出异常,所以要注意unclock的处理,必须放到finally中
9.CyclicBarrier
到了一个数目,就调用设置好的一个方法
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
System.out.println("人满 发车...");
});
for (int i = 0; i < 30; i++) {
new Thread(()->{
try {
TimeUnit.SECONDS.sleep(1);
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
}