Java多线程-基础篇

线程概述

什么是进程

进程是系统进行资源分配基本单位,也是独立运行的单位。

什么是线程

线程又称为轻量级进程,它是进程内一个相对独立的、可调度的执行单元,也是CPU的基本调度单位

俩者区别

  1. 进程是系统进行资源分配的基本单位,线程是CPU的基本调度单位。
  2. 一个进程至少拥有一个线程。
  3. 进程之间不能共享数据,而进程中的线程可以。

线程组成

  1. CPU时间片:操作系统会为每个线程单位分配执行时间
  2. 运行数据:
    1. 堆空间:存储线程需要使用的对象,多个线程可以共享堆中的对象。
    2. 栈空间:存储线程需要使用的局部变量,每个线程都拥有独立的栈。
  3. 代码逻辑

线程特点

  1. 线程抢占式执行。
    1. 效率高。
    2. 可防止单一线程长时间独占CPU。
  2. 在单核CPU中,宏观上同时执行,微观上顺序执行。

JAVA如何创建线程

继承Thread类

package com.zsl;

public class ThreadOne extends Thread{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println("子线程编号:"+i);
        }
    }
}
package com.zsl;


import org.junit.Test;

public class TestUtils {

    @Test
    public void ThreadOneTest(){
        System.out.println("开始主线程");
        ThreadOne threadOne = new ThreadOne();
        threadOne.start();
        System.out.println("结束主线程");
    }
}
/*
开始主线程
结束主线程
子线程编号:0
子线程编号:1
子线程编号:2
子线程编号:3
子线程编号:4
子线程编号:5
......
*/

实现Runnable接口

package com.zsl.threadLearning;

/**
* @author m1767
*/
public class ThreadCallable implements Runnable{
    private final Integer SIZE = 100000;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+i);
        }
    }

    public static void main(String[] args) {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        thread.start();
        System.out.println("主线程结束");
    }
}

实现Callable接口(线程池进行讲解)

线程的状态

初始状态

  • 线程被创建的时候处于初始状态

就绪状态

  • 线程对象调用start方法后进入就绪状态。

运行状态

  • 获取处理器后,进入运行状态,等到时间片结束后,就进入就绪状态了

等待状态

  • 线程无法继续执行的状态。比如调用sleep方法。

终止状态

  • 主线程结束或者该线程的run方法结束后进入终止状态,并释放CPU

线程常用方法

sleep()方法

public static void sleep(long millis)当前现场休眠

public class ThreadCallable implements Runnable{
    private final Integer SIZE = 10;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        thread.start();
        System.out.println("主线程结束");
    }

yield()方法

public static void yield()当前线程主动休眠millis毫秒。

public class ThreadCallable implements Runnable{
    private final Integer SIZE = 10;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+Thread.currentThread().getName()+";i="+i);
            Thread.yield();
        }
    }

    public static void main(String[] args) {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        Thread thread1 = new Thread(threadCallable);
        thread.start();
        thread1.start();
        System.out.println("主线程结束");
    }
}

join()方法

public final void join()允许其他线程加入到当前线程中。当某线程调用该方法时,加入并阻塞当前线程,直到加入的线程执行完毕,当前线程才继续执行。

public class ThreadCallable implements Runnable{
    private final Integer SIZE = 10000;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+Thread.currentThread().getName()+";i="+i);
            Thread.yield();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        thread.start();
        thread.join();
        for (int i = 0; i <10 ; i++) {
            System.out.println("主线程======"+i);
        }
        System.out.println("主线程结束");
    }
}

子线程加入主线程后阻塞了主线程、直到子线程执行完,才执行主线程。

setPriority(int newPriority)方法

setPriority(int newPriority)改变该线程的优先级,线程优先级为1-10,默认为5,优先级越高,表示获取CPU机会越多。

