JavaSE学习笔记 Day22

发布时间:2023年12月21日

JavaSE学习笔记 Day22

个人整理非商业用途,欢迎探讨与指正!!
? 上一篇



···

18.7线程的等待状态

初始状态:
就绪状态:
运行状态:
等待状态:
?限期等待:有时间的等待,Thread.sleep() 或者 join(非0) 等待时间到就回到就绪状态
?无限期等待:一直等待,join(0)或者join() 条件满足之后才会回到就绪状态
终止状态:

18.8线程安全

共享的资源(同一个对象),一次只能被一个线程所访问,可以保证我们的数据准确性

18.8.1线程同步的实现

同步代码块

public class Test01 {

	public static void main(String[] args) {
//		创建的对象
		TicketRunnable tr = new TicketRunnable();
		
//		创建多个线程去访问同一个资源
		Thread t1 = new Thread(tr,"窗口1");
		Thread t2 = new Thread(tr,"窗口2");
		Thread t3 = new Thread(tr,"窗口3");
		Thread t4 = new Thread(tr,"窗口4");
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class TicketRunnable implements Runnable{
	int ticket = 100;
	final Object obj = new Object();
	
	@Override
	public void run() {
		while(true) {
//			使用同步代码块
			synchronized (obj) {//互斥锁对象(可以是任意的java对象,但是要保证是唯一的对象)
//				死循环需要程序的出口
				if(ticket < 0) {
					break;
				}
//				200毫秒买票一张
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
//				进行放票操作
				System.out.println(Thread.currentThread().getName()+"卖出了"+ ticket-- +"号票");
			}
		}
	}
}

同步方法

public class Test02 {

	public static void main(String[] args) {
		//		创建的对象
		TicketRunnable1 tr = new TicketRunnable1();

		//		创建多个线程去访问同一个资源
		Thread t1 = new Thread(tr,"窗口1");
		Thread t2 = new Thread(tr,"窗口2");
		Thread t3 = new Thread(tr,"窗口3");
		Thread t4 = new Thread(tr,"窗口4");

		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}
}

class TicketRunnable1 implements Runnable{
	int ticket = 100;

//	同步的方法,run被同步了,不是某个资源了
	@Override
	public void run() {
		while(true) {
			sale();
		}
	}
//	锁定的方法是sale
	public synchronized void sale() {
	//		死循环需要程序的出口
		if(ticket < 0) {
			System.out.println("票买完了");
//			System.exit(0);//停止JVM
//			return;
			throw new RuntimeException("没票了");
		}
		//				200毫秒买票一张
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	
		//				进行放票操作
		System.out.println(Thread.currentThread().getName()+"卖出了"+ ticket-- +"号票");
	}
}

18.9线程的阻塞状态

线程的生命周期:
?初始状态:
?就绪状态:
?运行状态:
?等待状态:
? 阻塞状态:调用使用synchronized修饰的方法,就进入了阻塞状态,释放锁时从阻塞状态进入到就绪状态
?终止状态:
线程的五大状态:初始状态—>就绪状态—>运行状态—>阻塞/等待状态—>终止状态

18.10线程死锁

当第一个线程拥有A对象并锁定,等待B对象.同时第二线程拥有B对象并锁定,等待A对象
死锁在程序的运行中发生的概率几乎为0

public class Test03 {

	public static void main(String[] args) {
		BigLeft bl = new BigLeft();
		BigRight br = new BigRight();
		bl.start();
		br.start();
	}
}

class MyLock {
	static Object objA = new Object();//A对象
	static Object objB = new Object();//B对象
}

class BigLeft extends Thread {
	@Override
	public void run() {
		synchronized (MyLock.objA) {
			System.out.println("大左拿到了对象A");
			synchronized (MyLock.objB) {
				System.out.println("大左在等待对象B");
			}
		}
	}
}
class BigRight extends Thread {
	@Override
	public void run() {
		synchronized (MyLock.objB) {
			System.out.println("大右拿到了对象B");
			synchronized (MyLock.objA) {
				System.out.println("大左在等待对象A");
			}
		}
	}
}

18.11线程通信(理解)

18.11.1生产者和消费者

若干个生产者可以生成产品,将生成的产品提供给若干的消费者去消费。生产者和消费者是并发执行的,在两者之间设置一个可以存储多个的产品缓冲区,用于将生产者的产品放入到缓冲中,消费者将缓冲中的产品进行消费,需要保持生产者和消费者之间的一个同步
需要使用的Object中的方法:wait(),notify(),notifyAll()
提示:Object中的等待,唤醒方法都必须在同步的代码块中

package com.qf.test03;

public class Test01 {

//	共享的商场
	static Shop shop = new Shop();
	
	public static void main(String[] args) {
		ConThread ct = new ConThread();
		PhoneThread pt = new PhoneThread();
		ct.start();
		pt.start();
	}
}

/**
 * 商品 
 * */
class Phone {
	String name;
}
/**
 * 缓冲区 
 * */
class Shop {
//	表示买卖的商品 若为null需要进行生成 若不null需要进行出售
	Phone phone;
//	进货(生产)
	public synchronized void putPhone(Phone phone) {//参数表示进货的商品
//		有货
		if(this.phone != null) {
			try {
				System.out.println("当前有货,需要等待消费");
				this.wait();//等待消费
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
//		表示没有货
		System.out.println("正在生产手机:"+phone.name);
//		模拟生产者
		this.phone = phone;
//		唤醒其他线程 通知消费者
		this.notify();
	}
	
//	卖货(消费)
	public synchronized void getPhone() {
//		没有货,需要提醒生产
		if(this.phone == null) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
//		表示要消费了
		System.out.println("正在消费:"+this.phone.name);
		
//		模拟消费
		this.phone = null;
//		唤醒其他线程 通知生产者
		this.notify();
	}
}

/**
 * 消费者
 * */
class ConThread extends Thread {
	@Override
	public void run() {
		for(int i = 1;i<=10;i++) {
//			消费的操作
			Test01.shop.getPhone();
		}
	}
}
/**
 * 生成者
 * */
class PhoneThread extends Thread {
	@Override
	public void run() {
		for(int i = 1;i<=10;i++) {
			Phone phone = new Phone();
			phone.name = "IPhone" + i;
//			生成操作
			Test01.shop.putPhone(phone);
		}
	}
}

18.12本地线程

可以完成数据的共享
数据共享:
?1.使用synchronized进行代码的同步
?2.使用本地线程ThreadLocal

public class Test01 {

	public static void main(String[] args) {
//		本地线程中的数据可以完成共享,每次取出都是同一个数据
		ThreadLocal<Dog> t = new ThreadLocal<>();
//		添加方法
		t.set(new Dog());
//		获取方法 无论取多少次,都是同一个对象
		Dog dog1 = t.get();
		Dog dog2 = t.get();
		Dog dog3 = t.get();
//		移除方法
		t.remove();
		Dog dog4 = t.get();
//		再添加
		t.set(new Dog());
		Dog dog5 = t.get();
		Dog dog6 = t.get();
		Dog dog7 = t.get();
		
		System.out.println(dog1 == dog2);
		System.out.println(dog2 == dog3);
		System.out.println(dog4);
		System.out.println(dog1 == dog4);
		
		System.out.println(dog1 == dog5);
		System.out.println(dog5 == dog6);
		System.out.println(dog7 == dog6);
	}
}

class Dog {
	
}

18.13线程池

若有非常多的任务需要使用线程来完成,并且线程的使用时间都不是很长,这样频繁的去创建和销毁线程会比较销毁性能,可以使用线程池来完成这样的任务,线程池可以完成线程的重用
线程中有一个线程队列,队列中的每个线程都是处于空闲状态,每次就不用重新创建了

18.13.1线程池的常见类和接口

Executor:线程池的顶级接口
ExecutorSevice:线程池接口,可通过summit()提交任务代码
Executors工厂类:通过此类去创建一个线程池对象
?newFixedThreadPool(int nThreads):获取固定数量的线程池,参数就是线程的个数
?newCechedThreadPool():获取动态数量的线程池,若不够就自行的创建,无上限

public class Test01 {

	public static void main(String[] args) {
//		1.获取固定个数的线程池,个数为3
		ExecutorService es = Executors.newFixedThreadPool(3);
//		2.提交线程池需要帮助我们完成的任务(Runnable的实现类),submit方法可以自动的执行run
		es.submit(new A());
		es.submit(new A());
		es.submit(new A());
//		先执行完前三个线程的任务,执行完毕后再进行第四个的执行
		es.submit(new A());
//		3.关闭线程池:当所有的资源都执行完毕时,关闭线程池
		es.shutdown();
	}
}

//	创建线程任务类
class A implements Runnable {

	@Override
	public void run() {
		for(int i = 1;i<=10;i++) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread()+":"+i);
		}
	}
}
18.13.2Callable接口

JDK1.5后加入的,与Runnable接口类似,实现后表示一个线程任务
Callable具有泛型,可以声明异常

public class Test02 {

	public static void main(String[] args) {
//		可以任意添加任务,无需设定线程的数量
		ExecutorService es = Executors.newCachedThreadPool();
		es.submit(new B());
		es.submit(new B());
		es.submit(new B());
		es.submit(new B());
		es.shutdown();
	}
}

//	创建线程任务类
class B implements Callable<String> {//此泛型规定了方法的返回值

	@Override
	public String call() throws Exception {
		for(int i = 1;i<=100;i++) {
			Thread.sleep(1000);
			System.out.println(Thread.currentThread()+":"+i);
		}
		
		return null;
	}
}
18.13.3Future接口

表示将要执行完的任务的结果
get()方法以阻塞的形式,等待Future中异步处理结果(call的返回值)

public class Test03 {

	public static void main(String[] args) throws Exception {
//		可以任意添加任务,无需设定线程的数量
		ExecutorService es = Executors.newCachedThreadPool();
		
		Future<Integer> submit = es.submit(new C());
		System.out.println(submit.get());
		
		es.shutdown();
	}
}

//	创建线程任务类
class C implements Callable<Integer> {//此泛型规定了方法的返回值

	@Override
	public Integer call() throws Exception {
		int sum = 0;
		for(int i = 1;i<=100;i++) {
			sum += i;
		}
		return sum;
	}
}
public class Test04 {

//	计算1~1000000的和 使用4个任务去完成 
//	任务1:1~250000 任务2:250001~500000 任务3:500001~750000 任务4:750001~1000000
	public static void main(String[] args) throws Exception {
		long start = System.currentTimeMillis();
//		创建线程池对象
		ExecutorService es = Executors.newCachedThreadPool();
		Future<Integer> f1 = es.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 1;i<=250000;i++) {
					sum += i;
				}
				return sum;
			}
		});
		Future<Integer> f2 = es.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 250001;i<=500000;i++) {
					sum += i;
				}
				return sum;
			}
		});
		Future<Integer> f3 = es.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 500001;i<=750000;i++) {
					sum += i;
				}
				return sum;
			}
		});
		Future<Integer> f4 = es.submit(new Callable<Integer>() {
			@Override
			public Integer call() throws Exception {
				int sum = 0;
				for(int i = 750001;i<=1000000;i++) {
					sum += i;
				}
				return sum;
			}
		});
		
		System.out.println(f1.get() + f2.get() + f3.get() + f4.get());
		
		es.shutdown();
		long end = System.currentTimeMillis();
		System.out.println(end - start);
	}
}

