【JUC】并发编程学习笔记(一)

一、基础知识

1.1、JUC

JUC是java.util.concurrent在并发编程中使用的工具包

1.2、进程与线程

进程(Process ) 是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。程序是指令、数据及其组织形式的描述,进程是程序的实体。

线程( thread ) 是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。v

1.3、线程的状态

  1. NEW,(新建)
  2. RUNNABLE,(准备就绪)
  3. BLOCKED,(阻塞)
  4. WAITING,(不见不散)
  5. TIMED_WAITING,(过时不候)
  6. TERMINATED;(终结)

1.4、wait/sleep的区别

( 1 ) sleep是Thread的静态方法,wait是 Object的方法,任何对象实例都能调用。

( 2 ) sleep不会释放锁,它也不需要占用锁。wait 会释放锁,但调用它的前提是当前线程占有锁(即代码要在synchronized中)。

(3)它们都可以被interrupted方法中断。

1.5、并发与并行。

并发:同一时刻多个线程在访问同一个资源,多个线程对一个点·

例子︰春运抢票 电商秒杀 .…

并行:多项工作一起执行,之后再汇总

例子︰泡方便面,电水壶烧水,一边撕调料倒入桶中;

1.6、管程

管程 是一种同步机制,保证同一个时间,只有一个线程访问被保护数据或者代码

jvm同步基于进入和退出,使用管程对象实现的

1.7、用户线程与守护线程

用户线程∶自定义线程(主线程结束了,用户线程还在运行, jvm存活)

守护线程:比如垃圾回收(没有用户线程了,都是守护线程,jvm结束)

public class Main01 {
    public static void main(String[] args) {
        Thread aa=new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"::"+Thread.currentThread().isDaemon());
            while (true){}
        },"aa");

		//aa设置成守护线程
		//aa.setDaemon(true);
        aa.start();
        System.out.println(Thread.currentThread().getName()+"over");
    }
}

虽然main主方法结束了 但自定义线程aa还在死循环中 那么程序还在运行中( jvm存活)
在这里插入图片描述
将aa设置成守护线程 主线程结束 没有用户线程 那么程序停止(jvm结束)
在这里插入图片描述

二、Lock锁

2.1、synchronized

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

  1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号}括起来的代码,作用的对象是调用这个代码块的对象;·
  2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用
    的对象是调用这个方法的对象;
  3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的
    所有对象;
  4. 修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主
    的对象是这个类的所有对象。

2.1.1、案例

3个售票员 卖出30张票

class Ticket{
    private int num =30;
    synchronized void sale(){
       if(num>0){
           System.out.println(Thread.currentThread().getName()+"卖出,"+"剩余:"+(--num ));
       }
    }
}
public class SaleTicketBySynchronized  {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"aa").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"bb").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 40; i++) {
                    ticket.sale();
                }
            }
        },"cc").start();
    }

}

2.2、Lock

为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器。

Lock 实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。

Lock和synchronized有一点非常大的不同,采用synchronized不需要用户去手动释放锁,当synchronized方法或者synchronized 代码块执行完或者出现异常之后系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。

2.2.1、案例

3个售票员 卖出30张票

import java.util.concurrent.locks.ReentrantLock;

class Ticket{
    private int num =30;

    private final ReentrantLock lock=new ReentrantLock();

    public void sale(){
        //上锁
        lock.lock();
        try {
            if(num>0){
                System.out.println(Thread.currentThread().getName()+"卖出,"+"剩余:"+(--num));
            }
        }finally {
            //解锁
            lock.unlock();
        }
    }
}

public class SaleTicketByLock {
    public static void main(String[] args) {
        Ticket ticket=new Ticket();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"aa").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"bb").start();

        new Thread(()-> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        },"cc").start();
    }
}

三、线程间的通信

3.1、案例synchronized 方式

有一个数开始为0 两个线程 不断+1-1

class Share{
    private int number=0;
    //+1
    public synchronized void incr() throws InterruptedException {
        if(number!=0){
            this.wait();
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他线程
        this.notifyAll();
    }
    //-1
    public synchronized void decr() throws InterruptedException {
        if(number!=1){
            this.wait();
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"::"+number);
        //通知其他线程
        this.notifyAll();
    }
}
public class ThreadDemo01 {
    public static void main(String[] args) {
        Share share=new Share();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//+1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"aa").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"bb").start();
    }
}

3.2、虚假唤醒

在案例中增加两个新线程 也是一加一减 cc dd
此时数据就会混乱
在这里插入图片描述

原因 wait执行时 被唤醒会继续执行(在哪里睡,就在哪里醒)恰巧又是两个加的线程抢到执行权 此时数据就会混乱

解决 将if 替换为while 不管怎么被唤醒都要执行判断

3.3、案例lock方式

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Share{
    private int number=0;
    private Lock lock=new ReentrantLock();
    private Condition condition=lock.newCondition();
    //+1
    public void incr() throws InterruptedException {
        lock.lock();
        try {
            while (number!=0){
                condition.await();
            }
            number++;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            //通知其他线程
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
    //-1
    public void decr() throws InterruptedException {
        lock.lock();
        try {
            while (number!=1){
                condition.await();
            }
            number--;
            System.out.println(Thread.currentThread().getName()+"::"+number);
            //通知其他线程
            condition.signalAll();
        }finally {
            lock.unlock();
        }
    }
}

public class ThreadDemo02 {
    public static void main(String[] args) {
        Share share=new Share();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"aa").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"bb").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.incr();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"cc").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    share.decr();//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"dd").start();
    }
}

