09多线程
1.高可用
2.高性能
3.高并发
多任务开启多线程----多任务是多线程的出发点----并行的路径
- 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();
- 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
抢完了