多线程

多线程(java.thread)

1.线程简介

多线程指多条路径,每个线程都有自己的工作内存,负责和主内存进行交互

方法间调用:普通方法调用,从哪里来到哪里去,闭合的一条路径,程序执行时必须等方法调用完才能往下执行

多线程使用:开辟了多条路径,不需要等到方法执行完就可以往下执行

2.线程实现

实现线程的三种方式:

  1. 继承Thread类(实现run()方法,调用时执行Thread类的start()方法)
  2. 实现Runnable接口(实现run()方法,调用时通过Thread对象调用start()方法)
  3. 实现Callable接口(实现call()方法)

2.1继承Thread类

• 执行线程必须调用start(),加入到调度器中

• 不一定立即执行,系统安排调度分配执行

• 直接调用run()不是开启多线程,是普通方法调用

start()方法开启一个线程,不保证立刻执行,交给cpu安排时间片进行调度,不用等方法执行完就可以执行下面的代码,但还是调用的run()方法

run()方法就是普通的方法调用,必须等到方法执行完才能往下执行

public class Listaa {
	public static void main (String[] args)throws IOException {
		// 仅仅创建了线程对象
		Say say = new Say();
		say.start();// 启动线程,不保证立刻执行,方法执行时,调用run()方法被执行
        // say.run();// 普通方法调用
		for(int i=0;i<10;i++){
			System.out.println("游戏");
		}
	}
}
class Say extends Thread{
    // 线程体的入口点
	@Override
	public void run() {
        // 线程执行的代码
		for(int i=0;i<10;i++){
			System.out.println("听歌");
		}
	}
}

public class WebDownloader {
	public void download(String url,String name) {
		try {
			FileUtils.copyURLToFile(new URL(url), new File(name));
		} catch (MalformedURLException e) {
			e.printStackTrace();
			System.out.println("不合法的url");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("下载失败"); 
		}
	}
}

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

	@Override
	public void run() {
		WebDownloader wd =new WebDownloader();
		wd.download(url, name);		
		System.out.println(name);
	}
	
	public static void main(String[] args) {
		TDownloader td1 =new TDownloader("http://upload.news.cecb2b.com/2014/0511/1399775432250.jpg","phone.jpg");
		TDownloader td2 =new TDownloader("http://p1.pstatp.com/large/403c00037462ae2eee13","spl.jpg");
		TDownloader td3 =new TDownloader("http://5b0988e595225.cdn.sohucs.com/images/20170830/d8b57e0dce0d4fa29bd5ef014be663d5.jpeg","success.jpg");
		
		//启动三个线程
		td1.start();
		td2.start();
		td3.start();
	}
}

2.2实现Runnable接口

通过Thread类对象调用start(),本质是一种静态代理模式(也是装饰器模式)

public class StartRun implements Runnable{
	/**
	 * 线程入口点
	 */
	@Override
	public void run() {
		for(int i=0;i<20;i++) {
			System.out.println("一边听歌");
		}
	}
	public static void main(String[] args) {			
		//创建实现类对象
		new Thread(new StartRun()).start();//本质是一种静态代理模式
		for(int i=0;i<20;i++) {
			System.out.println("一边coding");
		}
	}

}


public class WebDownloader {
	public void download(String url,String name) {
		try {
			FileUtils.copyURLToFile(new URL(url), new File(name));
		} catch (MalformedURLException e) {
			e.printStackTrace();
			System.out.println("不合法的url");
		} catch (IOException e) {
			e.printStackTrace();
			System.out.println("下载失败"); 
		}
	}
}

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

	@Override
	public void run() {
		WebDownloader wd =new WebDownloader();
		wd.download(url, name);		
		System.out.println(name);
	}
	
	public static void main(String[] args) {
		TDownloader td1 =new TDownloader("http://upload.news.cecb2b.com/2014/0511/1399775432250.jpg","phone.jpg");
		TDownloader td2 =new TDownloader("http://p1.pstatp.com/large/403c00037462ae2eee13","spl.jpg");
		TDownloader td3 =new TDownloader("http://5b0988e595225.cdn.sohucs.com/images/20170830/d8b57e0dce0d4fa29bd5ef014be663d5.jpeg","success.jpg");
		
		//启动三个线程
		new Thread(td1).start();
        new Thread(td2).start();
        new Thread(td3).start();
	}
}
/**
 * 共享资源,并发(线程安全)
 */