public class ThreadCallable implements Runnable{
    private final Integer SIZE = 100;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+Thread.currentThread().getName()+";i="+i);
            Thread.yield();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        Thread thread1 = new Thread(threadCallable);
        Thread thread2 = new Thread(threadCallable);
        thread.setPriority(1);
        thread1.setPriority(10);
        thread.start();
        thread1.start();
        thread2.start();
        for (int i = 0; i <10 ; i++) {
            System.out.println("主线程======"+i);
        }
        System.out.println("主线程结束");
    }
}

setDaemon(boolean on)方法

public final void setDaemon(boolean on) 如果参数为true,则标记该线程为守护线程。
在JAVA中线程有两类:用户线程(前台线程)、守护线程(后台线程)。守护可以理解为守护用户线程。如果程序中所有用户线程都执行完毕了,守护线程会自动结束。垃圾回收线程属于守护线程


@SuppressWarnings("all")
public class ThreadCallable implements Runnable{
    private final Integer SIZE = 100;

    @Override
    public void run() {
        for (int i = 0; i < SIZE; i++) {
            System.out.println("子线程-"+Thread.currentThread().getName()+";i="+i);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ThreadCallable threadCallable = new ThreadCallable();
        Thread thread = new Thread(threadCallable);
        thread.setDaemon(true);
        thread.start();
        for (int i = 0; i <10 ; i++) {
            System.out.println("主线程======"+i);
            Thread.sleep(1000);
        }
        System.out.println("主线程结束");
    }
}

线程安全

对于线程安全问题,我们用一个简单的例子进行描述。

public class ThreadSafe {
    public static int index =0;

    public static void main(String[] args) throws InterruptedException {
        String[] names = new String[5];
        Runnable runnableA = new Runnable() {
            @Override
            public void run() {
                names[index]="hello";
                index++;
            }
        };
        Runnable runnableB=new Runnable() {
            @Override
            public void run() {
                names[index]="world";
                index++;
            }
        };
        Thread A=new Thread(runnableA);
        Thread B=new Thread(runnableB);
        A.start();
        B.start();
        //加入主线程,用来阻塞主线程使最后的输出语句最后执行
        A.join();
        B.join();
        System.out.println(Arrays.toString(names));
    }
}

输出的其中一种情况为[hello, world, null, null, null]/[world, null, null, null, null],这里对第二种情况进行说明。线程A存hello的时候,CPU被线程B抢占了,由于index没有发生变化还是为0,此时存word完全覆盖掉了hello,然后执行index,最后导致这个结果

多线程安全问题

当多线程并发访问临界资源时,如果破坏了原子操作,可能会造成数据不一致。

  • 临界资源:共享资源(对于同一个对象),一次仅允许一个线程使用,才可以保证其正确性。
  • 原子操作:不可分割的多步操作,被视为一个整体,其顺序和步骤不可打乱或缺省,比如上一段代码的存hello和存world应当被看成两个原子操作。

同步方式(1)

//对临界资源对象加锁
synchronized(临界资源对象){
    //代码(原子操作)
}

public class ThreadSafe {
    public static int index =0;

