java多线程

进程与线程

进程通常指的是一个软件列如QQ.exe等。一个进程里面有多个线程,至少包含一个线程,java中默认有两个线程(main,GC)

java中开启线程三种方式:Thread,Runnable,callable

java不可以开启线程

通过group.add()把当前线程添加到当前线程组,调用本地方法start0(),调用的是底层c++,因为java无法直接操作硬件,java是运行在虚拟机上的。所以需要通过c++完成。

    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }

    private native void start0();

并发编程:并发与并行

并发编程的本质:充分利用CPU的资源

并发:多个线程操作同一个资源

        一核CPU,模拟多条线程

并线:多个一块行动

       多核CPU,模拟多条线程可以同时执行

public static void main(String[] args) {
        //获得cpu核数
        System.out.println(Runtime.getRuntime().availableProcessors());
    }

线程的状态

//线程新生
NEW,
//运行
RUNNABLE,
//阻塞     
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;

wait/sleep的区别

  • 类不同
    • wait是Object类中的,sleep是Threa中的
  • 锁的释放
    • wait不释放锁,sleep不释放锁
  • 使用范围不同
    • wait必须在同步代码块中才能使用,sleep不做限制
  • wait/sleep都需要捕获异常InterruptedException中断异常

Lock锁

lock的使用

 Lock l = ...;
 l.lock();
 try {
   // access the resource protected by this lock
 } finally {
   l.unlock();
 }

lock接口的实现类

ReentrantLock:可重入锁

ReentrantReadWriteLock.ReadLock:读锁

ReentrantReadWriteLock.WriteLock:写锁

ReentrantLock的构造方法:

默认是非公平锁

//非公平锁
public ReentrantLock() {
        sync = new NonfairSync();
}
//公平锁
public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
}

synchronized和lock的区别

  1. synchronized是java中的关键字,lock是一个java类
  2. synchronized无法判断获得锁的状态,lock可以判断是否获得锁
  3. synchronized会自动释放锁,lock需要手动释放锁,不释放锁会死锁
  4. 使用synchronized 线程1获得了锁后面的线程将会等待,线程1要是阻塞后面的线程就会一直等待。lock锁使用tryLock()就不会等待
  5. synchronized可重入锁,不可以中断,非公平锁,lock可重入锁,可以中断,可以字节设置非公平锁和公平锁
  6. synchronized适合少量的代码同步问题,lock适合锁大量的同步代码

生成者与消费者

synchronized:

public class Demo01 {
    public static void main(String[] args) {
        Data data  = new Data();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.incte();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"A").start();
        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                try {
                    data.derce();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"B").start();
    }
}
class Data{
    private Integer num = 0;
    public synchronized void incte() throws InterruptedException{
        while(num != 0 ){
            this.wait();
        }
        num ++;
        System.out.println(Thread.currentThread().getName()+" -->"+ num);
        this.notifyAll();
    }
    public synchronized void derce() throws InterruptedException{
        while(num == 0 ){
            this.wait();
        }
        num --;
        System.out.println(Thread.currentThread().getName()+" -->"+ num);
        this.notifyAll();
    }
}

JUC:

通过lock找到Condition,

Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。

代码演示

public class Demo01 {
    public static void main(String[] args) {
        Data data  = new Data();
        new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.incte();},"C").start();
        new Thread(()->{for (int i = 0; i < 10; i++)data.derce();},"D").start();
    }
}
class Data{
    private Integer num = 0;
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    public  void incte(){
        lock.lock();
        try {
            while(num != 0 ){
                condition.await();
            }
            num ++;
            System.out.println(Thread.currentThread().getName()+" -->"+ num);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
           lock.unlock();
        }
    }
    public void derce(){
        lock.lock();
        try {
            while(num == 0 ){
                condition.await();
            }
            num --;
            ;System.out.println(Thread.currentThread().getName()+" -->"+ num);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

Condition实现精准唤醒

public class Demo02 {
    public static void main(String[] args) {
        DataTwo dataTwo = new DataTwo();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinA();},"A").start();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinB();},"B").start();
        new Thread(()->{for (int i = 0; i < 10; i++)dataTwo.prinC();},"C").start();
    }
}
class DataTwo{
    Lock l = new ReentrantLock();
    Condition condition = l.newCondition();
    private int num = 1;
    public void prinA(){
        l.lock();
        try {
            while (num != 1){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num=2;
            condition.signal();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            l.unlock();
        }
    }
    public void prinB(){
        l.lock();
        try {
            while (num != 2){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num=3;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
    public void prinC(){
        l.lock();
        try {
            while(num != 3){
                condition.await();
            }
            System.out.println(Thread.currentThread().getName()+"正在执行");
            num = 1;
            condition.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            l.unlock();
        }
    }
}

CopyOnWriteArrayList

在并发下ArrayList是线程不安全的,会出现异常java.util.ConcurrentModificationException(并发修改异常)

public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,4));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }

那么怎么解决这个并发修改异常问题呢?

方法一

list集合中Vector集合是线程安全的所以可以使用Vector解决

List<String> list = new Vector<String>();

方法二

可以使用Collections中的synchronizedList()将ArrayLIst改变为线程安全的

List<String> list = Collections.synchronizedList(new ArrayList<>());