public class Web12306 implements Runnable{
	//票数
	private int ticketNums = 99;
	
	@Override
	public void run() {
		while(true) {
			if(ticketNums<0) {
				break;
			}
			try {
				Thread.sleep(200);// 线程睡眠0.2秒,这样线程不安全
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
	public static void main(String[] args) {
		//一份资源
		Web12306 web =new Web12306();
		System.out.println(Thread.currentThread().getName());
		//多个代理
		new Thread(web,"码畜").start();
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();;
	}
}

/**
 * 静态代理
 * 公共接口:
 * 1、真实角色
 * 2、代理角色
 */
public class StaticProxy {
	public static void main(String[] args) {
		new WeddingCompany(new You()).happyMarry();
		
		//new Thread(线程对象).start();
	}
}
interface Marry{
	void happyMarry();
}
//真实角色
class You implements Marry{

	@Override
	public void happyMarry() {
		System.out.println("you and 嫦娥终于奔月了....");
	}
	
}
//代理角色
class WeddingCompany implements Marry{
	//真实角色
	private Marry target;
	public WeddingCompany(Marry target) {
			this.target = target;
	}
	@Override
	public void happyMarry() {
		ready();
		this.target.happyMarry();
		after();
	}
	
	private void ready() {
		System.out.println("布置猪窝。。。。");
	}
	private void after() {
		System.out.println("闹玉兔。。。。");
	}
}
/**
 * Lambda表达式 简化线程(用一次)的使用,使用Lambda表达式接口必须只有一个方法
 */
public class LambdaThread {
	//静态内部类
	static class Test implements Runnable{		
		public void run() {
			for(int i=0;i<20;i++) {
				System.out.println("一边听歌");
			}
		}
	}
	public static void main(String[] args) {			
		//new Thread(new Test()).start();		
		
		//局部内部类
		class Test2 implements Runnable{		
			public void run() {
				for(int i=0;i<20;i++) {
					System.out.println("一边听歌");
				}
			}
		}		
		new Thread(new Test2()).start();
		
		//匿名内部类 必须借助接口或者父类
		new Thread(new Runnable() {
			public void run() {
				for(int i=0;i<20;i++) {
					System.out.println("一边听歌");
				}
			}
		}).start();
		
		//jdk8 简化  lambda表达式
		new Thread(()-> {
				for(int i=0;i<20;i++) {
					System.out.println("一边听歌");
				}
			}
		).start();
		
	}

}

/**
 * lambda推导 +参数+返回值
 */
public class LambdaTest03 {
	
	public static void main(String[] args) {
		IInterest interest = (int a,int c)-> {
			System.out.println("i like lambda -->"+(a+c));
			return a+c;
		};
		interest.lambda(100,200);
		
		interest = (a,c)-> {
			System.out.println("i like lambda -->"+(a+c));
			return a+c;
		};
		
		interest.lambda(200,200);
		
		interest = (a,c)-> {
			return a+c;
		};
		
		interest = (a,c)-> a+c;
		
		interest = (a,c)-> 100;
		
		System.out.println(interest.lambda(10, 20));
	}
}
interface IInterest{
	int lambda(int a,int b);
}
//外部类
class Interest implements IInterest{

	@Override
	public int lambda(int a,int c) {
		System.out.println("i like lambda -->"+(a+c));
		return a+c;
	}
	
}

2.3实现Callable接口

  1. 创建目标对象: CDownloader cd =new CDownloader(“图片地址”,“baidu.png”);

  2. 创建执行服务: ExecutorService ser=Executors.newFixedThreadPool(1);

  3. 提交执行: Future result =ser.submit(cd) ;

  4. 获取结果: boolean r =result.get();

  5. 关闭服务: ser.shutdownNow();

public class CDownloader implements Callable<Boolean>{
	private String url; //远程路径
	private String name;  //存储名字
	
	public CDownloader(String url, String name) {
		this.url = url; 
		this.name = name;
	}

	@Override
	public Boolean call() throws Exception {
		WebDownloader wd =new WebDownloader();
		wd.download(url, name);		
		System.out.println(name);
		return true;
	}
	
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		CDownloader cd1 =new CDownloader("http://upload.news.cecb2b.com/2014/0511/1399775432250.jpg","phone.jpg");
		CDownloader cd2 =new CDownloader("http://p1.pstatp.com/large/403c00037462ae2eee13","spl.jpg");
		CDownloader cd3 =new CDownloader("http://5b0988e595225.cdn.sohucs.com/images/20170830/d8b57e0dce0d4fa29bd5ef014be663d5.jpeg","success.jpg");
		
		//创建执行服务: 
		ExecutorService  ser=Executors.newFixedThreadPool(3);
		//提交执行: 
		Future<Boolean> result1 =ser.submit(cd1) ;
		Future<Boolean> result2 =ser.submit(cd2) ;
		Future<Boolean> result3 =ser.submit(cd3) ;
		//获取结果:  
		boolean r1 =result1.get();
		boolean r2 =result1.get();
		boolean r3 =result1.get();
		System.out.println(r3);
		//关闭服务:  
		ser.shutdownNow();

	}
}

3.线程状态

线程不能重新开启

线程阻塞进入就绪状态,不是运行状态

1.新生状态

2.就绪状态(1.start方法,2.阻塞解除,3.yield方法,线程自己中断自己,4.JVM从本地线程切换到其他线程)

3.运行状态

4.阻塞状态(1.sleep方法,抱着资源睡觉,2.wait方法,不占用资源,3.join方法,插队,4.IO流的read,writer,需要调用操作系统)(sleep称为wait状态,join称为timewait状态,wait和io流称为Blocked)

5.死亡状态(1.stop方法,2.destroy方法。不推荐,也都被废弃了)一般是等代码执行完或者想办法让代码执行完


1.线程状态 Thread.state
NEW                    // 新生状态
尚未启动的线程处于此状态。 
RUNNABLE               // 就绪状态和运行状态,统一称之为RUNNABLE
在Java虚拟机中执行的线程处于此状态。 
BLOCKED                // 阻塞状态 wait方法及IO流的read和writer方法          
被阻塞等待监视器锁定的线程处于此状态。 
WAITING                // 阻塞状态 sleep方法
正在等待另一个线程执行特定动作的线程处于此状态。 
TIMED_WAITING          // 阻塞状态 join方法
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 
TERMINATED             // 死亡状态
已退出的线程处于此状态。

2.活动线程数 Thread.activeCount();


3.1线程终止

// 不使用JDK提供的stop()/destroy()方法(它们本身也被JDK废弃了)。
// 提供一个boolean型的终止变量,当这个变量置为false,则终止线程的运行。
class Study implements Runnable{
//1)、线程类中 定义 线程体使用的标识
    private boolean flag =true;
    @Override
    public void run() {
    //2)、线程体使用该标识
        while(flag){
            System.out.println("study thread....");
        } 
    }
    //3)、对外提供方法改变标识
    public void stop(){
    	this.flag =false;
    } 
}

3.2线程暂停

// 静态方法 Thread.sleep();
// sleep(时间)指定当前线程阻塞的毫秒数; 
// sleep存在异常InterruptedException; 
// sleep时间达到后线程进入就绪状态; 
// sleep可以模拟网络延时、倒计时等。 
// 每一个对象都有一个锁,sleep不会释放锁;会占用线程资源,和wait进行对比
// 容易有并发异常
/**
 * 共享资源,并发(线程安全)
 */
public class Web12306 implements Runnable{
	//票数
	private int ticketNums = 99;
	@Override
	public void run() {
		while(true) {
			if(ticketNums<0) {
				break;
			}
			try {
				Thread.sleep(200);// 线程睡眠0.2秒,这样线程不安全
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
	public static void main(String[] args) {
		//一份资源
		Web12306 web =new Web12306();
		System.out.println(Thread.currentThread().getName());
		//多个代理
		new Thread(web,"码畜").start();
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();;
	}
}
/**
 * sleep模拟倒计时
 */
public class BlockedSleep03 {
	public static void main(String[] args) throws InterruptedException {
		//倒计时
		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;
			 }
		}
	}
	public static void test() throws InterruptedException {
		//倒数10个数,1秒一个
		int num = 10;
		while(true) {
			Thread.sleep(1000);
			System.out.println(num--);
		}
	}
}

3.3线程礼让

// 静态方法 Thread.yield();
// 礼让线程,让当前正在执行线程暂停
// 不是阻塞线程,而是将线程从**运行状态**转入**就绪状态**
// 让cpu调度器重新调度
/**
 * yield 礼让线程,暂停线程 直接进入就绪状态不是阻塞状态
 */
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");
	}
}

3.4线程插队

// join合并线程,待此线程执行完成后,再执行其他线程,**其他线程阻塞**
// join是成员方法,必须通过Thread对象进行调用,new Thread(对象).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(); //father被阻塞
			System.out.println("老爸接过烟,把零钱给了儿子");
		} catch (InterruptedException e) {
			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) {
				e.printStackTrace();
			}
		}
		System.out.println("赶紧买烟去。。。。");
		System.out.println("手拿一包中华回家了。。。。");
	}
}