    public static void main(String[] args) throws InterruptedException {
        String[] names = new String[5];
        Runnable runnableA = new Runnable() {
            @Override
            public void run() {
                synchronized (names){
                    names[index]="hello";
                    index++;
                }

            }
        };
        Runnable runnableB=new Runnable() {
            @Override
            public void run() {
                synchronized (names){
                    names[index]="world";
                    index++;
                }
            }
        };
        Thread A=new Thread(runnableA);
        Thread B=new Thread(runnableB);
        A.start();
        B.start();
        //加入主线程,用来阻塞主线程使最后的输出语句最后执行
        B.join();
        A.join();
        System.out.println(Arrays.toString(names));

    }
}

每个对象都有一个互斥锁标记用来分配给线程的
只有拥有对象互斥锁标记的线程,才能进入对该对象加锁的同步代码块。
线程退出同步代码块时,会释放相应的互斥锁标记。

线程基本状态

public enum State {
    NEW,//初始状态
    RUNNABLE,//就绪状态和执行状态
    BLOCKED,//BLOCKED 阻塞状态
    WAITING,//WAITING (无期限)等待状态
    TIMED_WAITING,//TIMED_WAITING 有限等待状态
    TERMINATED;//TERMINATED 终止状态
}

线程正在等待时的状态。线程被以下方法所调用就会进入等待状态:
Object.wait无参方法
Thread.join无参方法
LockSupport.park
wait方法可以让当前线程进入等待状态,需要其他线程调用此线程对象的notify方法或者notifyAll方法来唤醒此线程;调用join方法的线程需要等到被调用线程终止才能结束等待状态。

TIMED_WAITING 有限等待状态
线程在指定时间后才能结束等待的一种等待状态。是由于调用了以下方法所引起的一种状态:
Thread.sleep
Object.wait带参方法
Thread.join带参方法
LockSupport.parkNanos
LockSupport.parkUntil

同步方式(2)

//对当前对象(this)加锁
synchronized 返回值类型 方法名称(形参列表){
    //代码(原子操作)
}

死锁

死锁

  • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
  • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的锁标记,由此可能造成死锁。
/**
 * 锁对象(筷子)
 */
 class Chopsticks {
}

public class ThreadSafe {
    public static int index = 0;

    public static void main(String[] args) throws InterruptedException {
        //创建两个锁对象(两根筷子)
        Chopsticks chopsticks1=new Chopsticks();
        Chopsticks chopsticks2=new Chopsticks();

        Runnable A=new Runnable() {
            @Override
            public void run() {
                //持有第一根筷子
                synchronized (chopsticks1) {
                    System.out.println("A拿到了一根筷子。");
                    //持有第二根筷子
                    synchronized (chopsticks2) {
                        System.out.println("A拿到了两根筷子,开始恰饭。");
                    }
                }
            }
        };

        Runnable B=new Runnable() {
            @Override
            public void run() {
                //持有第一根筷子
                synchronized (chopsticks2) {
                    System.out.println("B拿到了一根筷子。");
                    //持有第二根筷子
                    synchronized (chopsticks1) {
                        System.out.println("B拿到了两根筷子,开始恰饭。");
                    }
                }
            }
        };

        new Thread(A).start();
        new Thread(B).start();
    }
}

运行结果(程序没有终止):A在等待B,B又在等待A。造成死锁
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gMvQhPEC-1674030532273)(/upload/2023/01/1673946339412-69a39251-ec41-4ffe-bf01-9b94be537562.png)]

线程通信

对数据的共享。

public class AddMoney implements Runnable{
    private Blank blank;

