Java多线程01

Java 多线程01

博客主要内容全部来自以下链接,写了一些个人理解而已

https://blog.csdn.net/qq_41617848/article/details/107619810?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163316365616780264089131%2522%252C%2522scm%2522%253A%252220140713.130102334…%2522%257D&request_id=163316365616780264089131&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2alltop_positive~default-1-107619810.first_rank_v2_pc_rank_v29&utm_term=juc&spm=1018.2226.3001.4187

01.多线程“卖票”

50张票,作为资源,让多个窗口(线程)进行贩卖

class Ticket
{
    private int number=50;
    public synchronized void sale()//被synchronized关键词修饰,表示线程共享且加锁
    {
        if(number>0)//当还有票,就打印出票的数量和卖票线程的名字
        {
            System.out.println(Thread.currentThread().getName()+"卖出了第"+number+"张票,剩余"+
                    number+"张票");
            number--;
        }
    }
}

开启多个线程,打印出卖每张票的窗口名

public static void main(String[] args) {
        Ticket ticket=new Ticket();//创建资源类
  //以下创建了三个线程,卖票
        new Thread(()->{
            for(int i=0;i<40;i++){
                ticket.sale();
            }
        },"A").start();
        new Thread(()->{
            for(int i=0;i<40;i++)
            {
                ticket.sale();
            }
        },"B").start();
        new Thread(()->{
            for(int i=0;i<40;i++)
            {
                ticket.sale();
            }
        },"C").start();
    }

结果输出:

A卖出了第50张票,剩余50张票
A卖出了第49张票,剩余49张票
A卖出了第48张票,剩余48张票
A卖出了第47张票,剩余47张票
A卖出了第46张票,剩余46张票
A卖出了第45张票,剩余45张票
A卖出了第44张票,剩余44张票
A卖出了第43张票,剩余43张票
A卖出了第42张票,剩余42张票
A卖出了第41张票,剩余41张票
A卖出了第40张票,剩余40张票
A卖出了第39张票,剩余39张票
A卖出了第38张票,剩余38张票
A卖出了第37张票,剩余37张票
A卖出了第36张票,剩余36张票
A卖出了第35张票,剩余35张票
A卖出了第34张票,剩余34张票
C卖出了第33张票,剩余33张票
C卖出了第32张票,剩余32张票
C卖出了第31张票,剩余31张票
C卖出了第30张票,剩余30张票
C卖出了第29张票,剩余29张票
C卖出了第28张票,剩余28张票
C卖出了第27张票,剩余27张票
C卖出了第26张票,剩余26张票
C卖出了第25张票,剩余25张票
C卖出了第24张票,剩余24张票
C卖出了第23张票,剩余23张票
C卖出了第22张票,剩余22张票
C卖出了第21张票,剩余21张票
C卖出了第20张票,剩余20张票
C卖出了第19张票,剩余19张票
C卖出了第18张票,剩余18张票
C卖出了第17张票,剩余17张票
C卖出了第16张票,剩余16张票
C卖出了第15张票,剩余15张票
C卖出了第14张票,剩余14张票
C卖出了第13张票,剩余13张票
C卖出了第12张票,剩余12张票
C卖出了第11张票,剩余11张票
C卖出了第10张票,剩余10张票
C卖出了第9张票,剩余9张票
C卖出了第8张票,剩余8张票
C卖出了第7张票,剩余7张票
C卖出了第6张票,剩余6张票
C卖出了第5张票,剩余5张票
C卖出了第4张票,剩余4张票
C卖出了第3张票,剩余3张票
C卖出了第2张票,剩余2张票
C卖出了第1张票,剩余1张票

02.公平锁和非公平锁

公平锁: 十分公平,必须先来后到~;

非公平锁: 十分不公平,可以插队;(默认为非公平锁)

03.Lock锁

建立Lock锁进行锁定,然后当前线程访问之后,释放锁

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

public class SaleTicketDemo02
{
    public static void main(String[] args) {
       //创建资源类
        Ticket2 ticket = new Ticket2();
        new Thread(()->{for(int i=0;i<40;i++) ticket.sale(); },"A").start();
        new Thread(()->{for(int i=0;i<40;i++) ticket.sale(); },"B").start();
        new Thread(()->{for(int i=0;i<40;i++) ticket.sale(); },"C").start();
    }
}
class Ticket2{
    private int number=50;

    Lock lock=new ReentrantLock();