3.5线程优先级

注意:优先级低只是意味着获得调度的概率低。并不是绝对先调用优先级高后调用优先级低的线程。

  1. Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程。线程调度器按照线程的优先级决定应调度哪个线程来执行。

  2. 线程的优先级用数字表示,范围从1到10

    2.1 Thread.MIN_PRIORITY = 1

    2.2 Thread.MAX_PRIORITY = 10

    2.3 Thread.NORM_PRIORITY = 5 // 所有线程创建后默认优先级是5

  3. 优先级的设定建议在start()调用前

  4. 使用下述方法获得或设置线程对象的优先级。

    4.1 int getPriority();

    4.2 void setPriority(int newPriority);

/**
 * 线程的优先级 1-10
 * 1、NORM_PRIORITY  5 默认
 * 2、MIN_PRIORITY   1 
 * 2、MAX_PRIORITY  10
 * 概率 ,不代表绝对的先后顺序
 */
public class PriorityTest {
	public static void main(String[] args) {
		System.out.println(Thread.currentThread().getPriority());
		
		MyPriority  mp = new MyPriority();
		
		Thread t1 = new Thread(mp,"adidas");
		Thread t2 = new Thread(mp,"NIKE");
		Thread t3 = new Thread(mp,"回力");
		Thread t4 = new Thread(mp,"李宁");
		Thread t5 = new Thread(mp,"双星");
		Thread t6 = new Thread(mp,"puma");
		
		//设置优先级在启动前
		t1.setPriority(10);
		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();
	}
}