    public AddMoney(Blank blank){
        this.blank = blank;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                this.blank.addMoney(101.0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class SubMoney implements Runnable{
    private Blank blank;

    public SubMoney(Blank blank){
        this.blank = blank;
    }

    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try {
                this.blank.take(101.0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
public class Blank {
    private Double money;
    private Double nowDateMoney;
    boolean flag = false;

    public Blank(Double money) {
        this.money = money;
    }

    public synchronized void addMoney(Double money) throws InterruptedException {
        if (flag) {
            this.wait();
        }
        this.money += money;
        System.out.println("您本次存钱=》" + this.money);
        flag = true;
        this.notify();
    }

    public synchronized void take(Double money) throws InterruptedException {
        if (!flag) {
            this.wait();
        }
        this.money += money;
        System.out.println("您本次取钱=》" + this.nowDateMoney);
        flag = false;
        this.notify();
    }
}

public class ThreadMain {
    public static void main(String[] args) {
        //创建银行卡对象
        Blank blank=new Blank(1000.0);
        //创建操作
        AddMoney addMoney=new AddMoney(blank);
        SubMoney subMoney=new SubMoney(blank);
        //创建线程对象并启动
        new Thread(addMoney).start();
        new Thread(subMoney).start();

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4Dt3Wf7E-1674030532274)(/upload/2023/01/1673950694922-a005aede-fb19-4d99-98d8-13f1f23fb03c.png)]

生产者消费者

在个车间仓库,生产者不断的生产商品,但是仓库慢了就要停止生产;消费者消费商品,仓库里空了就无法进行消费。所以俩者要保持同步。


@SuppressWarnings("all")
public class ProductFactory {
    private String[] products;
    private int index;

    public ProductFactory(){
        this.index = -1;
        this.products = new String[5];
    }

    public synchronized void  productor() throws InterruptedException {
        while (this.index >= 4){
            this.wait();
        }
        this.products[++this.index] = "product";
        System.out.println(Thread.currentThread().getName()+"生产了一个产品,产品数量:"+(index+1));
        System.out.println(Arrays.toString(products));
        this.notifyAll();
    }

    public synchronized void  consumer() throws InterruptedException {
        while (this.index <0){
            this.wait();
        }
        this.products[this.index--] = "null";
        System.out.println(Thread.currentThread().getName()+"消费了一个产品,产品数量:"+(index+1));
        System.out.println(Arrays.toString(products));
        this.notifyAll();
    }
}


public class Productor implements Runnable{
    ProductFactory productBuf=new ProductFactory();
    public Productor() {
    }
    public Productor(ProductFactory productBuf) {
        this.productBuf=productBuf;
    }
    @Override
    public void run() {
        //生产30个产品
        for(int i=0;i<30;i++) {
            try {
                productBuf.productor();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class Consumer implements Runnable{
	ProductFactory productBuf=new ProductFactory();
	public Consumer() {
	}
	public Consumer(ProductFactory productBuf) {
		this.productBuf=productBuf;
	}
	@Override
	public void run() {
		//消费30个产品
		for(int i=0;i<30;i++) {
			try {
				productBuf.consumer();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

@SuppressWarnings("all")
public class testProduct {
	public static void main(String[] args) {
		ProductFactory productBuf=new ProductFactory();
		Productor productor=new Productor(productBuf);
		Consumer consumer=new Consumer(productBuf);
		new Thread(productor,"生产者1号").start();
		new Thread(productor,"生产者2号").start();
		new Thread(consumer,"消费者1号").start();
		new Thread(consumer,"消费者2号").start();
	}
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UQHAuHU7-1674030532274)(/upload/2023/01/1674006538571-2cbfacd2-b9e7-4fd9-bb65-d21dc52a4225.png)]

线程池

线程池概念

线程存在的问题:

  1. 线程是宝贵的内存资源、单个线程约占1MB空间,过多分配易造成内存溢出。
  2. 频繁的创建及销毁线程会增加虚拟机回收频率、资源开销,造成性能下降。

所以线程池的出现是为了解决以上问题:

  • 线程容器可设定线程分配的数量
  • 将预先创建的线程对象存入池中并重用线程池中的线程对象
  • 避免频繁的创建和销毁

线程池原理

线程池有俩个线程,此时有三个任务。线程池中的俩个线程先分别完成任务1/2,任务3进入等待状态。执行完前面俩个任务后,再执行任务3.
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69gR2MzN-1674030532275)(/upload/2023/01/1674007277766-03c5e34e-2187-4b3f-8b7c-00465e7a1cce.png)]

创建线程池

常用的线程池接口的类(所在包java.util.concurrent)
Executor:线程池的顶级接口。
ExecutorService:线程池接口,可通过submit(Runnable task)提交任务代码。
Executors工厂类:创建线程池的工具类。
创建固定线程个数的线程池。
创建缓存线程池,由任务的多少决定。
创建单线程池。
创建调度线程池。调度:周期、定时执行。
通过newFixedThreadPool(int nThreads)获得固定数量的线程池。参数:指定线程池中线程的数量。
通过newCachedThreadPool()获得动态数量的线程池,如不够则创建新的,没有上限。

@SuppressWarnings("all")
public class ExecutorsTest {
    public static void main(String[] args) {
        ProductFactory productBuf=new ProductFactory();
        Productor productor=new Productor(productBuf);
        Consumer consumer=new Consumer(productBuf);
        //创建固定线程个数的线程池
        //ExecutorService executorService=Executors.newFixedThreadPool(4);
        //  创建缓存线程池,线程个数由任务个数决定
        ExecutorService executorService = Executors.newCachedThreadPool();
        //1.3创建单线程线程池
        //Executors.newSingleThreadExecutor();
        //1.4创建调度线程池
        //Executors.newScheduledThreadPool(corePoolSize);
        executorService.submit(productor);
        executorService.submit(productor);
        executorService.submit(consumer);
        executorService.submit(consumer);
        //等待所有已执行的任务执行完毕后关闭线程池,不再接受新任务
        executorService.shutdown();
        //会试图停止所有正在执行的任务
        //executorService.shutdownNow();
    }
}

Callable接口

public interface Callable<V>{
    public V call() throws Exception;
}

  • JDK1.5加入,与Runnable接口类似,实现之后代表一个线程任务。
  • Callable具有泛型返回值、可以声明异常。

与Runnable接口的区别

  1. Callable接口中call方法有返回值,Runnable接口中run方法没有返回值。
  2. Callable接口中call方法有声明异常,Runnable接口中run方法没有异常。
public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i < 100; i++) {
                    sum += i;
                }
                return  sum;
            }
        };
        // Thread 的构造方法没有带callable的构造方法
        // 可以通过FutureTask对象转化成可执行任务,FutureTask 表示将要执行的任务
        //该类实现了RunnableFuture<V>接口,而该接口又继承了Runnable类
        FutureTask<Integer> task = new FutureTask<>(callable);
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("和="+task.get());

    }
}

线程池的使用

public class CallableTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i < 100; i++) {
                    sum += i;
                }
                return  sum;
            }
        };
        Future<Integer> future = executorService.submit(callable);

        System.out.println("和="+future.get());
        executorService.shutdown();
    }
}

Future接口

表示将要完成任务的结果
用法如上。

线程的同步与异步