3.4、线程间定制化通信

启动三个线程,按照如下要求:
AA打印5次后,BB打印10次后,CC打印15次 循环10次

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class ShareResource{
    //1->aa 2->bb 3->bb
    private int flag=1;
    private Lock lock=new ReentrantLock();
    private Condition c1=lock.newCondition();
    private Condition c2=lock.newCondition();
    private Condition c3=lock.newCondition();

    public void print5(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag!=1){
                c1.await();
            }
            for (int i = 0; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+":: 轮数:"+loop);
            }
            flag=2;
            //通知线程
            c2.signal();
        }finally {
            lock.unlock();
        }
    }

    public void print10(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag!=2){
                c2.await();
            }
            for (int i = 0; i < 10; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+":: 轮数:"+loop);
            }
            flag=3;
            //通知线程
            c3.signal();
        }finally {
            lock.unlock();
        }
    }

    public void print15(int loop) throws InterruptedException {
        lock.lock();
        try {
            while (flag!=3){
                c3.await();
            }
            for (int i = 0; i < 15; i++) {
                System.out.println(Thread.currentThread().getName()+"::"+i+":: 轮数:"+loop);
            }
            flag=1;
            //通知线程
            c1.signal();
        }finally {
            lock.unlock();
        }
    }
}
public class ThreadDemo03 {
    public static void main(String[] args) {
        ShareResource shareResource=new ShareResource();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print5(i);//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"aa").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print10(i);//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"bb").start();

        new Thread(()-> {
            for (int i = 0; i < 10; i++) {
                try {
                    shareResource.print15(i);//-1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"cc").start();
    }
}

四、集合的线程安全

4.1、ArrayList线程不安全

4.1.1、例子

import java.util.ArrayList;
import java.util.UUID;

public class ThreadDemo04 {
    public static void main(String[] args) {

        ArrayList<String> arrayList=new ArrayList();

        for (int i = 1; i < 10; i++) {
            new Thread(()-> {
                arrayList.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

4.1.2、解决不安全方式一:Vector

Vector源码在添加时使用了synchronized 关键字 因此效率会低很多

import java.util.ArrayList;
import java.util.UUID;
import java.util.Vector;

public class ThreadDemo04 {
    public static void main(String[] args) {
        //ArrayList<String> arrayList=new ArrayList();
        Vector<String> arrayList=new Vector<>();
        for (int i = 1; i < 10; i++) {
            new Thread(()-> {
                arrayList.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

4.1.3、解决不安全方式二:Collections

在定义的时候使用了synchronized 关键字 因此效率会低很多

import java.util.*;

public class ThreadDemo04 {
    public static void main(String[] args) {

        //ArrayList<String> arrayList=new ArrayList();
        //Vector<String> arrayList=new Vector<>();
        List<String> arrayList=Collections.synchronizedList(new ArrayList());
        for (int i = 1; i < 10; i++) {
            new Thread(()-> {
                arrayList.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

4.1.3、解决不安全方式三:CopyOnWriteArrayList

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

public class ThreadDemo04 {
    public static void main(String[] args) {

        //ArrayList<String> arrayList=new ArrayList();
        //Vector<String> arrayList=new Vector<>();
        //List<String> arrayList=Collections.synchronizedList(new ArrayList());
        List<String> arrayList = new CopyOnWriteArrayList();
        for (int i = 1; i < 10; i++) {
            new Thread(()-> {
                arrayList.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(arrayList);
            },String.valueOf(i)).start();
        }
    }
}

4.2、HashSet线程不安全

4.2.1、案例

import java.util.*;

public class ThreadDemo04 {
    public static void main(String[] args) {
        HashSet<String> set=new HashSet<>();
        for (int i = 1; i < 50; i++) {
            new Thread(()-> {
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

4.2.2、解决方式:CopyOnWriteArraySet

import java.util.*;
import java.util.concurrent.CopyOnWriteArraySet;

public class ThreadDemo04 {
    public static void main(String[] args) {
        Set<String> set=new CopyOnWriteArraySet<>();
        for (int i = 1; i < 50; i++) {
            new Thread(()-> {
                set.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

4.3、HashMap线程不安全

4.3.1、案例

import java.util.*;

public class ThreadDemo04 {
    public static void main(String[] args) {
        Map<String,String> set=new HashMap<>();
        for (int i = 1; i < 50; i++) {
            new Thread(()-> {
                set.put(UUID.randomUUID().toString().substring(0,8),"nacl");
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}

4.3.2、解决方式:ConcurrentHashMap

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class ThreadDemo04 {
    public static void main(String[] args) {
        Map<String,String> set=new ConcurrentHashMap<>();
        for (int i = 1; i < 50; i++) {
            new Thread(()-> {
                set.put(UUID.randomUUID().toString().substring(0,8),"nacl");
                System.out.println(set);
            },String.valueOf(i)).start();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值