3.6守护线程

Thread t = new Thread();
t.setDaemon(true);// 将用户线程调整为守护线程

3.7线程方法

方法功能
isAlive()判断线程是否还活着,即线程是否终止
setName()给线程起一个名字
getName()获取线程的名字
currentThread()取得当前正在运行的线程对象

4.线程同步(synchronized)

并发:同一个对象多个线程同时操作

目标:即保证安全又提高性能

4.1线程同步的条件:

1.形成队列,同一个时间点只允许一个线程操作

2.锁机制,当一个线程获取对象( 资源)的排他锁,独占资源,其他线程必须等待,使用后释放锁

4.2锁机制的问题:

1.一个线程持有锁会导致其他所有需要此锁的线程挂起

2.频繁的加锁,释放锁会引起性能问题

3.优先级高的线程等待优先级低的线程释放锁会引起优先级倒置,性能问题

4.3 synchronized方法和synchronized块

范围太大,效率低下

范围太小,线程不安全

提高效率:先判断资源是否存在,再访问资源,我们锁的是访问资源这一块,好的方法是在锁外判断下资源是否存在,在锁里再判断下资源是否存在,再访问资源。

4.3.1 synchronized方法

锁的是对象的资源,不是方法,下面锁的是this,也就是SafeWeb12306这个对象,要保证方法内的资源都是属于这个对象的。