  • 同步形容一次方法调用,同步一旦开始,调用者必须等待该方法返回,才能继续。当主线程调用子线程执行任务时,必须等到子线程返回结果后才能继续。
  • 异步形容一次方法调用,异步一旦开始就像是一次消息传递,调用者告知之后立刻返回。二者竞争时间片,并发执行。异步有多条执行路径

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0lzINrRq-1674030532275)(/upload/2023/01/1674009552136-72f1bc6f-2238-4f34-a250-de1f43fa48fe.png)]

Lock接口

  • JDK1.5加入,与synchronized比较,不仅显示定义,而且结构更灵活。
  • 提供了更多实用性方法,功能更强大、性能更优越。

常用方法:

  • void lock获取锁,如果锁被占用,当前线程则进入等待状态。
  • boolean tryLock()尝试获取锁(成功返回true,失败返回false,不阻塞)
  • void unlock()释放锁。

重入锁

  • ReentrantLock: Lock接口的实现类,与synchronized一样具有互斥锁功能。所谓重入锁,是指一个线程拿到该锁后还可以再次成功获取,而不会因为该锁已经被持有(尽管是自己所持有)而陷入等待状态(死锁)。之前说过的synchronized也是可重入锁
@SuppressWarnings("all")
public class Chopsticks {
    private boolean getOneFlag = false;
    private boolean getAnotherFlag = false;


    public synchronized void getOne(){
        this.getOneFlag = true;
        System.out.println("当前用餐人员:"+Thread.currentThread().getName()+"拿了一个筷子");
        if (getAnotherFlag){
            canEat();
            this.getAnotherFlag = false;
            this.getOneFlag = false;
        }else{
            getAnother();
        }
    }

    public synchronized void getAnother(){
        this.getAnotherFlag = true;
        System.out.println("当前用餐人员:"+Thread.currentThread().getName()+"拿了一个筷子");
        if (getOneFlag){
            canEat();
            this.getAnotherFlag = false;
            this.getOneFlag = false;
        }else{
            getOne();
        }
    }