18.14Lock锁

在JDK1.5后加入,与synchronized比较,显示定义,结构更加的灵活

public class Test01 {

	public static void main(String[] args) {
		ExecutorService es = Executors.newCachedThreadPool();
		TicketRun run = new TicketRun();
		es.submit(run);
		es.submit(run);
		es.submit(run);
		es.shutdown();
	}
}

class TicketRun implements Runnable {
	int ticket = 100;
//	创建Lock对象
//	互斥锁和synchronized 功能一样
	Lock lock = new ReentrantLock();

	@Override
	public void run() {
		while(true) {
			try {
//				上锁(添加锁)
				lock.lock();
				if(ticket < 0) {
					break;
				}
				System.out.println(Thread.currentThread()+"--->卖出了"+ ticket-- +"号票");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
				//开锁(释放锁) 无论如何都要进行释放
				lock.unlock();
			}
		}
	}
}

公平锁:
?让每个线程都公平的去执行当前的任务
非公平锁:
?优先让上一个线程去完成接下来的任务
synchronized:非公平锁,互斥锁,同一个时刻只允许一个线程只有锁,当有一个线程持有锁,其他线程都会进入到阻塞状态直到锁被释放
// 参数为true时公平锁 false时非公平锁 默认为非公平的
Lock lock = new ReentrantLock(true/false);

文章来源:https://blog.csdn.net/CHiN_951/article/details/135115825
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。