javaSE-day12(多线程)

发布时间 2023-03-22 21:09:29作者: 王小子

1.多线程的常用方法
Thread提供了很多与线程操作相关的方法

方法:
public void run(): 线程的任务方法
public void start(): 启动线程
public String getName(): 获取当前线程的名称,线程名称默认是Thread-索引
public void setName(String name): 为线程设置名称
public static Thread currentThread(): 获取当前执行的线程对象
public static void sleep(long time): 让当前执行的线程休眠多少毫秒后,在继续执行
public final void join()。。。: 让当前调用这个方法的线程先执行完。
piblic static yied(): 让出CPU执行权,其他线程执行
常见构造器:
public Thread(String name):可以为当前线程指定名称
public Thread(Runnable target):封装Runnable对象为线程对象
public Thread(Runnable target,String name):封装Runnable对象为线程对象并指定线程名称

2.线程安全问题
线程安全问题指的是:
多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。(同一个对象中的成员变量)
锁对象随便选择一个唯一的对象不行,会影响到其他无关线程的执行。
锁对象的使用规范:

  1. 建议使用共享资源作为锁对象,对于实例方法建议使用this作为对象。
  2. 对于静态方法建议使用字节码(类名.class)对象作为锁对象。

synchronized(同步锁)
同步方法,和同步代码块的线程等待位置不同。

java的线程调用是抢占式调用
	Lock比synchronized更灵活
	tryLock():尝试是否能获取锁对象,如果获得,就进入执行
	如果不能,去执行其他代码,不会死等
	Lock是java(内存)层面的锁:通过判断一个变量(0,1)值看是否上锁
	synchronized:通过系统,进入阻塞状态,锁被释放,还需要系统将其唤醒。(两次)
	jdk6之后synchronized升级了:
		偏向锁:
			是同步方法,只有一条线程执行时,线程偏向第一个获得锁对象的线程,
			下次再进入同步时,无需获得和释放锁对象。
			如果有其他线程竞争锁资源,锁会自动升级成轻量级锁
		轻量级锁:
		当线程竞争不大的时候,没有获得锁对象的线程,会自适应自旋(循环)
		如果还是等不到锁对象,则自动升级为重量级锁
		重量级锁:
		通过系统,进入阻塞状态,锁被释放,还需要系统将其唤醒。
		(上面锁升级过程,整个过程不可逆)
	lock:
		公平锁:新线程会排队,排在阻塞队列的其他线程任务后面等待
		非公平锁:新线程会插队,不会进入阻塞队列,而是同阻塞队列中出现的线程任务一同竞争锁资源。
	乐观锁:(不加锁)判断旧值和新值(数据库加修改数据,版本号或最后修改时间)
			有ABA问题 100->101->100,这是后就不能比旧值,比版本号或时间(这两个一旦被记录就不能改回去)。
	悲观锁:直接加锁

3.线程通信
当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以相互协调,避免无效的资源争夺。
生产者消费者模型:
生产者线程负责生产数据
消费者线程负责消费生产者生产的数据。
注意生产者生产完数据应该等待自己,通知消费者消费;消费者消费完数据也应该等待自己,再通知生产者生产!如下:

public class Demo03 {
	public static void main(String[] args) {
		//创建包子对象
		BaoZi bz = new BaoZi();
		BaoZiPu bzp = new BaoZiPu(bz);//通过构造方法传对象
		bzp.setName("灌汤煎包");
		bzp.start();
		ChiHuo chiHuo = new ChiHuo(bz);
		chiHuo.setName("lisa");
		chiHuo.start();
	}
}
class BaoZi{
	private String xian;
	private boolean flag;

	public String getXian() {
		return xian;
	}

	public void setXian(String xian) {
		this.xian = xian;
	}

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}

	public BaoZi() {
	}

	public BaoZi(String xian, boolean flag) {
		this.xian = xian;
		this.flag = flag;
	}
}
class ChiHuo extends Thread{
	private BaoZi bz;
	public ChiHuo(BaoZi bz){this.bz = bz;}
	public void run(){
		while (true){
			synchronized (bz){
				//没有包子
				if (bz.isFlag()==false){
					try {
						bz.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//有包子,就吃
				System.out.println(getName()+"正在吃"+bz.getXian()+"的包子");
				bz.setFlag(false);//吃完
				//唤醒包子铺线程
				bz.notifyAll();//所有

			}
		}
	}
}
class BaoZiPu extends Thread{
	private BaoZi bz;
	public BaoZiPu(BaoZi bz){
		this.bz=bz;
	}
	public void run(){
		while (true){
			synchronized (bz){
				//没有包子
				if (bz.isFlag()==true){
					try {
						bz.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//没包子,就做
				bz.setXian("三鲜");
				System.out.println(getName()+"正在制作"+bz.getXian()+"的包子");
				bz.setFlag(true);//做完改变包子的状态
				//唤醒chihuo线程
				bz.notifyAll();//所有

			}
		}
	}
}

4.线程池
线程池是一个可以复用线程的技术。
不使用线程池的问题:
用户每发起一个请求后台就需要创建一个新的线程来处理。
而创建新线程的开销是很大的,并且请求过多的时候,肯定会产生大量的线程出来,会严重影响系统的性能。
作用:
1.控制线程数量
2.提高用户响应速度
3.降低系统资源消耗(一次性创建,用完不销毁,而是归还到线程池)

ThreadPoolExecutor参数:
		int corePoolSize,    --  核心线程数
		int maximumPoolSize,   -- 最大线程数(正式工+临时工)
		long keepAliveTime,     --临时线程存活时间
		TimeUnit unit,           -- 时间单位
		BlockingQueue<Runnable> workQueue    --阻塞队列,阻塞队列可以存放Integer.MAX_VALUE个线程任务,可能造成内存溢出
		ThreadFactory threadFactory,         --创建线程的方式
		RejectedExecutionHandler handler     --拒绝策略
			ThreadPoolExecutor.CallerRunsPolicy():绕过线程池,由提交线程任务的线程执行。

		t.shtdown()关闭线程池
		t.sumbit()提交线程任务
		核心线程不够用,阻塞队列满了,才会创建临时线程,当都无法及时处理才会执行拒绝策略(main求出了1-600的和是:179700 :main线程执行)

JDK 提供的线程池:

newScheduledThreadPool
	延迟执行线程池
	让线程任务可以延迟执行,还可以循环执行
	也是相当于无边界
newCachedThreadPool
	无边界线程池
	无法做到对线程数量可控
newFixedThreadPool
	有边界线程池
	指定线程数量
	阻塞队列可以存放Integer.MAX_VALUE个线程任务,可能造成内存溢出
newSingleThreadExecutor
	单例线程池
	用于执行需要有序执行的线程任务
	阻塞队列可以存放Integer.MAX_VALUE个线程任务,可能造成内存溢出

5.线程状态

NEW
	创建线程对象时的状态
RUNNABLE
	就绪状态
	有锁,但是等待CPU执行权
BLOCKING
	锁阻塞状态
	有CPU执行权,但是没有锁对象
WAITING
	无限等待状态
	线程调用wait()方法
	只有其它线程调用notify()方法才能将其唤醒
		唤醒,无锁
			锁阻塞状态
		唤醒,有锁
			就绪状态
TIMED_WAITING
	计时等待
	线程调用sleep()和wait(time)方法
	时间到了自动醒来
		有锁
			就绪状态
		无锁
			锁阻塞状态
TERMINATED
	死亡状态
	run()方法执行完毕
	stop()停止线程