  方法三

使用CopyOnWriteArrayList

List<String> list = new CopyOnWriteArrayList<>();

使用Vector本事就是线程安全的在添加数据的时候就是同步的,

使用Collections.synchronizedList()就是将传过去的list添加同步锁

使用CopyOnWriteArrayList读写分离,在写入数据的时候复制一份并且在他的底层是使用lock锁

 推荐使用CopyOnWriteArrayList因为使用synchronized效率会比较低但。

如果需要将map改变为线程安全需要使用到ConcurrentHash Map()

Map<String,Object> map = new ConcurrentHashMap<String,Object>();

Collable

Callable接口类似于Runnable,因为它们都是为其实例可能由另一个线程执行的类设计的。 然而,A Runnable不返回结果,也不能抛出被检查的异常。

Callable与Runnable不同于,Callable可以抛出异常,使用的方法不同Callable使用call方法,Callable可以有返回值

ReadWriteLock(读写锁)

java并发包中ReadWriteLock是一个接口,主要有两个方法

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}

 实现类:ReadWriteLockView,ReentrantReadWriteLock

readLock是读锁,writeLock是写锁,使用ReadWriteLock可以更加细粒度控制

public class ReadWriteLockTest {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    public static void main(String[] args) {
        Date date = new Date();
        Test test = new Test();
        new Thread(()->test.start(true,sdf.format(date)),"write-----1").start();//读
        new Thread(()->test.start(true,sdf.format(date)),"write-----2").start();//读
        new Thread(()->test.start(false,sdf.format(date)),"read-----2").start();//写
        new Thread(()->test.start(false,sdf.format(date)),"read-----1").start();//写
        new Thread(()->test.start(false,sdf.format(date)),"read-----3").start();//写
    }

}
class Test{
        ReadWriteLock rel = new ReentrantReadWriteLock();//构建ReentrantReadWriteLock使锁细腻话
        //创建读锁
        Lock readLock = rel.readLock();
        //创建写锁
        Lock writeLock = rel.writeLock();
    public <T> void start(boolean flse, T t){
        if(flse)read(t);else write(t);
    }
    //读方法
    public <T> void read(T t){
        readLock.lock();//使用读锁
        try {
            System.out.println("线程"+Thread.currentThread().getName()+"正在读,当前时间是:"+t);
            TimeUnit.SECONDS.sleep(2);//停滞两秒
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readLock.unlock();//释放读锁
        }
    }
    //写方法
    public <T> void write(T t){
        writeLock.lock();//使用读锁
        try {
            System.out.println("线程"+Thread.currentThread().getName()+"正在写,当前时间是:"+t);
            TimeUnit.SECONDS.sleep(2);//停滞两秒
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            writeLock.unlock();//释放读锁
        }
    }
}

线程池

使用线程池的优点:

  • 降低资源的消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高影响速度,通过重复利用已创建的线程降低线程创建和销毁造成的消耗
  • 提高线程的可管理性,

线程池的使用

JDK 5.0起提供了线程池相关API:ExecutorService 和Executors

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor

创建四种方式:

  • newCachedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

public static void main(String[] args) {
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName());
                }
            });
        }
    }
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
public static void main(String[] args) {
    //1.创建可固定长度的线程池
    ExecutorService newExecutorService = Executors.newFixedThreadPool(3);
    //创建了10个线程
    for (int i = 0; i < 10; i++) {
        int temp = i;
            newExecutorService.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("threadName;"+Thread.currentThread().getName()+",i"+temp);
                }
        });
    }
}
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。延迟执行示例
 public static void main(String[] args) {
        //1.创建可定时线程池
        ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(5);
        for (int i = 0; i < 10; i++) {
            final int temp = i;
            newScheduledThreadPool.schedule(new Runnable() {
                public void run() {
                    System.out.println("i:" + temp);
                }
            }, 3, TimeUnit.SECONDS);//时间数值,时间单位
        }

    }
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行需要关闭
public static void main(String[] args) {
        //1.创建单线程
        ExecutorService newSingleThreadExecutor = Executors.newSingleThreadExecutor();
        for (int i = 0; i < 10; i++) {
            final int index = i;
            newSingleThreadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println("index:" + index);
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        newSingleThreadExecutor.shutdown();
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值