Zhou Study hard, improve every day.

多线程相关

2020-04-27
本文 3262 字,阅读全文约需 10 分钟

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获得的锁是可重入的

这里是继承中有可能发生的情形子类调用父类的同步方法(也可以调用成功)
    

1.加锁粒度不要太大 2.锁对象不要是字符串等常量
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();
        }

Similar Posts

上一篇 shell脚本

Comments