    //卖票的方式
    // 使用Lock 锁
    public void sale(){
        //加锁
        lock.lock();
        try {
            //业务代码
            if(number>=0){
                System.out.println(Thread.currentThread().getName()+" 卖出了第"+number+" 张票,剩余:"+number+" 张票");
                number--;
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
        finally {
            //解锁
            lock.unlock();
        }
    }
}
A 卖出了第50 张票,剩余:50 张票
A 卖出了第49 张票,剩余:49 张票
A 卖出了第48 张票,剩余:48 张票
A 卖出了第47 张票,剩余:47 张票
A 卖出了第46 张票,剩余:46 张票
A 卖出了第45 张票,剩余:45 张票
A 卖出了第44 张票,剩余:44 张票
A 卖出了第43 张票,剩余:43 张票
A 卖出了第42 张票,剩余:42 张票
A 卖出了第41 张票,剩余:41 张票
A 卖出了第40 张票,剩余:40 张票
A 卖出了第39 张票,剩余:39 张票
A 卖出了第38 张票,剩余:38 张票
A 卖出了第37 张票,剩余:37 张票
A 卖出了第36 张票,剩余:36 张票
A 卖出了第35 张票,剩余:35 张票
A 卖出了第34 张票,剩余:34 张票
A 卖出了第33 张票,剩余:33 张票
A 卖出了第32 张票,剩余:32 张票
A 卖出了第31 张票,剩余:31 张票
A 卖出了第30 张票,剩余:30 张票
A 卖出了第29 张票,剩余:29 张票
A 卖出了第28 张票,剩余:28 张票
A 卖出了第27 张票,剩余:27 张票
A 卖出了第26 张票,剩余:26 张票
A 卖出了第25 张票,剩余:25 张票
A 卖出了第24 张票,剩余:24 张票
A 卖出了第23 张票,剩余:23 张票
A 卖出了第22 张票,剩余:22 张票
A 卖出了第21 张票,剩余:21 张票
A 卖出了第20 张票,剩余:20 张票
A 卖出了第19 张票,剩余:19 张票
A 卖出了第18 张票,剩余:18 张票
A 卖出了第17 张票,剩余:17 张票
A 卖出了第16 张票,剩余:16 张票
A 卖出了第15 张票,剩余:15 张票
A 卖出了第14 张票,剩余:14 张票
A 卖出了第13 张票,剩余:13 张票
A 卖出了第12 张票,剩余:12 张票
A 卖出了第11 张票,剩余:11 张票
B 卖出了第10 张票,剩余:10 张票
B 卖出了第9 张票,剩余:9 张票
B 卖出了第8 张票,剩余:8 张票
B 卖出了第7 张票,剩余:7 张票
B 卖出了第6 张票,剩余:6 张票
B 卖出了第5 张票,剩余:5 张票
B 卖出了第4 张票,剩余:4 张票
B 卖出了第3 张票,剩余:3 张票
B 卖出了第2 张票,剩余:2 张票
B 卖出了第1 张票,剩余:1 张票
B 卖出了第0 张票,剩余:0 张票

Lock锁和synchronized的区别

1、Synchronized 内置的Java关键字,Lock是一个Java类

2、Synchronized 无法判断获取锁的状态,Lock可以判断

3、Synchronized 会自动释放锁,lock必须要手动加锁和手动释放锁!可能会遇到死锁

4、Synchronized 线程1(获得锁->阻塞)、线程2(等待);

lock就不一定会一直等待下去,lock会有一个trylock去尝试获取锁,不会造成长久的等待。

5、Synchronized 是可重入锁,不可以中断的,非公平的;Lock,可重入的,可以判断锁,可以自己设置公平锁和非公平锁;

6、Synchronized 适合锁少量的代码同步问题,Lock适合锁大量的同步代码;

04.生产者和消费者问题

问题简介:生产者和消费者问题在现实系统中是很普遍的。例如在一个多媒体系统中,生产者编码视频帧,而消费者消费(解码)视频帧,缓冲区的目的就是减少视频流的抖动。又如在图形用户接口设计中,生产者检测到鼠标和键盘事件,并将其插入到缓冲区中。消费者以某种基于优先级的方式从缓冲区中取出这些事件并显示在屏幕上。

生产者和消费者模式共享一个有n个槽位的有限缓冲区。生产者反复地生成新的item,并将它插入到缓冲区中。消费者不断从缓冲区中取出这些item,并消费它们。它有3个显著特点:

因为生产和消费都涉及共享变量的更新,所以必须保证对缓冲区的访问是互斥的。
如果缓冲区是满的,那么生产者必须等待直到有一个槽位可用。
如果缓冲区是空的,那么消费者必须等待直到有一个item可以。

两个维护一个只有两个状态的数字,一个让数字增加1,另一个让数字减少1

//资源类创建
//建立资源类
class Data{
    //数字  资源类
    private int number = 0;

    //+1
    public synchronized void increment() throws InterruptedException {
        if(number!=0){
            //等待操作,当数字不等于0,说明当前的数字等于1,需要等待另一个线程进行减的操作
            this.wait();
        }
      //如果当前的数字为0,则增加1
        number++;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程 我+1完毕了,释放锁
        this.notifyAll();
    }

    //-1
    public synchronized void decrement() throws InterruptedException {
        if(number==0){
            //等待操作,当前的数字等于0,则不能进行减操作,需要等待其他的进程将数字加1
            this.wait();
        }
      //当前数字为1,进行减的操作
        number--;
        System.out.println(Thread.currentThread().getName()+"=>"+number);
        //通知其他线程  我-1完毕了,进行锁释放
        this.notifyAll();
    }

}

主类创建

public class JUCDemo3 {
    public static void main(String[] args) {
      //资源类创建
        Data data = new Data();

        new Thread(()->{for(int i=0;i<10;i++) {
            try {//尝试进行减的操作,成功则继续,不然就等待
                data.increment();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        },"A").start();
        new Thread(()->{for(int i=0;i<10;i++) {
            try {
                data.decrement();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }},"B").start();
    }

}

05.虚假唤醒问题

05.1如果不使用线程的唤醒操作

生产者和消费者问题,程序有两个线程,一个生产,一个消费,不能多余10,不能少于0,如果不使用进程的唤醒,产生的效果如下:

public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer pro = new Producer(clerk);
        Consumer cus = new Consumer(clerk);

        new Thread(pro,"生产者A").start();
        new Thread(cus,"消费者B").start();
    }
}
//仓库类
class Clerk {
    private int product = 0;

    //进货
    public synchronized void get() {
        if (product >= 10) {
            System.out.println("仓库已满");
        } else {
            System.out.println(Thread.currentThread().getName() + ":" + ++product);
        }

    }

    //卖货
    public synchronized void sale() {
        if (product <= 0) {
            System.out.println("缺货");
        } else {
            System.out.println(Thread.currentThread().getName() + ":" + --product);
        }
    }
}

//生产者类producer
class Producer implements Runnable {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.get();
        }
    }
}

//消费者类
class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
仓库已满
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
缺货
缺货
缺货
缺货
缺货
缺货
缺货
缺货
缺货
缺货
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9

05.2 线程进行唤醒操作

由于两个进程缺乏交流,导致生产者不断的进行生产,而消费者不断进行着消费。使用sleep()和wait()方法进行等待唤醒操作

public class TestProductorAndConsumer {

