Java--多线程(下)

一、死锁*

1.1 概述

  • 当第一个线程拥有A对象锁标记,并等待B对象锁标记,同时第二个线程拥有B对象锁标记,并等待A对象锁标记时,产生死锁。
  • 一个线程可以同时拥有多个对象的锁标记,当线程阻塞时,不会释放已经拥有的钡怀记,由此可能造成死锁。

1.2 代码实现

public class Demo01 {
    public static void main(String[] args) {
        /**
         *  创建类
         *      测试类
         *      锁对象
         *          存储两个锁
         *      线程BB
         *      线程CC
         */
        // 创建创建
        ThreadBB bb = new ThreadBB();
        ThreadCC cc = new ThreadCC();
        
        // 开启线程
        bb.start();
        cc.start();
        
    }
}

class LockObject {
    public static final Object LOCK_A = new Object();
    public static final Object LOCK_B = new Object();
}

class ThreadBB extends Thread {
    @Override
    public void run() {
        synchronized (LockObject.LOCK_A) {
            System.out.println("BB获取到第一根筷子LOCK_A,正在尝试获取第二根筷子LOCK_B");
            synchronized (LockObject.LOCK_B) {
                System.out.println("BB获取到筷子LOCK_A和筷子LOCK_B,可以开动啦");
            }
        }
    }
}

class ThreadCC extends Thread {
    @Override
    public void run() {
        synchronized (LockObject.LOCK_B) {
            System.out.println("CC获取到第一根筷子LOCK_B,正在尝试获取第二根筷子LOCK_A");
            synchronized (LockObject.LOCK_A) {
                System.out.println("CC获取到筷子LOCK_A和筷子LOCK_B,可以开动啦");
            }
        }
    }
}

二、生产者和消费者

2.1 概述

  • 若干个生产者在生产产品,这些产品将提供给若干个消费者去消费
  • 为了使生产者和消费者能并发执行,在两者之间设置一个能存储多个产品的缓冲区
  • 生产者将生产的产品放入缓冲区中,消费者从缓冲区中取走产品进行消费
  • 显然生产者和消费者之间必须保持同步
    • 即不允许消费者到一个空的缓冲区中取产品
    • 也不允许生产者向一个满的缓冲区中放入产品。

2.2 第一版

  • 线程安全问题
  • 顺序问题
public class Demo01 {
    public static void main(String[] args) {
        /**
         * 测试类
         * 
         * 线程BB
         * 线程CC
         */
        // 创建对象
        Card card = new Card();
        ThreadBB bb = new ThreadBB(card);
        ThreadCC cc = new ThreadCC(card);
        
        // 开启线程
        bb.start();
        cc.start();
    }
}


class Card {
    private int money = 0;

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
    
}

class ThreadBB extends Thread {
    
    private Card card;
    
    public ThreadBB(Card card) {
        super();
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            card.setMoney(card.getMoney()-5000);
            System.out.println("BB取出了5000,余额" + card.getMoney());
        }
    }
}

class ThreadCC extends Thread {
    
    private Card card;
    
    public ThreadCC(Card card) {
        super();
        this.card = card;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            card.setMoney(card.getMoney()+5000);
            System.out.println("CC存入了5000,余额" + card.getMoney());
        }
    }
}

2.3 第二版

  • 线程之间无通讯,使用i–的方式处理无法执行的线程
public class Demo01 {
    public static void main(String[] args) {
        /**
         * 测试类
         * 
         * 线程BB
         * 线程CC
         */
        // 创建对象
        Card card = new Card();
        ThreadBB bb = new ThreadBB(card);
        ThreadCC cc = new ThreadCC(card);
        
        // 开启线程
        bb.start();
        cc.start();
    }
}


class Card {
    private int money = 0;

    public int getMoney() {
        return money;
    }

    public void setMoney(int money) {
        this.money = money;
    }
    
}

class ThreadBB extends Thread {
    
