9.多线程

09多线程

1.高可用
2.高性能
3.高并发

多任务开启多线程----多任务是多线程的出发点----并行的路径

  1. thread启动的第一种方法
 class PrimeThread extends Thread {
         long minPrime;
         PrimeThread(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 
--------------------------------------------------------------------------------

然后,以下代码将创建一个线程并启动它运行: 

     PrimeThread p = new PrimeThread(143);
     p.start();

  1. thread启动的第二种方法
   class PrimeRun implements Runnable {
         long minPrime;
         PrimeRun(long minPrime) {
             this.minPrime = minPrime;
         }

         public void run() {
             // compute primes larger than minPrime
              . . .
         }
     }
 
--------------------------------------------------------------------------------

然后,以下代码将创建一个线程并启动它运行: 

     PrimeRun p = new PrimeRun(143);
     new Thread(p).start();

原理图:
在这里插入图片描述
提出问题,如果for循环写在在线程开辟之前,而且线程后没有代码,那么将会将for内的内容全部执行完之后再进行线程,这就是一个线性执行顺序,但是放在之后则会进行多线程
代码:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 *开始多线程
 *创建多线程方式一:
 *1.创建:继承thread+重写run
 *2.创建子类对象+start
 */
public class StartThread extends Thread{
	//线程入口
	@Override
		public void run() {
			for (int i = 0; i < 200; i++) {
				System.out.println("一边听歌");
			}
		}

	public static void main(String[] args) {
		StartThread st = new StartThread();
		st.start();	//不保证立即运行,由cpu调用
			
		//st.run();//普通代码的调用,这时候不管这么运行都只能先听完歌再敲代码
		for (int i = 0; i < 200; i++) {
			System.out.println("一边coding");
		}
		
	}
}

在这里插入图片描述
使用thread完成多个线程对同一对象地操作(抢票操作):

package com.sheye.thread;

class Adder {
    int ticketNum = 10;

    public  void  decrease() { 
    	while(true) {
			if (ticketNum<0) {
				break;
			}
			try {
				//Thread.sleep(200); 加入sleep产生相同值与负数
			} catch (Exception e) {
				e.printStackTrace();
			}
           System.out.println(Thread.currentThread().getName()+"抢到了----还剩:"+ticketNum--);
         }
    }
  }

 public class AdderThread extends Thread{    	
    Adder adder = null;
    public AdderThread(Adder ad){
        this.adder = ad;
     }
     
    public void run() {	  
           adder.decrease();        
     }
 
   public static void main(String[] args) throws InterruptedException{
     Adder ad =new Adder();    
     Thread t1=new AdderThread(ad);
     Thread t2=new AdderThread(ad);
     Thread t3=new AdderThread(ad);
     t1.start();
     t2.start();
     t3.start();
  }
}

运行结果:

Thread-0抢到了----还剩:10
Thread-1抢到了----还剩:9
Thread-0抢到了----还剩:8
Thread-2抢到了----还剩:7
Thread-0抢到了----还剩:5
Thread-0抢到了----还剩:3
Thread-0抢到了----还剩:2
Thread-0抢到了----还剩:1
Thread-0抢到了----还剩:0
Thread-1抢到了----还剩:6
Thread-2抢到了----还剩:4

实现runnable接口:

1.创建目标对象: IDownloader id =new IDownloader(“图片地址”,“baidu.png”);
2.创建线程对象+关联目标对象: Thread t =new Thread(id); //代理对象
3.启动线程: t.start()
4.extends thread也是以上3个步骤

Thread和Runnable进行对比(两者都不能抛出异常而callable可以):

Thread:
•子类继承Thread具备了多线程能力
•启动线程: 子类对象.start()
•不建议使用:避免OOP(面向对象)单继承局限

Runnable:
•实现接口Runnable具有多线程能力
•启动线程: 传入目标对象+Thread对象.start()
•推荐使用: OOP多实现,灵活方便,方便同一份对象的代理。

1.runnable的基本使用

package ThreadStudy01;
/**
 * 
 * @author Sheye
 *开始多线程
 *创建多线程方式二:推荐方式
 *1.创建:实现runnable+重写run
 *2.创建实现类对象+Thread类对象+start
 */
public class StartRunnable implements Runnable{
	//线程入口
	@Override
		public void run() {
			for (int i = 0; i < 200; i++) {
				System.out.println("一边听歌");
			}
		}

	public static void main(String[] args) {
//		//创建实现类对象
//		StartRunnable sr = new StartRunnable();
//		//创建代理对象
//		Thread t = new Thread(sr);
//		//启动
//		t.start();	//不保证立即运行,由cpu调用
		
		new Thread(new StartRunnable()).start();	//对象只使用一次使用匿名
			
		//st.run();//普通代码的调用,这时候不管这么运行都只能先听完歌再敲代码
		for (int i = 0; i < 200; i++) {
			System.out.println("一边coding");
		}
		
	}
}

2.runnable抢票(多个代理同一资源)以及thread.sleep带来的问题:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 *共享资源
 *一份资源,多个代理--》并发
 */
public class Web12306 implements Runnable{
	private int ticketNum=99;
	
	//run方法是不能对外throws异常的
	@Override
	public void run() {
		while(true) {
			if (ticketNum<0) {
				break;
			}
			try {
				Thread.sleep(200);	//加入sleep产生负数
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
		}
	}

	public static void main(String[] args) {
		//一份资源
		Web12306 w = new Web12306();
		//多个代理
		new Thread(w,"线程一").start();
		new Thread(w,"线程二").start();
		new Thread(w,"线程三").start();
		
	}
}

3.龟兔赛跑,两个代理对象抢夺最后一个资源,谁先抢到谁赢:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 * 龟兔赛跑
 */
public class Racer implements Runnable{
	private static String winner;	//胜利者
	
	@Override
	public void run() {
		
		for (int steps = 0; steps <= 100; steps++) {
			//模拟兔子在休息,每走10步休息一下
			if (Thread.currentThread().getName().equals("rabbit") && steps%10==0) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName()+"-->"+steps);
			//比赛是否结束
			boolean flag = gameOver(steps);
			if (flag) {
				break;
			}
		}
	}
	
	private boolean	gameOver(int steps) {
		if (winner!=null) {
			return true;
		}else {
			if(steps==100) {
				winner = Thread.currentThread().getName();
				System.out.println("winner==>"+winner);
				return true;
			}
		}
		return false;
	}
	
	public static void main(String[] args) {
		Racer a = new Racer();
		new Thread(a,"tortoise").start();
		new Thread(a,"rabbit").start();
	}
}

实现callable接口(高级并发juc):

1.创建目标对象: CDownloader cd =new CDownloader(“图片地址”,“baidu.png”);
2.创建执行服务: ExecutorServiceser=Executors.newFixedThreadPool(1);
3.提交执行: Future result1 =ser.submit(cd1) ;
4.获取结果: booleanr1 =result1.get();
5.关闭服务: ser.shutdownNow();

1.callable的基本使用:

package ThreadStudy01;

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;

/**
 * 
 * @author Sheye
 *连接创建线程的方式三:
 *
 */
//callable里面的范型与call方法的返回值相同
public class CallableDownloder implements Callable<Boolean>{

	private String url;	//远程路径
	private String name;	//存储名字
	
	public CallableDownloder(String url, String name) {
		
		this.url = url;
		this.name = name;
	}

	@Override
	public Boolean call() throws Exception{
		WebDownloader wd = new WebDownloader();
		wd.downloader(url, name);
		return true;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//1.创建目标对象:
		CallableDownloder cd1 = new CallableDownloder("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574008683685&di=06ec4addb10f637b42378a8c09389c3c&imgtype=jpg&src=http%3A%2F%2Fimg3.imgtn.bdimg.com%2Fit%2Fu%3D315309498%2C1005774921%26fm%3D214%26gp%3D0.jpg", "dest/1.jpg");
		CallableDownloder cd2 = new CallableDownloder("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574008683352&di=470497985d82a663f6b852a60e778225&imgtype=0&src=http%3A%2F%2Fcdn.applysquare.net%2Fstorage%2Fqa%2FKybiYr7EV%2Fthread%2Fb33N20XC2.jpeg", "dest/2.jpg");
		CallableDownloder cd3 = new CallableDownloder("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1574008736488&di=3ac500ef437b864cf1ffb10ffb16049d&imgtype=jpg&src=http%3A%2F%2Fimg1.imgtn.bdimg.com%2Fit%2Fu%3D4212767701%2C2930177101%26fm%3D214%26gp%3D0.jpg", "dest/3.jpg");
	
		//2.创建执行服务: 
		ExecutorService es=Executors.newFixedThreadPool(3);
		//3.提交执行: 
		Future<Boolean> result1 =es.submit(cd1) ;
		Future<Boolean> result2 =es.submit(cd2) ;
		Future<Boolean> result3 =es.submit(cd3) ;
		//4.获取结果: 
		boolean r1 =result1.get();
		boolean r2 =result2.get();
		boolean r3 =result3.get();
		System.out.println(r3);
		//5.关闭服务: 
		es.shutdownNow();
	}

}

2.用callable实现龟兔赛跑:

package ThreadStudy01;

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;

/**
 * 
 * @author Sheye
 * 龟兔赛跑
 */
public class CallableRacer implements Callable<Integer>{
	private static String winner;	//胜利者
	
	@Override
	public Integer call() throws Exception{
		
		for (int steps = 0; steps <= 100; steps++) {
			//模拟兔子在休息,每走10步休息一下
			if (Thread.currentThread().getName().equals("rabbit") && steps%10==0) {
					Thread.sleep(100);
			}
			System.out.println(Thread.currentThread().getName()+"-->"+steps);
			//比赛是否结束
			boolean flag = gameOver(steps);
			if (flag) {
				return steps;
			}
		}
		return null;
	}
	
	private boolean	gameOver(int steps) {
		if (winner!=null) {
			return true;
		}else {
			if(steps==100) {
				winner = Thread.currentThread().getName();
				System.out.println("winner==>"+winner);
				return true;
			}
		}
		return false;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		CallableRacer a = new CallableRacer();
		
		//2.创建执行服务: 
		ExecutorService es=Executors.newFixedThreadPool(2);
		//3.提交执行: 
		Future<Integer> result1 =es.submit(a) ;
		Future<Integer> result2 =es.submit(a) ;
		
		//4.获取结果: 
		Integer r1 =result1.get();
		Integer r2 =result2.get();
	
		System.out.println(r1+":"+r2);
		//5.关闭服务: 
		es.shutdownNow();
	}
}

静态代理设计模式:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 * 比方:婚庆公司为我筹办婚礼
 *静态代理
 *公共接口(1,2都需要实现相同的接口)
 *1.真实角色
 *2.代理角色
 */
public class StaticProxy {

	public static void main(String[] args) {
		new WeddingCompany(new You()).happyMarry();
	}
}

interface Marry {
	void happyMarry();
}

//真实角色
class You implements Marry{

	@Override
	public void happyMarry() {
		System.out.println("我和java终于在一起了");
	}
	
}

//代理角色
class WeddingCompany implements Marry{

	private Marry targer;
	
	public WeddingCompany(Marry targer) {
		super();
		this.targer = targer;
	}

	@Override
	public void happyMarry() {
		ready();
		this.targer.happyMarry();	//调用you里面happyMarry方法
		after();
	}
	
	public void ready() {
		System.out.println("布置婚庆道具");
	}
	
	public void after() {
		System.out.println("闹洞房");
		}
}

运行结果:

布置婚庆道具
我和java终于在一起了
闹洞房

lambda表达式(jdk8的新特性):

•λ希腊字母表中排序第十一位的字母,英语名称为Lambda,
•避免匿名内部类定义过多
•其实质属于函数式编程的概念

tips:匿名内部类必须借助接口或者父类,没有子类的名称

1,;lambda表达式的推到过程:

//本块有两个java文件

package ThreadStudy01;
/**
 * 
 * @author Sheye
 * lambda表达式简化线程的使用(用一次的线程或者简单的线程)
 */
public class LambdaThread{
	//静态内部类-->好处:Test不使用就不会编译
	//此处要静态方法是main为静态,静态调用静态
	static class Test implements Runnable{
		
		public void run() {
			for (int i = 0; i < 200; i++) {
				System.out.println("一边听歌");
			}
		}
	}
	
	public static void main(String[] args) {
		new Thread(new Test()).start();

	}
}
//以上为第一个java文件
__________________________________________________________________________________________
//以下为第二个java文件
package ThreadStudy01;
/**
 * 
 * @author Sheye
 * lambda表达式简化线程的使用(用一次的线程或者简单的线程)
 */
public class LambdaThread01{
	
	
	public static void main(String[] args) {
		
		//局部内部类
		class Test2 implements Runnable{
			
			public void run() {
				for (int i = 0; i < 200; i++) {
					System.out.println("一边听歌");
				}
			}
		}
		
		new Thread(new Test2()).start();
		
		//匿名内部类必须借助接口或者父类,没有子类的名称
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				for (int i = 0; i < 200; i++) {
					System.out.println("一边听歌");
				}	
			}
		}).start();
		
		//jdk8再进行简化	lambda
		new Thread(()->{
			for (int i = 0; i < 200; i++) {
				System.out.println("一边听歌");
			}	
		}
		).start();
	}
}