    public static void main(String[] args) {
        Clerk clerk = new Clerk();
        Producer pro = new Producer(clerk);
        Consumer cus = new Consumer(clerk);

        new Thread(pro,"生产者A").start();
        new Thread(cus,"消费者B").start();
    }
}
//仓库类
class Clerk {
    private int product = 0;

    //进货
    public synchronized void get() {
        if (product >= 10) {

            System.out.println("仓库已满");
            try {
                this.wait();//如果仓库满了,则应该进行等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + ":" + ++product);
            this.notify();
        }

    }

    //卖货
    public synchronized void sale() {
        if (product <= 0) {

            System.out.println("缺货");
            try {
                this.wait();//如果当前缺货,则进行等待,而不是一直进行
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + ":" + --product);
            this.notify();//对其他的进程进行通知
        }

    }
}

//生产者类producer
class Producer implements Runnable {
    private Clerk clerk;

    public Producer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.get();
        }
    }
}

//消费者类
class Consumer implements Runnable {
    private Clerk clerk;

    public Consumer(Clerk clerk) {
        this.clerk = clerk;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; i++) {
            clerk.sale();
        }
    }
}

生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
生产者A:10
仓库已满
消费者B:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0
缺货
生产者A:1
生产者A:2
生产者A:3
生产者A:4
生产者A:5
生产者A:6
生产者A:7
生产者A:8
生产者A:9
消费者B:8
消费者B:7
消费者B:6
消费者B:5
消费者B:4
消费者B:3
消费者B:2
消费者B:1
消费者B:0

06.Lock锁可以进行唤醒固定的进程

三个进程共享依次唤醒

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


public class JUCDemo4 {

    public static void main(String[] args) {
        Data3 data3 = new Data3();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printA();
            }
        },"A").start();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printB();
            }
        },"B").start();
        new Thread(()->{
            for(int i=0;i<10;i++){
                data3.printC();
            }
        },"C").start();
    }
}


class Data3{
    //资源类
    private Lock lock=new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private Condition condition3 = lock.newCondition();
    private int number = 1; //1A 2B 3C