    public synchronized void canEat() {
        System.out.println(Thread.currentThread().getName()+"拿到了两根筷子,开恰!");
    }
}
public class ChopsticksTest {
    public static void main(String[] args) {
        Chopsticks chopsticks = new Chopsticks();
        Runnable runnableA = new Runnable() {
            @Override
            public void run() {
                chopsticks.getOne();
            }
        };
        Runnable runnableB = new Runnable() {
            @Override
            public void run() {
                chopsticks.getOne();
            }
        };
        Thread threadA = new Thread(runnableA,"A");
        Thread threadB = new Thread(runnableB,"B");
        threadA.start();
        threadB.start();
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MacOCBoC-1674030532276)(/upload/2023/01/1674025936210-86094c81-77b4-4a25-8ebe-a793def3d0c7.png)]
锁的对象是Chopsticks,当A线程获取到锁后,即使没有执行完,线程B抢占了CPU,由于没有线程B没有获取锁,也只能继续等待。线程A会继续执行后续的代码,执行完之后线程B才获取锁,进入getAnother()方法,开始执行代码。

重入锁的使用

public class Ticket implements Runnable {
    int numbers = 100;
    Lock lock = new ReentrantLock();

    @Override
    public void run() {
            lock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + "买了一张票,还剩" + (--numbers) + "张。");
            } finally {
                lock.unlock();
            }

    }

    public static void main(String[] args) {
        Ticket ticket = new Ticket();
        ExecutorService executorService = Executors.newFixedThreadPool(50);
        for (int i = 0; i < 50; i++) {
            executorService.submit( ticket);
        }
        executorService.shutdown();

    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4vs4mdVL-1674030532276)(/upload/2023/01/1674027111819-37430f80-335b-47d2-afcf-7dd38a7972c1.png)]
注意:有几个lock(),就要写几个unlock;

读写锁

ReentrantReadWriteLock:

  1. 一种支持一写多读的同步锁,读写分离,可以分别分配读锁和写锁。
  2. 支持多次分配读锁,使多个读操作可以并发执行。

互斥规则:

  1. 写----写:互斥,一个线程在写的同时其他线程会被阻塞。
  2. 读----写:互斥,读的时候不能写,写的时候不能读。
  3. 读----读:不互斥、不阻塞。

在读操作远远高于写操作的环境中,可在保证线程安全的情况下,提高运行效率。

@SuppressWarnings("all")
//演示读写锁的使用
public class ReadWriteLock {
		//创建读写锁对象
		ReentrantReadWriteLock rrlLock=new ReentrantReadWriteLock();
		ReentrantReadWriteLock.ReadLock readLock=rrlLock.readLock();//获得读锁
		ReentrantReadWriteLock.WriteLock writeLock=rrlLock.writeLock();//获得写锁
		private int value=999;
		//读方法
		public int getValue() {
			readLock.lock();//开启读锁
			try {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				return this.value;
			} finally {
				readLock.unlock();//释放读锁
			}
		}
		//写方法
		public void setValue(int value) {
			writeLock.lock();//开启写锁
			try {
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				this.value=value;
			} finally {
				writeLock.unlock();//释放写锁
			}
		}

	public static void main(String[] args) {
		ExecutorService eService= Executors.newFixedThreadPool(20);
		ReadWriteLock rwlLock=new ReadWriteLock();
		Runnable read=new Runnable() {
			@Override
			public void run() {
				System.out.println(rwlLock.getValue());
			}
		};
		Runnable write=new Runnable() {
			@Override
			public void run() {
				rwlLock.setValue(666);
				System.out.println("改写为666");
			}
		};
		//读18次
		for(int i=0;i<18;i++) {
			eService.submit(read);
		}
		//写2次
		for(int i=0;i<2;i++) {
			eService.submit(write);
		}
		//读18次
		for(int i=0;i<18;i++) {
			eService.submit(read);
		}
		eService.shutdown();
	}
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值