    private Card card;
    
    public ThreadBB(Card card) {
        super();
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            synchronized (Card.class) {
                if (card.getMoney() <= 0) {
                    i--;
                    continue;
                }
                card.setMoney(card.getMoney()-5000);
                System.out.println("BB取出了5000,余额" + card.getMoney());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

class ThreadCC extends Thread {
    
    private Card card;
    
    public ThreadCC(Card card) {
        super();
        this.card = card;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            if (card.getMoney() > 0) {
                i--;
                continue;
            }
            synchronized (Card.class) {
                card.setMoney(card.getMoney()+5000);
                System.out.println("CC存入了5000,余额" + card.getMoney());
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

2.4 第三版

  • 采用线程通讯方式交替执行
public class Demo01 {
    public static void main(String[] args) {
        /**
         * 测试类
         * 
         * 线程BB
         * 线程CC
         */
        // 创建对象
        Card card = new Card();
        ThreadBB bb = new ThreadBB(card);
        ThreadCC cc = new ThreadCC(card);
        
        // 开启线程
        bb.start();
        cc.start();
    }
}


class Card {
    private int money = 0;

    /**
     *  存款方法
     *      CC操作存款方法
     *      如果money>0,CC线程等待,通知BB线程执行
     *      如果money<=0,CC线程执行
     */
    public synchronized void saveMoney() {
        if (money > 0) {
            try {
                // money>0,线程等待
                this.wait();
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        // 如果代码执行到这里说明money<=0
        money += 5000;
        System.out.println("CC存入了5000,余额" + money);
        // 已经存入,通知BB执行
        this.notify();
    }
    
    /**
     * 取款方法
     *      BB线程执行
     *      如果money>0,BB线程执行
     *      如果money<=0,BB线程等待,通知CC线程执行
     */
    public synchronized void takeMoney() {
        // 判定是否有余额
        if (money <= 0) {
            // 卡里空了
            try {
                this.wait();
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
        // 如果代码执行到这里,money>0
        money -= 5000;
        System.out.println("BB取出了5000,余额" + money);
        // 已经取出,通知CC执行
        this.notify();
    }
    
}

class ThreadBB extends Thread {
    
    private Card card;
    
    public ThreadBB(Card card) {
        super();
        this.card = card;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            card.takeMoney();
        }
    }
}

class ThreadCC extends Thread {
    
    private Card card;
    
    public ThreadCC(Card card) {
        super();
        this.card = card;
    }
    
    @Override
    public void run() {
        for (int i = 1; i <= 12; i++) {
            card.saveMoney();
        }
    }
}

三、线程池

3.1 概述

  • 线程的创建和销毁会占用大量的资源
    • 内存
    • 处理器
  • 可以限制线程创建的数量,复用创建的线程,把线程放入一个容器

3.2 线程池入门

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Demo02 {
    public static void main(String[] args) throws InterruptedException {
        // 创建线程池对象
        ExecutorService es = Executors.newFixedThreadPool(2);
        
        // 提交任务
        es.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>" + i);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        
        es.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>" + i);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        
        es.submit(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println(Thread.currentThread().getName() + "==>" + i);
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        
        // 启用顺序关闭:运行结束的线程退出任务
        es.shutdown();
        System.out.println(es);
        
        Thread.sleep(3000);
        System.out.println(es);
    }
}

3.3 线程池API

Executor
  • 线程池的顶层接口
  • 执行已提交的 Runnable 任务的对象。
ExecutorService
  • 声明的线程池对象类型
  • 有子类ThreadPoolExecutor
 boolean isTerminated() 
          如果关闭后所有任务都已完成,则返回 truevoid shutdown() 
          启动一次顺序关闭,执行以前提交的任务,但不接受新任务。 
<T> Future<T>  submit(Callable<T> task) 
          提交一个返回值的任务用于执行,返回一个表示任务结果的 FutureFuture<?> submit(Runnable task) 
          提交一个 Runnable 任务用于执行,并返回一个表示该任务的 Future
Executors
  • Executors 类为这些 Executor 提供了便捷的工厂方法。
  • 创建线程池对象
static ExecutorService newCachedThreadPool() 
          创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。 
static ExecutorService newFixedThreadPool(int nThreads) 
          创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。 
static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 
          创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。 
static ExecutorService newSingleThreadExecutor() 
          创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
Callable
  • 返回结果并且可能抛出异常的任务。实现者定义了一个不带任何参数的叫做 call 的方法。
  • Callable 接口类似于 Runnable,两者都是为那些其实例可能被另一个线程执行的类设计的。
  • 但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
Future
  • Future 表示异步计算的结果。
  • 它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。
  • 计算完成后只能使用 get 方法来获取结果
    • 如有必要,计算完成前可以阻塞此方法。

3.4 多线程计算数组总和【掌握】

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Demo06 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        /**
         * 有一个二维数组,长度是4,使用多线程计算所有元素的总和【考题】
         */
        // 创建数组
        int[][] arr = {
                {12,565,98,2,4,67,8,322,657,97,656},
                {9,766,5,456,67,8,78,8,34,343,37632},
                {42314,7,3,76,1356763,45,85,464},
                {7633,24,876432,8,7434,5765,323,46,5}
        };
        // 创建线程池
        ExecutorService es = Executors.newFixedThreadPool(4);
        
        // 提交任务
        Future<Integer> future00 = es.submit(new CalculatCallable(arr[0]));
        Future<Integer> future01 = es.submit(new CalculatCallable(arr[1]));
        Future<Integer> future02 = es.submit(new CalculatCallable(arr[2]));
        Future<Integer> future03 = es.submit(new CalculatCallable(arr[3]));
        
        // 启动顺序关闭
        es.shutdown();
        
        // 输出计算结果
        System.out.println(future00.get() + future01.get() + future02.get() + future03.get());
        System.out.println(39406+2488+1399757+897670);
    }
}

/**
 * 创建任务线程类
 *  计算指定数组中所有元素的总和
 */
class CalculatCallable implements Callable<Integer>{
    
    private int[] arr;
    
    public CalculatCallable(int[] arr) {
        super();
        this.arr = arr;
    }

    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        return sum;
    }
}

四、Lock

4.1 概述

  • Lock 提供了比使用 synchronized 方法和语句更广泛的锁定操作。
    • 能实现synchronized的所有参数
    • 更加的灵活、高效

4.2 核心方法

lock
	锁定
unLock
	解锁,必须执行,放在finally代码块中执行

4.3 ReentrantLock

  • 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
  • 可重入
    • 可以多次执行
  • 互斥
    • A线程执行的时候B线程阻塞
    • B线程执行的时候A线程阻塞
  • 卖票
import java.util.concurrent.locks.ReentrantLock;

public class Demo01 {
	public static void main(String[] args) {
		/**
		 *	火车站四个窗口,共同卖出100张车票,用多线程实现。
		 */
		// 创建线程
		TicketThread t01 = new TicketThread();
		TicketThread t02 = new TicketThread();
		TicketThread t03 = new TicketThread();
		TicketThread t04 = new TicketThread();
		
		// 设置名字
		t01.setName("一号窗口");
		t02.setName("22号窗口");
		t03.setName("叁号窗口");
		t04.setName("④号窗口");
		
		// 启动线程
		t01.start();
		t02.start();
		t03.start();
		t04.start();
		
	}
}

class TicketThread extends Thread {
	private static int ticket = 100;
	// 创建互斥锁
	private static ReentrantLock lock = new ReentrantLock();
	@Override
	public void run() {
		while (true) {
			// 上锁
			lock.lock();
			try {
				if (ticket <= 0) {
					break;
				}
				ticket--;
				System.out.println(Thread.currentThread().getName() + "卖出了第" + (100 - ticket) + "张票,还剩下" + ticket);
			} finally {
				// 解锁
				lock.unlock();
			}
		}
	}
}
  • 数组
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;

public class Demo02 {
	public static void main(String[] args) throws InterruptedException {
		/**
		 *	创建类
		 *		测试类
		 *		数组资源类
		 *			数组
		 *			索引
		 *		线程A	
		 *			存入Hello
		 *		线程B
		 *			存入World
		 */
		// 创建数组和索引
		ArrSrc arrSrc = new ArrSrc();
		// 创建线程A和B
		ThreadA a = new ThreadA(arrSrc);
		ThreadB b = new ThreadB(arrSrc);
		
		// 启动线程修改数组
		a.start();
		b.start();
		
		// main线程休眠,等待线程a和b执行结束之后再看修改之后的结果
		Thread.sleep(10);
		// 输出修改之后的数组
		System.out.println(arrSrc);
	}
}


/**
 *	数组资源
 */
class ArrSrc {
	String[] arr = new String[5];
	int index = 0;
	private static ReentrantLock lock = new ReentrantLock();
	
	/**
	 * 修改数组的方法
	 * 	修改数组数据
	 * 	索引自增
	 * @param str
	 */
	public void modifyArr(String str) {
		lock.lock();
		try {
			arr[index] = str;
			index++;
		} finally {
			lock.unlock();
		}
	}

	@Override
	public String toString() {
		return "ArrSrc [arr=" + Arrays.toString(arr) + ", index=" + index + "]";
	}

}

class ThreadA extends Thread {
	private ArrSrc arrSrc;
	
	public ThreadA(ArrSrc arrSrc) {
		super();
		this.arrSrc = arrSrc;
	}

	@Override
	public void run() {
		arrSrc.modifyArr("Hello");
	}
}

class ThreadB extends Thread {
	private ArrSrc arrSrc;
	
	public ThreadB(ArrSrc arrSrc) {
		super();
		this.arrSrc = arrSrc;
	}

	@Override
	public void run() {
		arrSrc.modifyArr("World");
	}
}

4.4 读写锁

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

public class Demo03 {
	public static void main(String[] args) {
		
		MyClass mc = new MyClass();
		long startTime = System.currentTimeMillis();
		
		// 创建线程池,容量20【18读 + 2写】
		ExecutorService es = Executors.newFixedThreadPool(20);
		
		// 提交任务:读取数据*18
		for (int i = 0; i < 18; i++) {
			es.submit(new Runnable() {
				@Override
				public void run() {
					mc.getValue();
				}
			});
		}
		
		// 提交任务:写入数据*2
		for (int i = 0; i < 2; i++) {
			es.submit(new Runnable() {
				@Override
				public void run() {
					mc.setValue("Hello");
				}
			});
		}
		es.shutdown();
		
		// 阻塞:判定线程池中任务是否全部完成
		while (!es.isTerminated()) {}
		long endTime = System.currentTimeMillis();
		System.out.println(endTime - startTime);
		
	}
}

class MyClass {
	// 创建互斥锁
	private ReentrantLock lock = new ReentrantLock();
	// 创建读写锁
	private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
	// 获取读锁
	ReadLock readLock = readWriteLock.readLock();
	// 获取写锁
	WriteLock writeLock = readWriteLock.writeLock();
	
	private String value;

	/**
	 * 读取数据
	 * @return
	 */
	public String getValue() {
		// lock.lock();
		readLock.lock();
		try {
			Thread.sleep(1000);
			return value;
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			// lock.unlock();
			readLock.unlock();
		}
		return value;
	}

	/**
	 * 写入数据
	 * @param value
	 */
	public void setValue(String value) {
		//lock.lock();
		writeLock.lock();
		try {
			this.value = value;
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			//lock.unlock();
			writeLock.unlock();
		}
	}
	
}
  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值