2.lambda推导2:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 *lambda推导
 */
public class LambdaTest01 {
	
	//静态内部类
	static class Like2 implements ILike{

		@Override
		public void lambda() {
			System.out.println("i like java2");
			
		}
		
	}

	public static void main(String[] args) {
		//普通方法
		ILike like = new Like();
		like.lambda();
		
		//静态内部类的调用
		like= new Like2();
		like.lambda();
		
		//方法内部类
		class Like1 implements ILike{

			@Override
			public void lambda() {
				System.out.println("i like java3");
				
			}
			
		}
		ILike like1 = new Like1();
		like1.lambda();
		
		//匿名内部类
		//觉得这里的很突兀?下面的代码相当于ILike like = new ILike();
		//但是匿名内部类就是这么使用,不要子类的名字(理解匿名的含义),
		//反正子类也要重写父类的方法(相当于这就是子类),就直接new 父类{重写方法}---->只注重改写的方法
		like = new ILike() {
			
			@Override
			public void lambda() {
				System.out.println("i like java4");
				
			}
		};
		like.lambda();
		
		//lambda
		like = ()->{
			System.out.println("i like java5");
		};
		like.lambda();
	}
}

interface ILike{
	void lambda();	//lambda就只能有一个方法
}

//外部类
class Like implements ILike{

	@Override
	public void lambda() {
		System.out.println("i like java1");
		
	}
	
}


运行结果:

i like java1
i like java2
i like java3
i like java4
i like java5

3.lambda带重写方法带参数的简化:

package ThreadStudy01;
/**
 * 
 * @author Sheye
 *lambda+参数
 */
public class LambdaTest02 {
	
	public static void main(String[] args) {
		ILove love = (int a) ->{
			System.out.println("i Love java1:"+a);
			
		};
		//跟new thread().start不同这里要对象.方法,因为new就是生成对象,所以要对象.方法()
		love.lambda(100);
		
		//简化1:省略参数的数据类型
		love = (a) ->{	
			System.out.println("i Love java1:"+a);
			
		};
		love.lambda(50);
		
		//简化2(只有一个参数的情况):省略括号
		love = a ->{
			System.out.println("i Love java1:"+a);
			
		};
		love.lambda(25);
		
		//简化3(重写的方法里面只有一行代码时);省略花括号
		love = a ->System.out.println("i Love java1:"+a);
		love.lambda(15);
	}
		
}

interface ILove{
	void lambda(int a);	//lambda就只能有一个方法
}

//外部类
class Love implements ILove{

	@Override
	public void lambda(int a) {
		System.out.println("i Love java1:"+a);
		
	}
	
}


运行结果:

i Love java1:100
i Love java1:50
i Love java1:25
i Love java1:15

4.lambda带返回值带参数的简化:

package ThreadStudy01;

public class LambdaThread03 {

	public static void main(String[] args) {
		IInterest interest = (int a1, int a2)->{
			System.out.println("i like lambda:"+a1+":"+a2);
			return a1+a2;
		};
		interest.lambda(100, 200);
		
		//简化1:
		interest = (a1,a2)->{
			System.out.println("i like lambda:"+a1+":"+a2);
			return a1+a2;
		};
		interest.lambda(100, 200);
		
		//简化2:
		//只有一行的情况下,而且一行代码是return一个值
		interest = (a1,a2)->a1+a2;
		
		interest.lambda(100, 200);
		
	}
	
}

interface IInterest{
	int lambda(int a,int b);
}

class Interest implements IInterest{

	@Override
	public int lambda(int a1, int a2) {
		System.out.println("i like lambda:"+a1+":"+a2);
		return a1+a2;
	}
	
	
}

运行结果:

i like lambda:100:200
i like lambda:100:200

5.lambda的运用:

package ThreadStudy01;

public class LambdaThread04 {

	public static void main(String[] args) {
		new Thread(()->{
			for (int i = 0; i < 20; i++) {
				System.out.println("一边吃饭");
			}
		}).start();
	
	new Thread(()->System.out.println("一边听歌")).start();
		
	}
}

线程状态:
在这里插入图片描述
每个线程都有一个自己的工作空间,工作空间与系统的主存进行数据交换
在这里插入图片描述
线程三种状态的示意图:
在这里插入图片描述
线程处于就绪状态的情况:
1.调用start()方法
2.线程解除阻塞
3.调用yield()方法进行让,让出cpu调度,避免一个线程占用资源过多,进行中断,进入就绪
4.JVM本身将cpu从本地线程切换成其他线程

线程处于阻塞状态的情况:
1.调用sleep()方法,抱着资源睡觉,等待指定的时间
2.调用wait()方法,红绿灯,站在一边,不占着资源
3.调用join方法,加入,等待别人使用完cpu,才能进行服务
4.io操作的read(),write(),进行阻塞

线程处于死亡状态的情况:
1.代码正常执行完
2.线程被强制中止,比如stop(),destory()方法

线程的中止:

package ThreadStudy01;
/**
 * 
 * @author lenovo
 *终止线程:
 *1.线程正常执行完毕-->次数
 *2.外部干涉-->加入标识符
 */
public class TerminateThread implements Runnable{
	//加入标识 标记线程体是否可以运行
	private boolean flag = true;
	private String name;

	public TerminateThread(String name) {
		super();
		this.name = name;
	}

	@Override
	public void run() {
		//关联标识,true-->运行,false-->不运行
		int i=0;
		while (flag) {
			System.out.println(name+"-->"+i++);
		}
	}
	
	//对外提供方法改变标识
	public void terminate(){
		this.flag = false;
	}
	