    public void printA(){
        lock.lock();
        try {
            //业务 判断 -> 执行 -> 通知
            while(number!=1){
                //等待
                condition1.await();
            }
            //操作
            System.out.println(Thread.currentThread().getName()+",AAAAA");
            //唤醒指定的线程
            number=2;
            condition2.signal(); // 唤醒2

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printB(){
        lock.lock();
        try {
            //业务 判断 -> 执行 -> 通知
            while (number!=2){
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName()+",BBBBB");
            //唤醒3
            number=3;
            condition3.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
    public void printC(){
        lock.lock();
        try {
            //业务 判断 -> 执行 -> 通知
            while(number!=3){
                condition3.await();
            }
            System.out.println(Thread.currentThread().getName()+",CCCCC");
            //唤醒1
            number=1;
            condition1.signal();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
}
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC
A,AAAAA
B,BBBBB
C,CCCCC

07.集合类的线程安全

测试程序如下,发现

public class ListTest {


    public static void main(String[] args) {

        List<Object> arrayList = new ArrayList<>();

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

    }
}
Exception in thread "3" java.util.ConcurrentModificationException
	at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
	at java.util.ArrayList$Itr.next(ArrayList.java:861)
	at java.util.AbstractCollection.toString(AbstractCollection.java:461)
	at java.lang.String.valueOf(String.java:2994)

解决方法:

//1 
List<Object> arrayList = Collections.synchronizedList(new ArrayList<>());
//2(使用的lock锁,效率高)
List arrayList = new CopyOnWriteArrayList<>();

set,hashMap也是一样的线程不安全

08.多线程几种实现形式

08.1继承Runnable接口,重写方法

public class JUCDemo5 {
    public static void main(String[] args) {
        for (int i = 1; i < 10; i++) {
            //start进行调用
            new Thread(new MyThread()).start();
        }
    }
}

class MyThread implements Runnable//实现Runnable接口
{

    @Override//重写run方法
    public void run() {
        System.out.println(Thread.currentThread().getName());
    }
}

08.2继承Thread类实现多线程

public class MyThread extends Thread {
  public void run() {
   System.out.println("MyThread.run()");
  }
}
MyThread myThread1 = new MyThread();
myThread1.start();

08.3实现Callable接口

09.读写锁

多个线程对同一个文件进行读写操作,

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class JUCDemo6 {
    public static void main(String[] args) {
        MyCache_ReadWriteLock mycache = new MyCache_ReadWriteLock();
        //开启5个线程 写入数据
        for (int i = 1; i <=5 ; i++) {
            int finalI = i;
            new Thread(()->{
                mycache.put(String.valueOf(finalI),String.valueOf(finalI));
            }).start();
        }
        //开启10个线程去读取数据
        for (int i = 1; i <=10 ; i++) {
            int finalI = i;
            new Thread(()->{
                String o = mycache.get(String.valueOf(finalI));
            }).start();
        }
    }
}

class MyCache_ReadWriteLock{
    private volatile Map<String,String> map=new HashMap<>();

    //使用读写锁
    private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    //普通锁
    private Lock lock=new ReentrantLock();

    public void put(String key,String value){
        //加锁
        readWriteLock.writeLock().lock();
        try {
            //写入
            //业务流程
            System.out.println(Thread.currentThread().getName()+" 线程 开始写入");
            map.put(key, value);
            System.out.println(Thread.currentThread().getName()+" 线程 写入OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.writeLock().unlock(); //解锁
        }
    }

    public String get(String key){
        //加锁
        String o="";
        readWriteLock.readLock().lock();
        try {
            //得到
            System.out.println(Thread.currentThread().getName()+" 线程 开始读取");
            o = map.get(key);
            System.out.println(Thread.currentThread().getName()+" 线程 读取OK");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            readWriteLock.readLock().unlock();
        }
        return o;
    }
}

readWriteLock可以完美的保证文件的多线程读写问题

Thread-0 线程 开始写入
Thread-0 线程 写入OK
Thread-4 线程 开始写入
Thread-4 线程 写入OK
Thread-1 线程 开始写入
Thread-1 线程 写入OK
Thread-2 线程 开始写入
Thread-2 线程 写入OK
Thread-3 线程 开始写入
Thread-3 线程 写入OK
Thread-5 线程 开始读取
Thread-5 线程 读取OK
Thread-6 线程 开始读取
Thread-6 线程 读取OK
Thread-7 线程 开始读取
Thread-7 线程 读取OK
Thread-8 线程 开始读取
Thread-8 线程 读取OK
Thread-9 线程 开始读取
Thread-9 线程 读取OK
Thread-10 线程 开始读取
Thread-10 线程 读取OK
Thread-11 线程 开始读取
Thread-11 线程 读取OK
Thread-12 线程 开始读取
Thread-12 线程 读取OK
Thread-13 线程 开始读取
Thread-13 线程 读取OK
Thread-14 线程 开始读取
Thread-14 线程 读取OK
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值