/**
 * 线程安全: 在并发时保证数据的正确性、效率尽可能高
 * synchronized
 * 1、同步方法
 */
public class SynTest01 {

	public static void main(String[] args) {
		//一份资源
		SafeWeb12306 web =new SafeWeb12306();
		//多个代理
		new Thread(web,"码畜").start();
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();;
	}

}

class SafeWeb12306 implements Runnable{
	//票数
	private int ticketNums =10;
	private boolean flag = true;
	@Override
	public void run() {
		while(flag) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			test();
		}
	}	
	//线程安全  同步
	public synchronized void test() {
		if(ticketNums<=0) {
			flag = false;
			return ;
		}
		//模拟延时
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
	}
    // 或者
    public synchronized void test1() {
        synchronized(this){
            if(ticketNums<=0) {
			flag = false;
			return ;
		}
		//模拟延时
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
        }
	}
}
4.3.2 synchronized块

我们锁的是账户,不是取款机

提高效率:先判断资源是否存在,再访问资源,我们锁的是访问资源这一块

public class SynBlockTest01 {
	public static void main(String[] args) {		
		//账户
		Account account =new Account(1000,"结婚礼金");
		SynDrawing you = new SynDrawing(account,80,"可悲的你");
		SynDrawing wife = new SynDrawing(account,90,"happy的她");
		you.start();
		wife.start();
	}
} 
 //模拟取款 线程安全
class SynDrawing extends Thread{
	Account account ; //取钱的账户
	int drawingMoney ;//取的钱数
	int packetTotal ; //口袋的总数	
	
	public SynDrawing(Account account, int drawingMoney,String name) {
		super(name);
		this.account = account;
		this.drawingMoney = drawingMoney;
	}

	@Override
	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);
		}
	}
}

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(10000);
		System.out.println(list.size());
	}
}

4.4 性能提升

public class SynBlockTest03 {

	public static void main(String[] args) {
		//一份资源
		SynWeb12306 web =new SynWeb12306();
		//多个代理
		new Thread(web,"码畜").start();
		new Thread(web,"码农").start();
		new Thread(web,"码蟥").start();;
	}

}