	public static void main(String[] args){
		TerminateThread tt = new TerminateThread("我爱罗");
		new Thread(tt).start();
		
		for (int i = 0; i < 99; i++) {
			if (i==88) {
				tt.terminate(); //线程的中止
				System.out.println("线程已死");
			}
			System.out.println("main-->"+i);
		}	
	}
}

线程的暂停(sleep):
1.sleep模拟网络延时,放大问题发生的可能性
2.sleep为静态方法,可以抛出异常

package com.sheye.stat;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 
 * @author Sheye
 *sleep模拟网络延时,放大问题发生的可能性
 */
public class BlockedSleep03{
		
	public static void main(String[] args) throws InterruptedException {
		//倒计时10秒
		Date endTime = new Date(System.currentTimeMillis()+1000*10);
		long end  = endTime.getTime();
		while (true) {
			System.out.println(new SimpleDateFormat("mm:ss").format(endTime));
			Thread.sleep(1000);
			endTime=new Date(endTime.getTime()-1000);
			if (end-10000==endTime.getTime()) {
				break;
			}
		}
		}
	
	//倒数10个数,1秒一个
	public static void test() throws InterruptedException {
		
		//倒数10个数,1秒一个
		int num=10;
		while (true) {
			Thread.sleep(1000);
			System.out.println(num--);
		}
	}
}



线程礼让yield:

•礼让线程,让当前正在执行线程暂停
•不是阻塞线程,而是将线程从运行状态转入就绪状态
•让cpu调度器重新调度

1.线程之间的礼让:

package com.sheye.stat;

/**
 * yield让出cpu的调度,暂停线程 直接进入就绪状态 不是阻塞状态
 * @author Sheye
 *
 */
public class YieldDemo01 {

	public static void main(String[] args) {
		MyYield my = new MyYield();
		new Thread(my,"a").start();
		new Thread(my,"b").start();
	}
}

class MyYield implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"-->start");
		Thread.yield();	//礼让
		System.out.println(Thread.currentThread().getName()+"-->end");
	}
	
}

运行结果:

a-->start
b-->start
a-->end
b-->end

2.线程与主线程的礼让:

package com.sheye.stat;

/**
 * yield让出cpu的调度,暂停线程 直接进入就绪状态 不是阻塞状态
 * @author Sheye
 *yield thread的静态代码
 */
public class YieldDemo02 {

	public static void main(String[] args) {
		
		new Thread(()-> {
			for (int i = 0; i < 100; i++) {
				System.out.println("lambda..."+i);
			}
		}).start();
		
		
		for (int i = 0; i < 100; i++) {
			if (i%20==0) {
				Thread.yield();	//main礼让
			}
			System.out.println("main..."+i);
		}
	}
}

插队join:

1.join合并线程,堵塞当前线程,直到调用join的线程执行完成

package com.sheye.stat;
/**
 * 
 * @author Sheye
 *join合并线程,插队线程
 *join是对象方法,不是静态方法
 */
public class BlockedJoin02 {

	public static void main(String[] args) throws InterruptedException {
		System.out.println("爸爸和儿子的故事");
		new Thread(new Father()).start();
	}
}

class Father extends Thread{
	public void run() {
		System.out.println("想抽烟,发现没了");
		System.out.println("让儿子去买中华");
		Thread t = new Thread(new Son());
		t.start();
		try {
			t.join();
			System.out.println("老爸接过烟,把零钱给了儿子");		//son线程插队,此处输出(father线程)被堵塞
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//System.out.println("老爸接过烟,把零钱给了儿子");
	}
}

class Son extends Thread{
	public void run() {
		System.out.println("接过老爸的钱出去了。。。");
		System.out.println("路边有个游戏厅,玩了10秒");
		for (int i = 0; i < 10; i++) {
			System.out.println(i+"秒过去了");
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("赶紧买烟去...");
		System.out.println("手拿一包中华回家了。。。");
	}
}

运行结果:

爸爸和儿子的故事
想抽烟,发现没了
让儿子去买中华
接过老爸的钱出去了。。。
路边有个游戏厅,玩了10秒
0秒过去了
1秒过去了
2秒过去了
3秒过去了
4秒过去了
5秒过去了
6秒过去了
7秒过去了
8秒过去了
9秒过去了
赶紧买烟去...
手拿一包中华回家了。。。
老爸接过烟,把零钱给了儿子

做一个小结:
1.处理join其他的都为静态方法
2.对一下阻塞进行细分
在这里插入图片描述
3.yield不是堵塞,而是从运行状态转到就绪状态

深度观察状态:
在这里插入图片描述

package com.sheye.stat;

import java.lang.Thread.State;

/**
 * 
 * @author Sheye
 *观察线程的状态
 */
public class AllStat {