class SynWeb12306 implements Runnable{
	//票数
	private int ticketNums =10;
	private boolean flag = true;
	@Override
	public void run() {
		while(flag) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			test5();
		}
	}	
	//线程安全:尽可能锁定合理的范围(不是指代码 指数据的完整性)
	//double checking
	public  void test5() {
		if(ticketNums<=0) {//考虑的是没有票的情况
			flag = false;
			return ;
		}
		synchronized(this) {			
			if(ticketNums<=0) {//考虑最后的1张票
				flag = false;
				return ;
			}
			//模拟延时
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
	//线程不安全  范围太小锁不住
	public  void test4() {
		synchronized(this) {
				if(ticketNums<=0) {
					flag = false;
					return ;
				}
			}
			//模拟延时
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		
	}
	//线程不安全  ticketNums对象在变
	public  void test3() {
		synchronized((Integer)ticketNums) {
			if(ticketNums<=0) {
				flag = false;
				return ;
			}
			//模拟延时
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}
	//线程安全 范围太大 -->效率低下
	public  void test2() {
		synchronized(this) {
			if(ticketNums<=0) {
				flag = false;
				return ;
			}
			//模拟延时
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
		}
	}

	//线程安全  同步
	public synchronized void test1() {
		if(ticketNums<=0) {
			flag = false;
			return ;
		}
		//模拟延时
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
	}
}

4.4 并发容器

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

4.5 死锁

多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能进行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”的问题。

避免:不要在同一个代码块中,持有多个对象的锁

5.生产消费者(线程协作或线程通信)

生产者和消费者不能直接交流

生产者---------仓库---------消费者
在这里插入图片描述

分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消 费者之间相互依赖,互为条件

1.对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后, 又需要马上通知消费者消费

2.对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新 产品以供消费

3.在生产者消费者问题中,仅有synchronized是不够的

​ 3.1 synchronized可阻止并发更新同一个共享资源,实现了同步

​ 3.2 synchronized不能用来实现不同线程之间的消息传递(通信)

方法作用
final void wait()表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
final void wait(long timeout)指定等待的毫秒数
final void notifiy()唤醒一个处于等待状态的线程
final void notifyAll()唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度
5.1 管程法

在这里插入图片描述

下面的this指的是缓冲区的对象

/**
 * 协作模型:生产者消费者实现方式一:管程法
 * 借助缓冲区
 */
public class CoTest01 {
	public static void main(String[] args) {
		SynContainer container = new SynContainer();
		new Productor(container).start();
		new Consumer(container).start();
	}
}
//生产者
class Productor extends Thread{
	SynContainer container  ;	
	public Productor(SynContainer container) {
		this.container = container;
	}

	public void run() {
		//生产
		for(int i=0;i<100;i++) {
			System.out.println("生产-->"+i+"个馒头");
			container.push(new Steamedbun(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{
	Steamedbun[] buns = new Steamedbun[10]; //存储容器
	int count = 0; //计数器
	//存储 生产
	public synchronized void push(Steamedbun bun) {
		//何时能生产  容器存在空间
		//不能生产 只有等待
		if(count == buns.length) {
			try {
				this.wait(); //线程阻塞  消费者通知生产解除
			} catch (InterruptedException e) {
			}
		}
		//存在空间 可以生产
		buns[count] = bun;
		count++;
		//存在数据了,可以通知消费了
		this.notifyAll();
	}
	//获取 消费
	public synchronized Steamedbun pop() {
		//何时消费 容器中是否存在数据
		//没有数据 只有等待
		if(count == 0) {
			try {
				this.wait(); //线程阻塞  生产者通知消费解除
			} catch (InterruptedException e) {
			}
		}
		//存在数据可以消费
		count --;
		Steamedbun bun = buns[count] ;		
		this.notifyAll(); //存在空间了,可以唤醒对方生产了
		return bun;
	}
}
//馒头
class Steamedbun{
	int id;
	public Steamedbun(int id) {
		this.id = id;
	}
	
}

5.2 信号灯法
/**
 * 协作模型:生产者消费者实现方式二:信号灯法
 * 借助标志位
 */
public class CoTest02 {
	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<20;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<20;i++) {
			tv.watch();
		}
	}
}
//同一个资源 电视
class Tv{
	String voice;
	//信号灯
	//T 表示演员表演 观众等待
	//F 表示观众观看 演员等待
	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;
	}
}

6.高级主题

6.1 任务定时调度

public class TimerTest01 {

	public static void main(String[] args) {
		Timer timer = new Timer();
		//执行安排
		//timer.schedule(new MyTask(), 1000);  //执行任务一次
		//timer.schedule(new MyTask(), 1000,200); //执行多次
		Calendar cal = new GregorianCalendar(2099999,12,31,21,53,54);
		timer.schedule(new MyTask(), cal.getTime(),200); //指定时间
	}

}
//任务类
class  MyTask extends TimerTask{

	@Override
	public void run() {
		for(int i=0;i<10;i++) {
			System.out.println("放空大脑休息一会");
		}
		System.out.println("------end-------");
	}
	
}

6.2 quartz(任务调度框架)

1.Scheduler(调度器,控制所有的调度)
2.Trigger(触发器,采用DSL模式)
3.JobDetail(需要处理的Job)
4.Job(执行逻辑)

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---------");
    }

}
public class QuartzTest {

  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();
    Trigger trigger  = newTrigger().withIdentity("trigger1", "group1").startAt(runTime)
            .withSchedule(simpleSchedule().withIntervalInSeconds(5).withRepeatCount(3)).build();
    // 5、注册任务和触发条件
    sched.scheduleJob(job, trigger);

    // 6、启动
    sched.start();

    try {
      // 100秒后停止
      Thread.sleep(100L * 1000L);
    } catch (Exception e) {
    }
    sched.shutdown(true);
  }

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

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

  }

6.3 指令重排

内存执行顺序:
1.获取指令
2.从寄存器拿取值,copy到工作内存中‘
3.操作
4.写回寄存器
如果不存在数据依赖,后面的代码可能会先执行
写后读 a = 1;b = a; 写一个变量之后,再读这个位置。
写后写 a = 1;a = 2; 写一个变量之后,再写这个变量。
读后写 a = b;b = 1; 读一个变量之后,再写这个变量。
下面代码可能3可能会先执行

1.subTotal = price + fee;
2.total += subtotal;
3.flag = true;

6.3 DCL单例模式

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

}