	public static void main(String[] args) {
		Thread t = new Thread(()->{
			try {
				for (int i = 0; i < 5; i++) {
					Thread.sleep(100);
				}
				
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println("...");
		});
		
		//观察状态
		//NEW
		State stat = t.getState();
		System.out.println(stat);
		
		//RUNNABLE
		t.start();
		stat = t.getState();
		System.out.println(stat);
		
//
//		while (stat != Thread.State.TERMINATED) {
//			System.out.println("我是main");
//			try {
//				Thread.sleep(200);
//			} catch (InterruptedException e) {
//				e.printStackTrace();
//			}
//			stat = t.getState();
//			System.out.println(stat);	//TIMED_WAITING	
//	}
//		stat = t.getState();
//		System.out.println(stat);	//TERMINATED
		
		
		//TERMINATED
		while (true) {
			//活动的线程数
			int num = Thread.activeCount();
			System.out.println(num);
			if (num==1) {
				break;
			}
			try {
				Thread.sleep(200);	//主线程休眠
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			stat = t.getState();
			System.out.println(stat);
		}
	}
}

运行结果:

此处是备注第一块代码,而上图的实例是备注第二块代码
NEW
RUNNABLE
2
TIMED_WAITING
2
TIMED_WAITING
2
...
TERMINATED
1

线程的优先级:

1.Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。
2.线程的优先级用数字表示,范围从1到10
3.优先级的设定建议在start()调用前
4.优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。

package com.sheye.stat;


/**
 * 线程的优先级
 * 1.MAX_PRIORITY 10
 * 2.MIN_PRIORITY 1
 * 3.NORM_PRIORITY 5 所有线程优先级默认值为5
 * @author Sheye
 *概率,不代表绝对的执行的先后顺序
 */
public class PriorityTest {

	public static void main(String[] args) {
		System.out.println(Thread.currentThread().getPriority()); 	//5
		
		MyPriority mp = new MyPriority();
		
		Thread t1 = new Thread(mp);
		Thread t2 = new Thread(mp);
		Thread t3 = new Thread(mp);
		Thread t4 = new Thread(mp);
		Thread t5 = new Thread(mp);
		Thread t6 = new Thread(mp);
		
		//设置优先级一定要在start之前
		t1.setPriority(Thread.MAX_PRIORITY);
		t2.setPriority(Thread.MAX_PRIORITY);
		t3.setPriority(Thread.MAX_PRIORITY);
		t4.setPriority(Thread.MIN_PRIORITY);
		t5.setPriority(Thread.MIN_PRIORITY);
		t6.setPriority(Thread.MIN_PRIORITY);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
		t5.start();
		t6.start();
	}
}

class MyPriority implements Runnable{

	@Override
	public void run() {
		System.out.println(Thread.currentThread().getName()+"-->"+
				Thread.currentThread().getPriority());
		Thread.yield();
		
	}
	
}

运行结果:

5
Thread-0-->10
Thread-2-->10
Thread-3-->1
Thread-4-->1
Thread-1-->10
Thread-5-->1

线程的分类:
1.线程分为用户线程和守护线程(Daemon);
2.虚拟机必须确保用户线程执行完毕;
3.虚拟机不用等待守护线程执行完毕;
4.如后台记录操作日志、监控内存使用等

package com.sheye.stat;

/**
 * 
 * @author Sheye
 *守护线程是为了用户线程服务的,jvm停止不用等待守护进程执行完毕
 *默认:用户线程,jvm等待用户线程执行完毕就会停止
 */
public class DaemonTest {

	public static void main(String[] args) {
		Human human = new Human();
		God god = new God();
		
		Thread t =  new Thread(god);
		t.setDaemon(true);//设置用户进程为守护进程
		t.start();
		new Thread(human).start();
	}
}

//用户进程
class Human extends Thread{
	
	@Override
	public void run() {
		for (int i = 0; i <= 365*100 ; i++) {
			System.out.println("make evertyday worthy");
		}
		System.out.println("....");
	}
}

//守护进程
class God extends Thread{
	@Override
	public void run() {
		for (;true ; ) {
			System.out.println("god is blessing u");
		}
		
	}
}

基本信息:

在这里插入图片描述

package com.sheye.stat;


/**
 * 
 * @author Sheye
 *其他方法
 *isAlive:线程是否还活着
 *currentThread():(静态方法),表示当前线程
 *setName,getName:代理名称
 *真实名称为构造器中设置的名称
 */
public class InfoTest {

	public static void main(String[] args) throws InterruptedException {
		System.out.println(Thread.currentThread().getName()+":"+Thread.currentThread().isAlive());
		
		//设置名称:真实角色+代理角色
		MyInfo mi = new MyInfo("奥里给");
		Thread t = new Thread(mi);
		t.setName("迪迦超人");
		t.start();
		
		Thread.sleep(1000);
		System.out.println(t.getName()+":"+t.isAlive());
		
		
	}
}

class MyInfo implements Runnable{
	private String name;
	
	public MyInfo(String name) {
		this.name = name;
	}

	public void run() {
		System.out.println(Thread.currentThread().getName()+
				"-->"+this.name);
	}
	
}

运行结果:

main:true
迪迦超人-->奥里给
迪迦超人:false

线程不安全的三个事列:

并发:同一个对象多个线程同时操作,列如:同时操作统一账户,同时购买同一车次的票

1.线程不安全:数据有负数和相同的数
原理图:
(1)产生负数:
在这里插入图片描述
(2)产生相同的数:
在这里插入图片描述
解释:
A的工作空间向主存拷贝票数10
然后-1,再进行数据覆盖
B的工作空间在A覆盖之前可能已经对票数10进行了拷贝
等A覆盖完后为9,B也-1对主存进行覆盖为9
(3)同时对银行取钱:
在这里插入图片描述
1.线程不安全:数据有负数和相同的数

package com.sheye.synchronize;



/**
 * @author Sheye
 *线程不安全:数据有负数和相同的数
 *
 */
public class UnsafeTest {

	public static void main(String[] args) {
		
		//一份资源
		Web12306 w = new Web12306();
		//多个代理
		new Thread(w,"线程一").start();
		new Thread(w,"线程二").start();
		new Thread(w,"线程三").start();

	}

}

class Web12306 implements Runnable{
	//票数
	private int ticketNum=10;
	private boolean  flag =true;
	
	//run方法是不能对外throws异常的
	@Override
	public void run() {
		while(flag) {
		test();
	}		
}
	
	public void test() {
		
		if (ticketNum<0) {
			flag=false;
			return ;
		}
		try {
			/**
			*加sleep产生负数的原因是,如果判断条件是1,A线程进入,
			*然后就进行休眠,还没来得及对判断条件进行修改,B线程就已经进入
			*
			*其实就分两种情况
			*1.来得及覆盖会产生负数
			*2.来不及覆盖就会产生相同的数
			*如果进行休眠,A线程休眠结束,B还在进行休眠,A是可以对值进行覆盖的
			*所以当我们备注掉sleep,没有休眠,各个线程并行,来不及覆盖,只会出现相同的值,不会出现负数
			*不备注掉sleep,线程可能来得及覆盖,也可来不及覆盖
			*/
			Thread.sleep(200);	//加入sleep产生负数,多试几次 
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
	}
	

}

2.线程不安全:取钱

package com.sheye.synchronize;
/**
 * 线程不安全:取钱
 * @author Sheye
 *
 */
public class UnsafeTest02 {

	public static void main(String[] args) {
		//账户
		Account account = new Account(100, "结婚彩礼");
		Drawing you = new Drawing(account, 80, "可悲的u");
		Drawing wife = new Drawing(account, 90, "幸福的老婆");
		you.start();
		wife.start();
	}
}

//账户
class Account{
	int money;
	String name;
	public Account(int money, String name) {
		super();
		this.money = money;
		this.name = name;
	}
}

//模拟取款
class Drawing extends Thread {
	Account account;	//取钱的账户
	int drawingMoney;	//取的钱数
	int packetTotal;	//取的总数
	
	public Drawing(Account account, int drawingMoney,String name) {
		super(name);	//线程的名字api有此构造函数 public Thread(String name) {init(null, null, name, 0);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}



	public void run() {
		if (account.money -drawingMoney<0) {
			return;
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		account.money -= drawingMoney;
		packetTotal+=drawingMoney;
		System.out.println(this.getName()+"-->账户余额为"+account.money);
		System.out.println(this.getName()+"-->口袋的钱为"+packetTotal);
	}
}

3.线程不安全:操作容器

package com.sheye.synchronize;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Sheye
 *线程不安全:操作容器
 *
 */
public class UnsafeTest03 {

	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for (int i = 0;i<10000; i++) {
			new Thread(()->{
				list.add(Thread.currentThread().getName());
			}) .start();
		}
		System.out.println(list.size());
	}

}


线程的同步:

1.现实生活中:
我们会遇到“同一个资源,多个人都想使用”的问题。比如:派发礼品,多个人都想获得。天然的解决办法就是,在礼品前,大家排队。前一人领取完后,后一人再领取。

2.线程中:
处理多线程问题时,多个线程访问同一个对象,并且某些线程还想修改这个对象。这时候,我们就需要用到“线程同步”。线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面的线程使用完毕后,下一个线程再使用。

多线程中的处理办法是:队列+锁

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

并发_同步:

并发与同步概念上的区别:

并发:多个线程对同一个对象同时操作
同步:多个事物的步调遵循一定的规范,而不是混乱不堪的.synchronized的锁机制就是为了防止一个线程在读的时候,另一个线程在改,导致数据混乱不堪

所以可以总结为在并发的基础上让线程们遵循一定的规范

一.为什么会产生负数再次做出分析:
在这里插入图片描述
1.B线程进入if,这时候票数还有一张,进入,B线程休眠200ms,但是还没有进行ticketNum–
2.A线程进入if,票数还是1,进入,A线程休眠200ms
3.C线程进入if,票数还是1,进入,C线程休眠200ms
4.B线程恢复运行,ticketNum–,A线程恢复运行,ticketNum–,C线程恢复运行,ticketNum–,产生负数

二.为什么产生相同值做出分析:
在这里插入图片描述
1.线程A把主存的10拷贝过去,然后进行-1操作,再线程A还没有对主存进行覆盖的时候
2.线程B对主存进行拷贝,然后进行了-1操作,这时候A把9对10进行覆盖,输出9
3.线程B也把9对0对主存进行覆盖,又输出9
在这里插入图片描述

1.线程安全,锁当前的类的成员变量

package com.sheye.synchronize;
/**
 * @author Sheye
 *线程不安全:在并发时保证数据的正确性。效率尽可能地高
 *synchronized
 *1。同步方法	什么情况都能用
 *2.同步块	符合条件地情况下缩小了范围
 */
public class SynTest01 {

	public static void main(String[] args) {
		
		//一份资源
		SafeWeb12306 w = new SafeWeb12306();
		//多个代理
		new Thread(w,"线程一").start();
		new Thread(w,"线程二").start();
		new Thread(w,"线程三").start();

	}

}

class SafeWeb12306 implements Runnable{
	//票数
	private int ticketNum=100;
	private boolean  flag =true;
	
	//run方法是不能对外throws异常的
	@Override
	public void run() {
		while(flag) {
		try {
			Thread.sleep(200);	//加入sleep产生负数,多试几次 
		} catch (InterruptedException e) {
			
			e.printStackTrace();
		}
		test();
	}		
}
	
	//线程安全 同步
	//锁不是锁方法,而是锁ticketNum,flag成员变量,锁成员变量实质上就是锁SafeWeb12306这个对象
	//锁了资源,这个资源是对象的资源,是this
	public synchronized void test() {
		
		if (ticketNum<0) {
			flag=false;
			return ;
		}
		System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
	}
	

}

运行结果:

线程一---->剩余票数:10
线程三---->剩余票数:9
线程二---->剩余票数:8
线程二---->剩余票数:7
线程一---->剩余票数:6
线程三---->剩余票数:5
线程二---->剩余票数:4
线程一---->剩余票数:3
线程三---->剩余票数:2
线程二---->剩余票数:1
线程一---->剩余票数:0

2.线程安全,但是想锁的对象选择错误
package com.sheye.synchronize;
/**
 * @author Sheye
 *线程不安全:在并发时保证数据的正确性。效率尽可能地高
 *synchronized
 *1。同步方法	什么情况都能用
 *2.同步块	符合条件地情况下缩小了范围
 *
 */
public class SynTest02 {

	public static void main(String[] args) {
		//账户
		Account1 account = new Account1(100, "结婚彩礼");
		Drawing1 you = new Drawing1(account, 80, "可悲的u");
		Drawing1 wife = new Drawing1(account, 90, "幸福的老婆");
		you.start();
		wife.start();
	}
}

//账户
class Account1{
	int money;
	String name;
	public Account1(int money, String name) {
		super();
		this.money = money;
		this.name = name;
	}
}

//模拟取款
class Drawing1 extends Thread {
	Account1 account;	//取钱的账户
	int drawingMoney;	//取的钱数
	int packetTotal;	//取的总数
	
	public Drawing1(Account1 account, int drawingMoney,String name) {
		super(name);	//线程的名字api有此构造函数 public Thread(String name) {init(null, null, name, 0);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}



	public void run() {
		test();
	}
	
	/**
	 * 出现异常的是账户的余额
	 * 目标不对锁定失败,这里不是锁定this,应该锁定account
	 */
	public synchronized void test() {
		if (account.money -drawingMoney<0) {
			return;
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		account.money -= drawingMoney;
		packetTotal+=drawingMoney;
		System.out.println(this.getName()+"-->账户余额为"+account.money);
		System.out.println(this.getName()+"-->口袋的钱为"+packetTotal);
	}
}

synchronized块:
在这里插入图片描述
回顾java中存在的几种块:
1.局部块
2.构造块
3.静态块
4.同步块
局部块,构造块,静态块

1.对取钱问题,准确锁定目标对象,提高性能

package com.sheye.synchronize;
/**
 * @author Sheye
 *线程不安全:在并发时保证数据的正确性。效率尽可能地高
 *synchronized
 *1。同步方法	什么情况都能用
 *2.同步块	目标更明确
 *
 */
public class SynBlockTest01 {

	public static void main(String[] args) {
		//账户
		Account2 account = new Account2(100, "结婚彩礼");
		Drawing2 you = new Drawing2(account, 80, "可悲的u");
		Drawing2 wife = new Drawing2(account, 90, "幸福的老婆");
		you.start();
		wife.start();
	}
}

//账户
class Account2{
	int money;
	String name;
	public Account2(int money, String name) {
		super();
		this.money = money;
		this.name = name;
	}
}

//模拟取款
class Drawing2 extends Thread {
	Account2 account;	//取钱的账户
	int drawingMoney;	//取的钱数
	int packetTotal;	//取的总数
	
	public Drawing2(Account2 account, int drawingMoney,String name) {
		super(name);	//线程的名字api有此构造函数 public Thread(String name) {init(null, null, name, 0);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}



	public void run() {
		test();
	}
	
	/**
	 * 
	 * 目标锁定account
	 */
	public void test() {
		//提升性能
		//假设账户已经没有钱了,但是每个线程进入的时候还是要看锁存不存在,影响性能,可以直接加一个判断,跳出
		if (account.money<0) {
			return;
		}
		//同步块
		synchronized(account) {
			if (account.money -drawingMoney<0) {
				return;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			account.money -= drawingMoney;
			packetTotal+=drawingMoney;
			System.out.println(this.getName()+"-->账户余额为"+account.money);
			System.out.println(this.getName()+"-->口袋的钱为"+packetTotal);
		}
		
	}
}

运行结果:

可悲的u-->账户余额为20
可悲的u-->口袋的钱为80

2.解决容器问题:

package com.sheye.synchronize;

import java.util.ArrayList;
import java.util.List;

/**
 * @author Sheye
 *线程安全:操作容器
 *
 */
public class SynBlockTest02 {

	public static void main(String[] args) throws InterruptedException {
		List<String> list = new ArrayList<String>();
		for (int i = 0;i<10000; i++) {
			new Thread(()->{
				synchronized(list){
					list.add(Thread.currentThread().getName());
				}
				
			}) .start();
		}
		//不加休眠,可能就会导致主线程进去,多个线程还没进行完,输出语句就打印了
		//输出属于主线程,多个线程并行,让主线程休眠,就是让其他线程完成添加,如果不让主线程休眠,主线程与其他线程抢行,其他线程还没运行完,主线程直接输出
		Thread.sleep(1000);
		System.out.println(list.size());
	}
}

并发同步性能分析:

同步方法和同步块的区别:
1.同步方法:什么情况都能用
2.同步块:符合条件地情况下缩小了锁定对象的范围,使之效率更高

package com.sheye.synchronize;

/**
 * @author Sheye
 *线程不安全:在并发时保证数据的正确性。效率尽可能地高
 *synchronized
 *1.同步方法	什么情况都能用
 *2.同步块	符合条件地情况下缩小了锁定对象的范围,使之效率更高
 */
public class SynBlockTest03 {

	public static void main(String[] args) {
		
		//一份资源
		SynWeb12306 w = new SynWeb12306();
		//多个代理
		new Thread(w,"线程一").start();
		new Thread(w,"线程二").start();
		new Thread(w,"线程三").start();

	}

}

class SynWeb12306 implements Runnable{
	//票数
	private int ticketNum=10;
	private boolean  flag =true;
	
	//run方法是不能对外throws异常的
	@Override
	public void run() {
		while(flag) {
			try {
				Thread.sleep(100);	
			} catch (InterruptedException e) {
				
				e.printStackTrace();
			}
			test3();
		}	
}

	
	//线程安全 范围太大 -->效率低下
	public void test() {
		synchronized(this) {
			if (ticketNum<0) {
				flag=false;
				return ;
			}
			try {
				Thread.sleep(200);	//模拟延时
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
		}
	}
	
	/**
	 * 线程不安全  ticketNum作为对象,地址在变,synchronized锁的是一个不变的"对象"
	 * 内容可以变但是地址不能变,1的对象,2的对象,3的对象都是不一样的
	 */
	public void test1() {
		synchronized((Integer)ticketNum) {
			if (ticketNum<=0) {
				flag=false;
				return ;
			}
			try {
				Thread.sleep(200);
			} catch (Exception e) {
				
			}
			
			System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
	}

}
	
	//线程不安全  范围太小锁不住
	public void test2() {
		synchronized(this) {
			if (ticketNum<=0) {
				flag=false;
				return ;
			}
		}
			try {
				Thread.sleep(200);
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
}
	
	//线程安全:尽可能锁合理的范围(不是指代码,是指数据的完整性)
	//双重检测--每个检查作用不一样
	public void test3() {
		if (ticketNum<=0) {	//考虑没票的情况
			flag=false;
			return ;
		}
		synchronized(this) {
			if (ticketNum<=0) {//考虑最后一张票的情况
				flag=false;
				return ;
			}
			try {
				//模拟延时
				Thread.sleep(200);	
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"---->剩余票数:"+ticketNum--);
		}
	}
	}

方法test2产生负数分析:
在这里插入图片描述
并发同步地运用1–快乐影院:
1.使用synchronized块:

package com.sheye.synchronize;
/**
* 
* @author Sheye
*快乐影院
*/
public class HappyCinema {

   public static void main(String[] args) {
   	Cinema c = new Cinema(2, "万达影院");
   	new Thread(new Customer(c, 2),"熊大").start();
   	new Thread(new Customer(c, 1),"熊二").start();
   }
}

//顾客
class Customer implements Runnable{
   Cinema cinema;
   int seats;
   
   public Customer(Cinema cinema, int seats) {
   	super();
   	this.cinema = cinema;
   	this.seats = seats;
   }

   public void run() {
   	synchronized(cinema) {
   	boolean flag = cinema.bookTicket(seats);
   	if (flag) {
   		System.out.println("出票成功"+Thread.currentThread().getName()+"-->位置为:"+seats);
   	}else {
   		System.out.println("出票失败"+Thread.currentThread().getName()+"-->位置不够");
   	}
   	
   }
   }
}

//影院
class Cinema{
   int avaliableSeats; //可用的位置
   String name;	//影院名称
   public Cinema(int avaliableSeats, String name) {
   	super();
   	this.avaliableSeats = avaliableSeats;
   	this.name = name;
   }
   
   /**购票
    * @param seats-->想要订购的位置
    * @return
    */
   public boolean bookTicket(int seats) {
   	if (seats > avaliableSeats) {
   		return false;
   	}
   	System.out.println("剩余的位置"+avaliableSeats);
   	avaliableSeats -=seats;
   	return true;
   }
}

运行结果:

剩余的位置2
出票成功熊大-->位置为:2
出票失败熊二-->位置不够

2.使用synchronized块,并加入容器:

package com.sheye.synchronize;

import java.util.ArrayList;
import java.util.List;

/**
 * 
 * @author Sheye
 *快乐影院
 */
public class HappyCinema02 {

	public static void main(String[] args) {
		List<Integer> availableSeats =  new ArrayList<Integer>();
		availableSeats.add(1);
		availableSeats.add(2);
		availableSeats.add(3);
		availableSeats.add(4);
		availableSeats.add(6);
		
		List<Integer> seats1 =  new ArrayList<Integer>();
		seats1.add(1);
		seats1.add(2);
		
		List<Integer> seats2 =  new ArrayList<Integer>();
		seats2.add(4);
		seats2.add(5);
		seats2.add(6);
		Cinema1 c = new Cinema1(availableSeats, "万达影院");
		new Thread(new Customer1(c, seats1),"熊大").start();
		new Thread(new Customer1(c, seats2),"熊二").start();
	}
}

//顾客
class Customer1 implements Runnable{
	Cinema1 cinema;
	List<Integer> seats;
	
	public Customer1(Cinema1 cinema, List<Integer> seats) {
		super();
		this.cinema = cinema;
		this.seats = seats;
	}

	public void run() {
		synchronized(cinema) {
		boolean flag = cinema.bookTicket(seats);
		if (flag) {
			System.out.println("出票成功"+Thread.currentThread().getName()+"-->位置为:"+seats);
		}else {
			System.out.println("出票失败"+Thread.currentThread().getName()+"-->位置不够");
		}
		
	}
	}
}

//影院
class Cinema1{
	List<Integer> availableSeats; //可用的位置
	String name;	//影院名称
	public Cinema1(List<Integer> availableSeats, String name) {
		super();
		this.availableSeats = availableSeats;
		this.name = name;
	}
	
	/**购票
	 * @param seats-->想要订购的位置
	 * @return
	 */
	public boolean bookTicket(List<Integer> seats) {
		System.out.println(this.name+":欢迎选购-->"+"可用的位置:"+availableSeats);
		List<Integer> copy = new ArrayList<Integer>();
		copy.addAll(availableSeats);
		
		//相减
		copy.removeAll(seats);
		//removeAll这个方法,比如说A为1,2,3.B为1,6,A移除B,移除交集1,所以要判断,移除的个数是否等于预定的座位数
		if (availableSeats.size()-copy.size()!=seats.size()) {
			return false;
		}
		//成功
		availableSeats=copy;
		return true;
		
	}
}

运行结果:

万达影院:欢迎选购-->可用的位置:[1, 2, 3, 4, 6]
出票成功熊大-->位置为:[1, 2]
万达影院:欢迎选购-->可用的位置:[3, 4, 6]
出票失败熊二-->位置不够

并发同步地运用2–快乐火车票:
3.使用synchronized方法,使用线程代理---->线程块改成线程方法

一般都是在实现runnable的类中,写一个synchronized方法或者块,让run方法调用synchronized方法,或者在run方法中写synchronized块,synchronized块中的对象为另一个类的类对象

但是用代理,直接改造把锁对象,锁对象对应的类实现runnable,W12306 原本为锁对象,结果类实现runnable,其实synchronized块的本质也是锁锁对象里面的方法,所以使用synchronized方法时,锁的是锁对象对应类里面的方法

package com.sheye.synchronize;
/**
 * 
 * @author Sheye
 *快乐影院
 */
public class Happy12306 {

	public static void main(String[] args) {
		W12306 c = new W12306(2, "万达影院");
		new Passanger(c,"熊大",2).start();
		new Passanger(c,"熊二",1).start();
	}
}

//顾客
/**
 * 
 * @author Sheye
 *thread里面的target代理
 *Passanger为子代理
 *target为被代理的对象
 */
class Passanger extends Thread{
	
	int seats;
	
	public Passanger(Runnable target,String name,int seats) {
		super(target,name);
		this.seats = seats;
	}

	
}

//影院
class W12306 implements Runnable{
	int avaliableSeats; //可用的位置
	String name;	//影院名称
	public W12306(int avaliableSeats, String name) {
		super();
		this.avaliableSeats = avaliableSeats;
		this.name = name;
	}
	
	public void run() {
		//当前线程转成类对象
		Passanger p = (Passanger) Thread.currentThread();
		boolean flag = this.bookTicket(p.seats);
		if (flag) {
			System.out.println("出票成功"+Thread.currentThread().getName()+"-->位置为:"+p.seats);
		}else {
			System.out.println("出票失败"+Thread.currentThread().getName()+"-->位置不够");
		}
		
	
	}
	
	/**购票
	 * @param seats-->想要订购的位置
	 * @return
	 */
	public synchronized boolean bookTicket(int seats) {
		if (seats > avaliableSeats) {
			return false;
		}
		System.out.println("剩余的位置"+avaliableSeats);
		avaliableSeats -=seats;
		return true;
	}
}

并发容器(CopyOnWriteArrayList–>底层有锁):

package com.sheye.synchronize;


import java.util.concurrent.CopyOnWriteArrayList;	//在写的基础上进行复制

/**
 * @author Sheye
 *线程不安全:操作并发容器
 *
 */
public class SynContainer {

	public static void main(String[] args) {
		CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<String>();	//底层有锁
		for (int i = 0;i<10000; i++) {
			new Thread(()->{
				list.add(Thread.currentThread().getName());
			}) .start();
		}
		System.out.println(list.size());
	}

}


死锁问题:
在这里插入图片描述
1.死锁不一定存在,过多的同步可能造成死锁
在这里插入图片描述
解释:
1.线程小丫拿到口红锁,然后进行休眠
2.线程大丫拿到镜子锁,进行休眠
3.当小丫想要镜子锁,大丫想要口红锁,双方的资源都被互相占用,导致死锁

package com.sheye.synchronize;

/**
 * 
 * @author Sheye
 *死锁:过多的同步可能造成相互不释放资源
 *从而相互等待,一般发生于同步中持有多个对象的锁
 *
 *模拟小丫占着镜子,大丫占着口红
 *
 *避免:不要在一个代码块中,同时持有多个对象的锁
 *就是一个同步块当中嵌套一个同步块
 */
public class DeadLock {

	public static void main(String[] args) {
		Makeup g1 = new Makeup(0, "大丫");
		Makeup g2 = new Makeup(1, "小丫");
		g1.start();
		g2.start();
	}

}

//口红
class Lipstick{
	
}

//镜子
class Mirror{
	
}

//化妆
class Makeup extends Thread{
	//使用静态new对象不管创建几次都是那一份
	static Lipstick lipstick = new Lipstick();
	static Mirror mirror = new Mirror();
	//选择
	int choice;
	//女孩的名字
	String girlName;	
	
	public Makeup(int choice,String girlNameame) {
		this.girlName = girlNameame;
		this.choice = choice;
	}

	@Override
	public void run() {
		//化妆
		makeup();
	}
	
	//相互持有对方的对象锁--》可能造成死锁
	private void makeup() {
		if (choice==0) {
			synchronized (lipstick) {	//获得口红的锁
				System.out.println(this.girlName + "获得口红");
				//1秒后想拥有镜子的锁
				try {
					//故意让他形成时间间隔,才有可能造成相互不释放资源
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				synchronized (mirror) {	//获得镜子的锁
					System.out.println(this.girlName + "获得镜子");
				}
			}
		}else {
			synchronized (mirror) {	//获得镜子的锁
				System.out.println(this.girlName + "获得镜子");
				//2秒后想拥有口红的锁
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
				synchronized (lipstick) {	//获得口红的锁
					System.out.println(this.girlName + "获得口红");
				}
			}
		}
	}
}

运行结果:

小丫获得镜子
大丫获得口红

死锁的解决:

package com.sheye.synchronize;

/**
 * 
 * @author Sheye
 *死锁:过多的同步可能造成相互不释放资源
 *从而相互等待,一般发生于同步中持有多个对象的锁
 *
 *模拟小丫占着镜子,大丫占着口红
 *
 *避免:不要在一个代码块中,同时持有多个对象的锁
 *就是一个同步块当中嵌套一个同步块
 */
public class DeadLock {

	public static void main(String[] args) {
		Makeup g1 = new Makeup(0, "大丫");
		Makeup g2 = new Makeup(1, "小丫");
		g1.start();
		g2.start();
	}

}

//口红
class Lipstick{
	
}

//镜子
class Mirror{
	
}

//化妆
class Makeup extends Thread{
	//使用静态new对象不管创建几次都是那一份
	static Lipstick lipstick = new Lipstick();
	static Mirror mirror = new Mirror();
	//选择
	int choice;
	//女孩的名字
	String girlName;	
	
	public Makeup(int choice,String girlNameame) {
		this.girlName = girlNameame;
		this.choice = choice;
	}

	@Override
	public void run() {
		//化妆
		makeup();
	}
	
	//相互持有对方的对象锁--》可能造成死锁
	private void makeup() {
		if (choice==0) {
			synchronized (lipstick) {	//获得口红的锁
				System.out.println(this.girlName + "获得口红");
				//1秒后想拥有镜子的锁
				try {
					//故意让他形成时间间隔,才有可能造成相互不释放资源
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
//				synchronized (mirror) {	//获得镜子的锁
//					System.out.println(this.girlName + "获得镜子");
//				}
			}
			synchronized (mirror) {	//获得镜子的锁
				System.out.println(this.girlName + "获得镜子");
			}
		}else {
			synchronized (mirror) {	//获得镜子的锁
				System.out.println(this.girlName + "获得镜子");
				//2秒后想拥有口红的锁
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					
					e.printStackTrace();
				}
//				synchronized (lipstick) {	//获得口红的锁
//					System.out.println(this.girlName + "获得口红");
//				}
			}
			synchronized (lipstick) {	//获得口红的锁
				System.out.println(this.girlName + "获得口红");
			}
		}
	}
}

运行结果:

小丫获得镜子
大丫获得口红
小丫获得口红
大丫获得镜子

线程通信:
线程通信必须在synchronized同步下进行
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
wait:是本线程进行堵塞,释放锁给另一个线程使用
//Causes the current thread to wait until another thread invokes the notify() method or the notifyAll() method for this object.
notify:
Wakes up a single thread that is waiting on this object’s monitor.(唤醒正在此对象的监视器上等待的单个线程)
可以得出wait是将本线程堵塞,而notify是唤醒一个等待中的线程,相当于唤醒另一个线程
在这里插入图片描述
1.生产者消费者模式->管程法:
线程通信必须在synchronized同步下进行:
1.生产者有秩序地(同步–>队列加锁)向容器中取
2.消费者有秩序地(同步–>队列加锁)向容器中存
3.让生产者与消费者进行通信
在这里插入图片描述
在这里插入图片描述
生产者消费者模式中的4个要素:
1.生产者和消费者都为多线程
2.容器为并发容器,支持生产者和消费者并发操作
3.容器中的数据

注意:调用一个返回值为对象的方法,方法.对象中的成员变量,这个方法也会执行- -,不然返回值哪里来的。。。。

package com.sheye.cooperation;


/**
 * 
 * @author Sheye
 *协作模式:生产者和消费者实现方式一:管程法
 *线程根据容器进行交流
 */
public class CooperationTest01 {

	public static void main(String[] args) {
		SynContainer sc = new SynContainer();
		new Producer(sc).start();
		new Consumer(sc).start();
	}
}

//生产者
class Producer extends Thread{
	SynContainer container;
	
	public Producer(SynContainer container) {
		this.container = container;
	}

	public void run() {
		//生产
		for (int i = 0; i < 100; i++) {
			System.out.println("生产第-->"+i+"个馒头");
			container.push(new Data(i));
		}
		
	}
}

//消费者
class Consumer extends Thread{
	SynContainer container;
	
	public Consumer(SynContainer container) {
		this.container = container;
	}
	
	public void run() {
		for (int i = 0; i < 100; i++) {
			//调用一个返回值为对象的方法,方法.对象中的成员变量,这个方法也会执行- -,不然返回值哪里来的。。。。
			System.out.println("消费第-->"+container.pop().id+"个馒头");
			
		}
	}
}

//缓冲区
class SynContainer{
	Data[] datas = new Data[10];	//存储数据的容器
	int count=0;	//计数器
	
	//存储 生产
	public synchronized void push(Data data) {
		//何时能生产 容器存在空间
		//存在空间 可以生产
		//不能生产 只有等待
		if (count==datas.length) {
			try {
				this.wait();	//本线程阻塞 消费者通知生产者解除
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		datas[count]=data;
		count++;
	    //存在数据 可以通知消费解除(生产者不断在产出,通知消费者消费)
		this.notifyAll();
	}
	
	//获取 消费
	public synchronized Data pop() {
		//何时消费 容器中是否存在数据
		//没有数据 只能等待
		if (count==0) {
			try {
				this.wait();	//本线程阻塞 生产者通知消费者堵塞
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		//存在数据可消费
		count--;
		Data data = datas[count];
		this.notifyAll();  //存在空间 可以唤醒对方生产(消费者在消费,证明有空空间,通知生产者进行生产)
		return data;
	}
}

//数据
class Data{
	int id;

	public Data(int id) {
		super();
		this.id = id;
	}
	
}

2.生产者消费者模式->信号灯法:
在这里插入图片描述

package com.sheye.cooperation;


/**
 * 
 * @author Sheye
 *协作模式:生产者和消费者实现方式二:信号灯
 *借助标志位
 *演员演什么,我们就看什么
 */
public class CooperationTest02 {

	public static void main(String[] args) {
		Tv tv=new Tv();
		
		new Player(tv).start();
		new Watcher(tv).start(); 
	}
}

//生产者 演员
class Player extends Thread{
	Tv tv;

	public Player(Tv tv) {
		this.tv = tv;
	}
	
	public void run() {
		for (int i = 0; i < 5; i++) {
			//奇偶校验
			if (i%2==0) {
				this.tv.play("奇葩说");
			}else {
				this.tv.play("太无聊,喝瓶立白洗洗嘴");
			}
		}
	}
}

//消费者 观众
class Watcher extends Thread{
	Tv tv;

	public Watcher(Tv tv) {
		this.tv = tv;
	}
	
	public void run() {
	for (int i = 0; i < 5; i++) {
		tv.watch();
	  }
	}
}

//同一个资源 电视
class Tv{
	String voice;
	
	//信号灯
	//真 表示演员表演 观众等待
	//假 表示观众观看 演员等待
	boolean flag = true;
	
	//表演
	public synchronized void play(String voice) {
		//演员等待
		if (!flag) {
			try {
				this.wait() ;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("表演了:"+voice);
		this.voice = voice;
		this.notifyAll();
		this.flag=!this.flag;
	}
	
	//观看
	public synchronized void watch() {
		//观众等待
		if (flag) {
			try {
				this.wait() ;
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("听到了:"+voice);
		this.notifyAll();
		//切换标志
		this.flag=!this.flag;
	}
}

运行结果:

表演了:奇葩说
听到了:奇葩说
表演了:太无聊,喝瓶立白洗洗嘴
听到了:太无聊,喝瓶立白洗洗嘴
表演了:奇葩说
听到了:奇葩说
表演了:太无聊,喝瓶立白洗洗嘴
听到了:太无聊,喝瓶立白洗洗嘴
表演了:奇葩说
听到了:奇葩说

线程通信总结:对于同一个资源,或者说线程通信借助的容器,介质。同步方法,wait和notifyAll都是在容器类中写

高级主题:

任务定时调度:
1.Timer 和TimerTask

package com.sheye.others;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 
 * @author Sheye
 *任务调度,借助timer类和timeerTest类
 */
public class TimerTest01 {

	public static void main(String[] args) {
		Timer timer = new Timer();
		//执行安排
		//timer.schedule(new MyTask(), 1000);	//执行任务一次
		//timer.schedule(new MyTask(), 1000,200);	//一秒后执行,每隔200ms执行一次
		Calendar cal = new GregorianCalendar(2019,11,25,22,22,10);
		timer.schedule(new MyTask(), cal.getTime(),200);	//指定时间
	}
}

//任务类
class MyTask extends TimerTask{

	
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println("防空大脑...");
		}
		System.out.println("---------------我是一条分割线--------------");
	}
	
	
}

2.QUARTZ—>任务定时调度框架
在这里插入图片描述
1.HelloJob(任务类)


package com.sheye.others;

/**
 * 任务
 */
import java.util.Date;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class HelloJob implements Job {
 
    public HelloJob() {
    }
  
    public void execute(JobExecutionContext context)
        throws JobExecutionException {
    	System.out.println("---------start-----------");
    	System.out.println("Hello World! - " + new Date());
    	System.out.println("---------end-----------");
    }

}

2.QuartTest(主类,调度器和触发器)


package com.sheye.others;

import static org.quartz.DateBuilder.evenSecondDateAfterNow;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.TriggerBuilder.newTrigger;

import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;


import java.util.Date;

/**
 * 
 * @author Administrator
 *quartz学习入门
 */
public class QuartTest {

  public void run() throws Exception {
   
	//1.创建Scheduler工厂
    SchedulerFactory sf = new StdSchedulerFactory();
    
    //2.从工厂中获取调度器
    Scheduler sched = sf.getScheduler();

    //3.创建JobDetail
    JobDetail job = newJob(HelloJob.class).withIdentity("job1", "group1").build();
    
    //下一秒
    Date runTime = evenSecondDateAfterNow();
    
    //4.触发条件-->下一秒触发
    //Trigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(runTime).build();
    /**
     * startAt(runTime)	开始时间
     * withIntervalInSeconds(10)	间隔时间
     * withRepeatCount(15)	间隔次数
     */
    Trigger trigger = newTrigger().withIdentity("trigger1", "group2").startAt(runTime)
            .withSchedule(simpleSchedule().withIntervalInSeconds(10).withRepeatCount(15)).forJob(job).build();



    //5.注册任务和触发条件
    sched.scheduleJob(job, trigger);
    
    //6.启动
    sched.start();
    
    //触发前的时间,一秒后被触发
    Date d = new Date();
    System.out.println(d);

    try {
      
      //500秒之后
      Thread.sleep(500L * 1000L);
      
    } catch (Exception e) {
      
    }

    //7.关闭
    sched.shutdown(true);
    
  }

  public static void main(String[] args) throws Exception {

    QuartTest example = new QuartTest();
    example.run();

  }

}

HappenBefore(多线程中可能造成的错误):
在这里插入图片描述
在这里插入图片描述
1.当寄存器r和寄存器r2里面的值相加,相加值放在r3中
2.当寄存器r3和寄存器r4里面的值相加,相加值放在r4中
3.然后将1移至寄存器r5
这三个指令,2指令执行较慢,如果3指令与1,2不存在数据依赖,为了加速效率,3指令提前,1个线程不会出错,是多线程中不同顺序可能造成错误
在这里插入图片描述

package com.sheye.others;

/**
 * 
 * @author Administrator
 *指令重排:代码的执行顺序与预期的不一样
 *目的:提高性能
 */
public class HappenBefore {
	
	//变量一
	private static int a = 0; 
	//变量二
	private static boolean flag = false;
	public static void main(String[] args) throws InterruptedException {
		
		Thread t1 = new Thread(()->{
			a = 1;
			flag = true;
			
		});
		
		Thread t2 = new Thread(()->{
			if (flag) {
				a *=1;
			}
			
			//指令重排 如果先运行上面的if 那么下面的if不会执行 但是最后输出了 happen before a->1
			if (a == 0) {
				System.out.println("happen before a->"+a);
			}
		});
		
		t1.start();
		t2.start();
		
		t1.join();
		t2.join();
	}

}

volatile(轻量级synchronize):
在这里插入图片描述
有线程一和线程二,线程一在自己的工作空间 主存中的一个数据进行拷贝,再在工作空间进行修改,这时候可能线程一还没有进行覆盖,线程二也 数据进行拷贝操作造成错误,而volatile就是让线程一修改数据后马上进行覆盖,避免错误(现在不常见,计算机性能很高,可以及时覆盖)

package com.sheye.others;

/**
 * 
 * @author Administrator
 *Volatile用于保证数据同步,也就是可见性
 */
public class VolatileTest {

	public volatile static int num = 0;
	public static void main(String[] args) throws InterruptedException {
		new Thread(()->{
			while(num==0) {
				
			}

		}) .start();
		
		
		Thread.sleep(1000);	//主线程休眠
		num=1;
	}
}

2.避免指令重排

单例模式(懒汉式):

package com.sheye.others;

/**
 * DCL单例模式:懒汉式套路基础上加入并发控制,在多线程环境下,保证对外存在一个对象(不能new对象)
 * 1.构造器的私有化--》避免外部new构造器
 * 2.提供私有的静态方法--》存储静态的地址
 * 3.提供公共的静态方法--》获取属性
 * @author Sheye
 *
 */
public class DoubleCheckedLocking {
	
	//2.提供私有的静态方法 
	//volatile可以避免指令重排 没有volatile其他线程可能访问一个没有初始化的对象
	private static volatile DoubleCheckedLocking instance;
	
	//3.提供公共的静态方法 
	public static  DoubleCheckedLocking getInstance() {
		//doublechecking再次检测
		if (null!=instance) {	//避免不必要已经存在对象
			return instance;
		}
		
		//我们用的时候都会往类加载器中丢入一个class
		synchronized (DoubleCheckedLocking.class) {
			if (null==instance) {
				instance = new DoubleCheckedLocking();
				//1.开辟空间 2.初始化对象信息 3.返回对象的地址给引用
				
			}
			return instance;
		}
		
	}
	
		
	public static  DoubleCheckedLocking getInstance1(long time) {
		
		
		if (null==instance) {
			try {
				Thread.sleep(time);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			instance = new DoubleCheckedLocking();
			//1.开辟空间 2.初始化对象信息 3.返回对象的地址给引用
			
		}
		return instance;
	}
		
	
	
	//1.构造器的私有化
	private DoubleCheckedLocking() {
		
	}
	
	public static void main(String[] args) {
		Thread t1 = new Thread(()->{
			System.out.println(DoubleCheckedLocking.getInstance());
		}) ;
		t1.start();
		
		Thread t2 = new Thread(()->{
			System.out.println(DoubleCheckedLocking.getInstance());
		}) ;
		t2.start();
	}

}

在这里插入图片描述
1.加上doublechecking的目的:
防止A在有锁的情况下下初始化因为网络延迟过于浪费时间,加上判断提高效率

在这里插入图片描述
1.因为初始化对象信息所花费的时间太多,可能会发生happenbefore,第三步操作在第二步操作之前执行,所以加上volatile

在这里插入图片描述
ThreadLocal:
在这里插入图片描述
threadlocal原理图:
在这里插入图片描述
1.threadlocal相当于一个银行,而线程相当于一个个保险箱
2.好处就是线程之间的工作空间互相保持独立,又可以共享threadlocal中空间

1.基本使用

package com.sheye.others;

/**
 * threadlocal:每个线程自身的存储本地,局部空间
 * get/set/initialValue方法
 * @author Administrator
 *
 */
public class ThreadLocalTest01 {

	//private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>();
	//更改初始化值
	//1.第一种方法
//	private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
//		protected Integer initialValue() {
//			return 200;
//		}
//	};
	
	//2.第二种方法
//	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->{
//		return 200;
//	});
	
	//3.第三种方法
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->200);
	
	
	public static void main(String[] args) {
		//获取值 没有设置其他线程,我们设置的都是主线程
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	//输出:main-->null
		
		//设置值
		threadLocal.set(400);
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
		
		//其他线程的使用
		new Thread(new MyRun()).start();
		new Thread(new MyRun()).start();
		
	}
	
	public static class MyRun implements Runnable{

		@Override
		public void run() {
			threadLocal.set((int)(Math.random()*99));
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
			
		}
		
	}
}

运行结果:

main-->200
main-->400
Thread-0-->20
Thread-1-->26

在这里插入图片描述
代码第9行使用泛型,就是让每个线程的空间(线程本地环境)都存储整形数据

2.线程之间相互不影响

package com.sheye.others;

/**
 * threadlocal:每个线程存储自身的数据,更改不会影响其他线程 
 * get/set/initialValue方法
 * @author Administrator
 *
 */
public class ThreadLocalTest02 {
	
	//3.第三种方法
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->1);
	
	
	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			new Thread(new MyRun()).start();
		}
		
	}
	
	public static class MyRun implements Runnable{

		@Override
		public void run() {
			Integer left = threadLocal.get();
			System.out.println(Thread.currentThread().getName()+"得到了-->"+threadLocal.get());
			threadLocal.set(left-1);
			System.out.println(Thread.currentThread().getName()+"还剩下-->"+threadLocal.get());
		}
		
	}
}

运行结果;

Thread-1得到了-->1
Thread-0得到了-->1
Thread-2得到了-->1
Thread-0还剩下-->0
Thread-1还剩下-->0
Thread-2还剩下-->0

3.线程所处的环境

package com.sheye.others;

/**
 * threadlocal:分析上下文,所处的环境,起点
 * 1.构造器:那里调用 就属于那里 找线程体
 * 2.run方法:线程本身
 * @author Administrator
 *
 */
public class ThreadLocalTest03 {
	
	//3.第三种方法
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->1);
	
	
	public static void main(String[] args) {
		new Thread(new MyRun()).start();
		
	}
	
	public static class MyRun implements Runnable{
		
		public MyRun() {	//此处构造器是由main线程进行初始化
			threadLocal.set(-100); //影响的是main线程
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
		}

		@Override
		public void run() {
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
			//new Thread(new MyRunxxx()).start();	如果此处调用其他线程,那么构造器由本线程进行创建
		}
		
	}
}

运行结果:

main-->-100
Thread-0-->1

在这里插入图片描述
4.threadlocal的子类InheritableThreadLocal的使用:

package com.sheye.others;

/**
 * InheritableThreadLocal:继承上下文,环境的数据 起点 拷贝一份给子线程
 * 1.构造器:那里调用 就属于那里 找线程体
 * 2.run方法:线程本身
 * @author Administrator
 *
 */
public class ThreadLocalTest04 {
	
	
	private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<Integer>();
	
	
	public static void main(String[] args) {
		threadLocal.set(2);
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
		
		//此线程由main线程开辟 共享开辟它的线程的数据
		new Thread(()->{
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
			threadLocal.set(20);	//自己也可以修改
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
		}) .start();
		
	}
	
	
}

运行结果:

main-->2
Thread-0-->2
Thread-0-->20

可重入锁:
可重入:比如我去朋友家去玩,我就有机会去朋友家的厕所,去他家的房间,去他家的卧室
在这里插入图片描述
1.可重入锁的基本使用:

package com.sheye.others;

/**
 * 可重入锁:锁可以重复使用
 * 
 * @author Administrator
 *
 */
public class LockTest02 {

	public void test() {
		// 第一次获得锁
		synchronized (this) {
			while (true) {
				// 第二次获得同样的锁
				synchronized (this) {
					System.out.println("ReentrantLock!");
				}
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		new LockTest02().test();
	}
}

2.手写不可重入锁:

package com.sheye.others;

import java.time.LocalDate;

/**
 * 不可重入锁:锁不可以重复使用
 * 
 * @author Administrator
 *
 */
public class LockTest01 {
	
	Lock lock = new Lock();
	
	public void a() throws InterruptedException {
		lock.lock();
		//lock置为true,dosomething的lock进入死循环==死锁
		doSomething();
		lock.unlock();
	}
	
	public void doSomething() throws InterruptedException {
		lock.lock();
		//............
		lock.unlock();
	}
	
	public static void main(String[] args) throws InterruptedException {
		LockTest01 lock = new LockTest01();
		lock.a();
		lock.doSomething();
	}
}

//不可重入锁
class Lock{
	//锁是否被占用
	private boolean isLocked = false;
	
	//使用锁
	public synchronized void lock() throws InterruptedException {
		while (isLocked) {
			wait();
		}
		
		isLocked = true;
	}
	
	//释放锁
	public synchronized void unlock() {
		isLocked = false;
		notify();
	}
}

3.手写可重入锁:

package com.sheye.others;



/**
 * 可重入锁:锁可以重复使用+计数器
 * 
 * @author Administrator
 *
 */
public class LockTest03 {
	
	ReLock lock = new ReLock();
	
	public void a() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		//lock置为true,dosomething的lock进入死循环==死锁
		doSomething();
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	
	public void doSomething() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		//............
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	
	public static void main(String[] args) throws InterruptedException {
		LockTest03 test = new LockTest03();
		test.a();
		
		Thread.sleep(1000);
		System.out.println(test.lock.getHoldCount());
	}
}

//可重入锁
class ReLock{
	//锁是否被占用
	private boolean isLocked = false;
	private Thread lockedBy = null; //存储线程
	private int holdCount=0;	//计数器
	
	//使用锁
	public synchronized void lock() throws InterruptedException {
		Thread t = Thread.currentThread();
		while (isLocked && lockedBy != t) {
			wait();
		}
		isLocked = true;
		lockedBy=t;
		holdCount++;
	}
	
	//释放锁
	public synchronized void unlock() {
		if (Thread.currentThread() == lockedBy) {
			holdCount--;
			if (holdCount==0) {	//计数器为0时才释放锁
				isLocked = false;
				notify();
				lockedBy=null;
			}
			
		}
		
	}

	public int getHoldCount() {
		return holdCount;
	}

	
}

运行结果:

1
2
1
0
0

4.java内置可重入锁(ReentrantLock)使用:

package com.sheye.others;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 可重入锁:锁可以重复使用+计数器
 * 
 * @author Administrator
 *
 */
public class LockTest04 {
	
	ReentrantLock lock = new ReentrantLock();
	
	public void a() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		//lock置为true,dosomething的lock进入死循环==死锁
		doSomething();
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	
	public void doSomething() throws InterruptedException {
		lock.lock();
		System.out.println(lock.getHoldCount());
		//............
		lock.unlock();
		System.out.println(lock.getHoldCount());
	}
	
	public static void main(String[] args) throws InterruptedException {
		LockTest04 test = new LockTest04();
		test.a();
		
		Thread.sleep(1000);
		System.out.println(test.lock.getHoldCount());
	}
}


运行结果:

1
2
1
0
0

CAS(比较并交换):
在这里插入图片描述
在这里插入图片描述
CAS原理:如果v等于a那么将b给a,否则什么都不做

package com.sheye.others;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * CAS比较并交换:利用的是cpu的cas
 * 只要看到Atomic 原子操作 就要想到cas
 * @author Sheye
 *
 */
public class CAS {
	//库存
	private static AtomicInteger stock = new AtomicInteger(5);
	
	public static void main(String[] args) {
		for (int i = 0; i < 5; i++) {
			new Thread(()->{
				//模拟网络延时
				try {
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				Integer left = stock.decrementAndGet();
				if (left<1) {
					System.out.println("抢完了");
					return;
				}
				System.out.println(Thread.currentThread().getName()+"抢了一件商品"+"--还剩--"+left);
				
			}) .start();
		}
		
		
	}

}

运行结果:

Thread-2抢了一件商品--还剩--2
Thread-0抢了一件商品--还剩--3
Thread-1抢了一件商品--还剩--4
Thread-4抢了一件商品--还剩--1
抢完了
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值