6.4 threadLocal

就是保存了多个线程的各自的成员变量
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP
请求,用户身份信息等,这样一个线程的所有调用到的方法都可以非常
方便地访问这些资源。
• Hibernate的Session 工具类HibernateUtil
• 通过不同的线程对象设置Bean属性,保证各个线程Bean对象的独立

/**
 * ThreadLocal:每个线程自身的存储本地、局部区域
 *  get/set/initialValue
 * @author 裴新 QQ:3401997271
 *
 */
public class ThreadLocalTest01 {
	//private static ThreadLocal<Integer> threadLocal = new ThreadLocal<> ();//默认null
	//更改初始化值方法1
	/*private static ThreadLocal<Integer> threadLocal = new ThreadLocal<> () {
		protected Integer initialValue() {
			return 200;
		}; 
	};*/
	//更改初始化值方法2
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()-> 200);
	public static void main(String[] args) {
		//获取值
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());// 200		
		//设置值
		threadLocal.set(99);
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());// 99
		
		new Thread(new MyRun()).start();//5
		new Thread(new MyRun()).start();//5
	}	
	public static  class MyRun implements Runnable{
		public void run() {
			threadLocal.set(5);
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());		
		}
	}
	
}

/**
 * ThreadLocal:分析上下文 环境  起点
 * 1、构造器: 哪里调用 就属于哪里 找线程体
 * 2、run方法:本线程自身的
 */
public class ThreadLocalTest03 {	
	private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()-> 1);
	public static void main(String[] args) {
		new Thread(new MyRun()).start();
		new Thread(new MyRun()).start();
	}	
	public static  class MyRun implements Runnable{
		public MyRun() {
			threadLocal.set(-100);// 此线程是属于main方法的线程
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	
		}
		public void run() {
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	
			//new Thread(new MyRunxxx()).start();
		}
	}
	
}

子类

/**
 * InheritableThreadLocal:继承上下文 环境的数据 ,拷贝一份给子线程
 */
public class ThreadLocalTest04 {	
	private static ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
	public static void main(String[] args) {
		threadLocal.set(2);
		System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	//2
		
		//线程由main线程开辟
		new Thread(()->{
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	//2
			threadLocal.set(200);
			System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());	//200
		}) .start();
		
	}	
	
}

6.5 可重入锁

如果某个线程试图获取一个已经由它自己持有的锁时,那么这个请求会立刻成功,并且会将这个锁的计数值加1,而当线程退出同步代码块时,计数器将会递减,当计数值等于0时,锁释放。

// 第一次获得锁
synchronized(this) {
	while(true) {
		// 第二次获得同样的锁
		synchronized(this) {
			System.out.println("ReentrantLock!");
		}
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
 }
class ReentrantLockTest {
public synchronized void a() { 
}
public synchronized void b() { 
}
/**
* 很明显的可重入锁用法
*/
public synchronized void all() {
	this.a(); //此时对象的锁计数值已经达到2了
	this.b(); }
}

6.6 CAS(比较并实现)

乐观锁的实现
锁分为两类:
• 悲观锁:synchronized是独占锁即悲观锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
• 乐观锁:每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

1.有三个值:一个当前内存值V、旧的预期值A、将更新的值B。先获取到内存
当中当前的内存值V,再将内存值V和原值A作比较,要是相等就修改为要修
改的值B并返回true,否则什么都不做,并返回false;
2.CAS是一组原子操作,不会被外部打断; 
3.属于硬件级别的操作(利用CPU的CAS指令,同时借助JNI来完成的非阻塞算 法),效率比加锁操作高。
4.ABA问题:如果变量V初次读取的时候是A,并且在准备赋值的时候检查到它
仍然是A,那能说明它的值没有被其他线程修改过了吗?如果在这段期间曾
经被改成B,然后又改回A,那CAS操作就会误认为它从来没有被修改过。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值