班组建设管理网站,临沂seo公司稳健火星,搜索引擎优化的报告,网站修改工具多线程学习笔记#xff08;重点#xff01;#xff01;#xff01;#xff09; 一、线程、进程、多线程1.线程创建1.Thread1.练习案例#xff1a;网图下载 2.Runnable1.练习案例#xff1a;买火车票2.练习案例#xff1a;龟兔赛跑 3.小结4. callable 2.静态代理模式3.L… 多线程学习笔记重点 一、线程、进程、多线程1.线程创建1.Thread1.练习案例网图下载 2.Runnable1.练习案例买火车票2.练习案例龟兔赛跑 3.小结4. callable 2.静态代理模式3.Lamda表达式4.线程停止5.线程休眠sleep6.线程礼让yield10.线程强制执行join 7.线程状态观测8.线程的优先级Priority9.线程同步1. 存在以下问题2. 三大不安全 3. 同步4. 死锁1.死锁避免方法 5.Lock锁6. 线程协作7.线程池 一、线程、进程、多线程
多任务边吃饭边玩手机 现实生活中有很多这样同时做多件事情的例子看起来是多个任务都在做其实本质上我们的额大脑在同一时间依旧只做了一件事。多线程Thread一条路多个道游戏每个人都有一个账号两个人同时玩一个游戏编程main主函数和子函数同时运行 图片来源西部开源 进程(Process)在操作系统中运行的程序就是进程一个进程可以有多个线程如视频中同时听到声音看图像看弹幕等 程序、进程、线程的关系 一个程序有一个进程一个进程可以有多个线程如视频同时有听声音看图像看弹幕等等。 程序是指令和数据的有序集合其本身没有任何运行含义是一个静态概念。 进程而进程是执行程序的一次执行过程是动态的概念是系统资源分配的单位。 线程 在进程中通常一个进程中可以包含若干个线程当然一个进程中至少有一个线程要不没有意义线程是CPU调度和执行的单位。
注意 很多多线程是模拟出来的真正的多线程是指有多个cpu即多核如服务器。模拟多线程在一个cpu的情况下在同一个时间点cpu只能执行一个代码因为切换的很快所以就有同时执行的错觉。
核心概念 1. 线程就是独立的执行路径 2. 在程序运行时即使没有自己创建线程后台也会有多个线程如主线程gc线程 3. main()称之为主线程为系统入口用于执行整个程序 4. 在一个进程中如果开辟了多个线程线程的运行由调度器安排调度调度器是与操作系统紧密相关的先后顺序是不能人为干预的。 5. 对同一份资源操作时会存在资源抢夺问题需要加入并发控制 6. 线程会带来额外的开销如cpu调度时间并发控制开销。 7. 每个线程在自己的工作内存交互内存控制不当会造成数据不一致。
1.线程创建
三种创建方式
ThreadThread class ————继承Thread类重点RunnableRunnable接口 ————实现Runnable接口重点CallableCallable接口 ————实现Callable接口了解
1.Thread
自定义线程类继承Thread类重写run()方法编写线程执行体创建线程对象调用start()方法启动线程
注线程不一定立即执行由CUP安排调度
/*** author yxf*/
//创建线程方法一继承Thread类重写run()方法调用start开启进程
//总结注意线程开启不一定立即执行由CPU调度执行
public class TestThread extends Thread{Overridepublic void run(){//run方法线程体for (int i 0; i 5510; i) {System.out.println(我在听歌--------------------------------- i);}}//main线程主线程public static void main(String[] args) {//创建一个线程对象TestThread testThread new TestThread();//调用start()方法开启线程testThread.start();for (int i 0; i 11500; i) {System.out.println(在学习多线程--- i);}}
}1.练习案例网图下载
导入外部包commons IO并添加为库。 把下载好的commons IO包放到项目中 右键lib文件选择Add as Library… 点击OK 查看 代码实现
/*** author yxf*/
//联系Thread实现多线程同步下载图片
public class TestThread1 extends Thread{private String url; //网路图片地址private String name; //保存的文件名public static void main(String[] args) {TestThread1 t1 new TestThread1(https://wx2.sinaimg.cn/mw2000/b7a7a24fgy1h82cus8xxlj22qj3pqkjo.jpg,shaking.jpg);TestThread1 t2 new TestThread1(https://wx2.sinaimg.cn/orj360/b7a7a24fly1h6edp8a91jj22ph3ttkjq.jpg,想我了没.jpg);t1.start();t2.start();}public TestThread1(String url,String name){this.url url;this.name name;}//下载图片线程的执行体Overridepublic void run(){WebDownloader webDownloader new WebDownloader();webDownloader.downloader(url,name);System.out.println(下载文件名为name);}
}//下载器
class WebDownloader{//下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println(IO异常downloader方法出现问题);}}
}运行结果
2.Runnable
实现Runnable 学习提示查看JDK帮助文档 定义MyRunnable类实现Runnable接口。实现run()方法编写线程执行体。创建线程对象调用start()方法启动线程。
注推荐使用Runnable对象因为Java单继承的局限性。
/*** author yxf*/
public class TestRunnable implements Runnable{public static void main(String[] args) {//创建实现类对象TestRunnable testRunnable new TestRunnable();//创建代理类对象Thread thread new Thread(testRunnable);//启动thread.start();for (int i 0; i 1000; i) {System.out.println(我在学习多线程------- i);}}Overridepublic void run() {//线程体for (int i 0; i 110; i) {System.out.println(我在听歌----------------------- i);}}
}Runnable实现网图下载
/*** author yxf*/
public class TestRunnable1 implements Runnable{private String url; //网路图片地址private String name; //保存的文件名public static void main(String[] args) {TestThread1 t1 new TestThread1(https://wx2.sinaimg.cn/mw2000/b7a7a24fgy1h82cus8xxlj22qj3pqkjo.jpg,shaking.jpg);TestThread1 t2 new TestThread1(https://wx2.sinaimg.cn/orj360/b7a7a24fly1h6edp8a91jj22ph3ttkjq.jpg,想我了没.jpg);new Thread(t1).start();new Thread(t2).start();}public TestRunnable1(String url, String name){this.url url;this.name name;}//下载图片线程的执行体Overridepublic void run(){WebDownloader webDownloader new WebDownloader();webDownloader.downloader(url,name);System.out.println(下载文件名为name);}
}//下载器
class WebDownloader1{//下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println(IO异常downloader方法出现问题);}}
}1.练习案例买火车票
/*** author yxf*/
//多个线程同时操作一个对象
//买火车票例子//发现问题多个线程操作同一个资源的情况下线程不安全数据混乱。
public class TestRunnable3 implements Runnable{//总票数private int ticketNums 10;Overridepublic void run() {while (true){if(ticketNums 0){break;}//模拟延时try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}//Thread.currentThread().getName()获取当前线程名称System.out.println(Thread.currentThread().getName()--拿到了第 ticketNums --张票);}}public static void main(String[] args) {TestRunnable3 ticket new TestRunnable3();new Thread(ticket,shakingChloe).start();new Thread(ticket,janiceMan).start();new Thread(ticket,黄牛党).start();}
}2.练习案例龟兔赛跑
/*** author yxf*/
//模拟龟兔赛跑
public class TestRunnable5 implements Runnable{//胜利者private static String winner;Overridepublic void run() {for (int i 0; i 100; i) {//模拟兔子睡觉if(Thread.currentThread().getName().equals(兔子) i%10 0){try {Thread.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}//判断比赛是否结束boolean flag gameOver(i);if(flag){break;}System.out.println(Thread.currentThread().getName()--跑了i步);}}//判断是否完成比赛private boolean gameOver(int steps){//判断是否有胜利者if(winner ! null){ //已经存在胜利者了return true;}{if(steps 100){winner Thread.currentThread().getName();System.out.println(winner is winner);return true;}}return false;}public static void main(String[] args) {TestRunnable5 game new TestRunnable5();new Thread(game,兔子).start();new Thread(game,乌龟).start();}
}3.小结 继承Thread类 1. 子类继承Thread类具备多线程能力。 2. 启动线程子类对象.start(); 3. 不建议使用避免OOP单继承局限性。 实现Runnable接口 1. 实现接口Runnable具有多线程能力。 2. 启动线程传入目标对象Thread对象.start(); 3. 推荐使用避免单继承局限性灵活方便方便同一个对象被多个线程使用。
4. callable
可以定义返回值 可以抛出异常
实现Callable接口需要返回值类型。重写call方法需要抛出异常。创建目标创建执行服务ExecutorService xx Executors.newFixedThreadPool(2);提交执行Future r1 ser.submit(t1);获取结果boolean rs1 r1.get();关闭服务ser.shutdownNow();
下载图片案例
/*** author yxf*///线程创建方式三实现callable接口重写call()方法。public class TestCallable implements CallableBoolean {private String url; //网路图片地址private String name; //保存的文件名public TestCallable(String url, String name) {this.url url;this.name name;}Overridepublic Boolean call(){WebDownloader webDownloader new WebDownloader();webDownloader.downloader(url,name);System.out.println(下载文件名为name);return true;}public static void main(String[] args) throws ExecutionException,InterruptedException {TestCallable t1 new TestCallable(https://wx2.sinaimg.cn/mw2000/b7a7a24fgy1h82cus8xxlj22qj3pqkjo.jpg,shaking.jpg);TestCallable t2 new TestCallable(https://wx2.sinaimg.cn/orj360/b7a7a24fly1h6edp8a91jj22ph3ttkjq.jpg,想我了没.jpg);//创建执行服务ExecutorService ser Executors.newFixedThreadPool(2);//提交执行FutureBoolean r1 ser.submit(t1);FutureBoolean r2 ser.submit(t2);//获取结果boolean rs1 r1.get();boolean rs2 r2.get();//关闭服务ser.shutdownNow();}//下载器class WebDownloader{//下载方法public void downloader(String url,String name){try {FileUtils.copyURLToFile(new URL(url),new File(name));} catch (IOException e) {e.printStackTrace();System.out.println(IO异常downloader方法出现问题);}}}
}2.静态代理模式
/*** author yxf*/
//静态代理模式总结
//真实对象和代理对象都要实现同一个接口
//代理对象要代理真实角色//好处//代理对象可以做很多真实对象做不了的事情//真实对象专注自己的事情
public class StaticProxy {public static void main(String[] args) {You you new You(); //结婚对象WeddingCompany wc new WeddingCompany(you);wc.HappyMarry();}
}
interface Marry{void HappyMarry();
}//真实角色谁去结婚
class You implements Marry{Overridepublic void HappyMarry() {System.out.println(BKLY即刻结婚);}
}//代理角色帮助你结婚婚庆公司
class WeddingCompany implements Marry{//代理谁-- 真实目标角色private Marry target;public WeddingCompany (Marry target){this.target target;}Overridepublic void HappyMarry() {before();this.target.HappyMarry();after();}private void before() {System.out.println(结婚前布置婚礼现场。);}private void after() {System.out.println(结婚后收尾款。);}
}3.Lamda表达式
Lamda是希腊字母表中排序第十一位字母Lamda是简化程序的作用。避免匿名内部类定义过多。其实质属于函数式编程的概念。
有了函数式接口就可以用Lamda表达式创建该接口的对象。
理解Functional Interface函数式接口是学习Java8 lamda表达式的关键。函数式接口定义 任何接口如果只包含唯一一个抽象方法那么它就是一个函数式接口。 public interface Runnable{public abstract void run();
}对于函数式接口我们可以通过lamda表达式来创建接口对象。
推导Lamdm表达式
/*** author yxf*/public class TestLambda {//3.静态内部类static class Like2 implements ILike{Overridepublic void Lambda() {System.out.println(I Like Lambda2);}}public static void main(String[] args) {ILike like new Like();like.Lambda();like new Like2();like.Lambda();//4.局部内部类class Like3 implements ILike{Overridepublic void Lambda() {System.out.println(I Like Lambda3);}}like new Like3();like.Lambda();//5.匿名内部类,没有类的名称必须借助接口或者父类like new ILike() {Overridepublic void Lambda() {System.out.println(I Like Lambda4);}};like.Lambda();//6.用lambda简化like () -{System.out.println(I Like Lambda5);};like.Lambda();}
}//1.定义一个函数式接口
interface ILike{void Lambda();
}//2.实现类
class Like implements ILike{Overridepublic void Lambda() {System.out.println(I Like Lambda);}
}练习案例 /*** author yxf*/
public class TestLambda01 {static class Love1 implements ILove{Overridepublic void love(int a) {System.out.println(I Love You -- a);}}public static void main(String[] args) {ILove love new Love();love.love(2);love new Love1();love.love(3);class Love2 implements ILove{Overridepublic void love(int a) {System.out.println(I Love You -- a);}}love new Love2();love.love(4);love new ILove(){Overridepublic void love(int a) {System.out.println(I Love You -- a);}};love.love(5);//Lambda 简化1.参数类型可以不写参数类型love (a) - {System.out.println(I Love You -- a);};love.love(520);//简化2.简化括号love a - {System.out.println(I Love You -- a);};love.love(521);//简化3.去掉花括号love a - System.out.println(I Love You -- a);love.love(45);//总结//Lambda表达式只能有一行代码的情况下才能简化成为一行如果有多行就必须要用代码块{}包裹起来//前提是接口为函数式接口//多个参数也可以去掉参数类型要去掉就全都去掉必须加上括号}
}interface ILove{void love(int a);
}class Love implements ILove{Overridepublic void love(int a) {System.out.println(I Love You -- a);}
}4.线程停止
线程状态图片来源西部开源
线程方法图片来源西部开源
线程停止 1. 不推荐使用JDK提供的stop()、destroy()方法。已废弃。 2. 推荐线程自己停止下来。 3. 建议使用一个标示位进行终止变量当flagfalse则终止线程运行。
/*** author yxf*/
//测试stop
//1.建议线程正常终止 --- 利用次数不建议死循环
//2.建议使用标示位 ---设置一个标示位
//3.不要使用stop或者destroy等过期或者JDK不建议使用的方法
public class TestStop implements Runnable{//1.设置一个标示位private boolean flag true;Overridepublic void run() {int i 0;while (flag){System.out.println(run...Thread---i);}}//2.设置一个公开的方法停止线程转换标示位public void stop(){this.flag false;}public static void main(String[] args) {TestStop ts new TestStop();new Thread(ts).start();for (int i 0; i 1000; i) {System.out.println(main --- i);if(i 900){//调用stop方法切换标示位让线程停止ts.stop();System.out.println(线程该停止了--------------------);}}}
}5.线程休眠sleep
sleep时间指定当前线程阻塞的毫秒数1000毫秒1秒。sleep存在异常InterruptedException。sleep时间达到后线程进入就绪状态。sleep可以模拟网络延时倒计时等。每一个对象都有一个锁sleep不会释放锁。
作用放大问题的发生性。避免多个线程操作一个对象譬如买票小明拿走的十张票其余人都没票。
模拟倒计时
/*** author yxf*/
//模拟倒计时
public class TestSleep {public static void main(String[] args) {try {tenDown();} catch (InterruptedException e) {e.printStackTrace();}}//模拟倒计时public static void tenDown() throws InterruptedException {int num 10;while (true){Thread.sleep(1000);System.out.println(num--);if (num 0) {break;}}}
}打印系统当前时间 public static void main(String[] args) {//打印当前系统时间Date startTime new Date(System.currentTimeMillis());//获取系统当前时间while (true){try {Thread.sleep(1000);System.out.println(new SimpleDateFormat(HH:mm:ss).format(startTime));startTime new Date(System.currentTimeMillis()); //更新当前时间} catch (InterruptedException e) {e.printStackTrace();}}}6.线程礼让yield
礼让线程让当前正在执行的线程暂停但不阻塞。将线程从运行状态转为就绪状态。让cpu重新调度礼让不一定成功看CPU心情。
/*** author yxf*/
public class TestYield {public static void main(String[] args) {MyYield myYield new MyYield();new Thread(myYield,a).start();new Thread(myYield,b).start();}
}
class MyYield implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName()线程开始执行);Thread.yield(); //线程礼让System.out.println(Thread.currentThread().getName()线程停止执行);}
}10.线程强制执行join
Join合并线程待此线程执行完成后再执行其它线程其它线程阻塞。
/*** author yxf*/
//测试Join方法想象为插队
public class TestJoin implements Runnable{Overridepublic void run() {for (int i 0; i 1000; i) {System.out.println(线程VIP来了i);}}public static void main(String[] args) throws InterruptedException {//启动我们的线程TestJoin tj new TestJoin();Thread thread new Thread(tj);thread.start();//主线程for (int i 0; i 500; i) {if(i 200){thread.join(); //插队}System.out.println(main i);}}
}7.线程状态观测
/*** author yxf*/
//观察测试线程的状态
public class TestState {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(()- {for (int i 0; i 5; i) {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(/);});//观察状态Thread.State state thread.getState();System.out.println(state); //NEW//观察启动后thread.start(); //启动线程state thread.getState();System.out.println(state); //Runwhile (state ! Thread.State.TERMINATED){Thread.sleep(100);state thread.getState();System.out.println(state);}//thread.start(); //死亡之后的线程不能再次启动}
}8.线程的优先级Priority
Java提供一个线程调度器来监控程序启动后进入就绪状态的所有线程线程调度器按照优先级决定应该调度哪个线程来执行。线程的优先级用数字表示范围从110默认是5。使用以下方式改变或获取优先级 getPriority() setPriority(int xx)
优先级的设定建议再start()调度前。优先级低只是意味着获得调度的概率低并不是优先级低就不会被调用看CPU心情调度。
/*** author yxf*/
public class TestPriority {public static void main(String[] args) {//主线程默认优先级 5System.out.println(Thread.currentThread().getName() --- 优先级是Thread.currentThread().getPriority());MyPriority myPriority new MyPriority();Thread thread new Thread(myPriority,折);Thread thread2 new Thread(myPriority,);Thread thread3 new Thread(myPriority,);Thread thread4 new Thread(myPriority,);Thread thread5 new Thread(myPriority,);Thread thread6 new Thread(myPriority,⚽️);Thread thread7 new Thread(myPriority,);Thread thread8 new Thread(myPriority,❄️);//没设置优先级默认是5thread.start();//先设置优先级再启动thread2.setPriority(3);thread2.start();thread3.setPriority(2);thread3.start();thread4.setPriority(9);thread4.start();thread5.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY 10thread5.start();thread6.setPriority(2);thread6.start();thread7.setPriority(Thread.MIN_PRIORITY); //MIN_PRIORITY 1thread7.start();thread8.setPriority(8);thread8.start();}
}
class MyPriority implements Runnable{Overridepublic void run() {System.out.println(Thread.currentThread().getName() --- 优先级是Thread.currentThread().getPriority());}
}
### 13.守护daemon线程
1. 线程分为**用户线程**和**守护线程**。
2. 虚拟机必须确保用户线程执行完毕。
3. 虚拟机不用等待守护线程执行完毕。
4. 如后台记录操作日志监控内存垃圾回收等待。
java
/*** author yxf*/
public class TestDaemon {public static void main(String[] args) {God god new God();You you new You();Thread thread new Thread(god);thread.setDaemon(true); //默认是false表示是用户线程正常的线程都是用户线程。。。thread.start(); // 启动守护线程new Thread(you).start(); //启动用户线程}
}//上帝 -- 守护线程
class God implements Runnable{Overridepublic void run() {while (true){System.out.println(上帝保佑着你);}}
}
//你 -- 用户线程
class You implements Runnable{Overridepublic void run() {for (int i 0; i 100; i) {System.out.println(苟活着 i);}System.out.println(good boy---------);}
}9.线程同步
并发同一个对象被多个线程同时操作。线程同步 处理多线程问题时多个线程访问同一个对象并且某些线程还想修改这个对象。这是好我们就需要线程同步线程同步其实就是一种等待机制多个需要同时访问此对象的线程进入这个对象的等待池 形成队列等待前面线程使用完毕下一个线程再使用。队列和锁队列锁才能保证安全性。锁机制synchronized
1. 存在以下问题
一个线程持有锁会导致其它所有需要此锁的线程挂起。在多线程竞争下加锁释放锁会导致比较多的上下文切换和调度延时引起性能问题。如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置引起性能问题。
2. 三大不安全
不安全的买票有负数和重复的数
/*** author yxf*/
//线程不安全有负数
public class UnsafeTicket {public static void main(String[] args) {BuyTicket ticket new BuyTicket();new Thread(ticket,shakingChloe).start();new Thread(ticket,janiceMan).start();new Thread(ticket,黄牛党).start();}
}class BuyTicket implements Runnable{//总票数private int ticketNums 10;//外部停止方式boolean flag true;Overridepublic void run() {while (flag){buy();}}private void buy(){//判断是否邮票if(ticketNums 0){flag false;return;}//模拟延时try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}//买票System.out.println(Thread.currentThread().getName()--拿到了第 ticketNums --张票);}
}不安全银行
/*** author yxf*/
//不安全的取钱
//两个人取银行取钱账户
public class UnsafeBank {public static void main(String[] args) {//账户Account account new Account(100,结婚基金);Drawing you new Drawing(account,50,本人);Drawing girlFriend new Drawing(account,100,girlFriend);you.start();girlFriend.start();}
}//账户
class Account{int money; //余额String name; //卡名public Account(int money,String name){this.money money;this.name name;}
}//银行模拟取款
class Drawing extends Thread{Account account; //账户int drawingMoney; //取了多少钱int nowMoney; //现在手里有多少钱public Drawing(Account account,int drawingMoney,String name){super(name);this.account account;this.drawingMoney drawingMoney;}//取钱Overridepublic void run(){//判断有没有钱if(account.money - drawingMoney 0){System.out.println(Thread.currentThread().getName() 余额不足);return;}//sleep可以放大问题的发生性try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//卡内余额 余额 - 你取的钱account.money account.money - drawingMoney;//你手里的钱 手上的钱 取出来的钱nowMoney nowMoney drawingMoney;System.out.println(account.name --- 余额为 account.money);//Thread.currentThread().getName() getNameSystem.out.println(this.getName() 手里的钱 nowMoney);}
}不安全的列表
import java.util.ArrayList;
import java.util.List;/*** author yxf*/
//线程不安全的集合
public class UnsafeList {public static void main(String[] args) {ListString list new ArrayListString();for (int i 0; i 50000; i) {new Thread(() - {list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}3. 同步
由于我们可以通过private关键字来保证睡的对象只能被方法访问所以我们只需要针对方法提出一套机制这套机制就是synchronized关键字它包括两种用法 synchronized方法和synchronized块。
同步方法 同步方法public synchronized void method(int args){}
synchronized方法控制“对象”的访问每个对象对应一把锁每个 synchronized方法都必须获得调用方法的对象的锁才能执行否则线程会阻塞方法一旦执行就独占该锁直到该方法返回菜释放锁后面被阻塞的线程才能获得这个锁继续执行。
缺陷若将一个大的当法声明为 synchronized 将会影响小路。
同步快 同步块synchronized(Obj){}Obj称之为同步监视器 1. Obj可以是任何对象但是推荐使用共享资源作为同步监视器。 2. 同步方法中无需指定同步监控器因为不不方法的同步监视器就是this就是这个对象本身或者class。同步监视器的执行过程 1. 第一个线程访问锁定同步监视器执行其中代码。 2. 第二个线程访问发现同步监视器被锁定无法访问。 3. 第一个线程访问完毕解锁同步监视器。 4. 第二个线程访问发现同监视器没有锁然后锁定并访问。
同步方法和同步块解决三大不安全案例
买火车票
/*** author yxf*/
//同步方法解决不安全买票
public class UnsafeTicket {public static void main(String[] args) {BuyTicket ticket new BuyTicket();new Thread(ticket,shakingChloe).start();new Thread(ticket,janiceMan).start();new Thread(ticket,黄牛党).start();}
}class BuyTicket implements Runnable{//总票数private int ticketNums 10;//外部停止方式boolean flag true;Overridepublic void run() {while (flag){//模拟延时try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}buy();}}//synchronized 同步方法锁的是thisprivate synchronized void buy(){//判断是否邮票if(ticketNums 0){flag false;return;}//买票System.out.println(Thread.currentThread().getName()--拿到了第 ticketNums --张票);}
}银行取钱
/*** author yxf*/
//synchronized 同步块 解决不安全取钱
public class UnsafeBank {public static void main(String[] args) {//账户Account account new Account(100,结婚基金);Drawing you new Drawing(account,50,本人);Drawing girlFriend new Drawing(account,100,girlFriend);you.start();girlFriend.start();}
}//账户
class Account{int money; //余额String name; //卡名public Account(int money,String name){this.money money;this.name name;}
}//银行模拟取款
class Drawing extends Thread{Account account; //账户int drawingMoney; //取了多少钱int nowMoney; //现在手里有多少钱public Drawing(Account account,int drawingMoney,String name){super(name);this.account account;this.drawingMoney drawingMoney;}//取钱//synchronized 默认是thisOverridepublic void run(){//锁的对象就是变化的量需要增删改的对象synchronized (account){//判断有没有钱if(account.money - drawingMoney 0){System.out.println(Thread.currentThread().getName() 余额不足);return;}//sleep可以放大问题的发生性try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}//卡内余额 余额 - 你取的钱account.money account.money - drawingMoney;//你手里的钱 手上的钱 取出来的钱nowMoney nowMoney drawingMoney;System.out.println(account.name --- 余额为 account.money);//Thread.currentThread().getName() getNameSystem.out.println(this.getName() 手里的钱 nowMoney);}}
}不安全集合
import java.util.ArrayList;
import java.util.List;/*** author yxf*/
//synchronized 同步块解决线程不安全的集合
public class UnsafeList {public static void main(String[] args) {ListString list new ArrayListString();for (int i 0; i 80000; i) {new Thread(() - {synchronized(list){list.add(Thread.currentThread().getName());}}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}扩展
import java.util.concurrent.CopyOnWriteArrayList;/*** author yxf*/
//测试JUC安全类型的集合
public class TestJUC {public static void main(String[] args) {CopyOnWriteArrayListString list new CopyOnWriteArrayListString();for (int i 0; i 10000; i) {new Thread(() -{list.add(Thread.currentThread().getName());}).start();}try {Thread.sleep(3000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(list.size());}
}4. 死锁
多个线程各自占有一些共享资源并且互相等待其它线程占有的资源才能运行而导致两个或者多个线程都在等待对方释放资源都停止执行的情形某一个同步块同时拥有“两个以上对象的锁”时就可能会发生“死锁”的问题。
/*** author yxf*/
public class DeadLock {public static void main(String[] args) {Makeup makeup new Makeup(0,白雪公主);Makeup makeup1 new Makeup(1,毒皇后);makeup.start();makeup1.start();}
}//口红
class Lipstick{}//镜子
class Mirror{}//化妆
class Makeup extends Thread{//需要的资源只有一份用static来保证只有一份static Lipstick lipstick new Lipstick();static Mirror mirror new Mirror();int choice; //选择String girlName; //使用化妆品的人Makeup(int choice,String girlName){this.choice choice;this.girlName girlName;}Overridepublic void run() {try {Makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆互相持有对方的锁就是需要拿到对方的资源private void Makeup() throws InterruptedException {if(choice 0){synchronized (lipstick){ //获得口红的锁System.out.println(this.girlName 拿到了口红的锁);Thread.sleep(1000);synchronized (mirror){ //一秒钟后获得镜子System.out.println(this.girlName 拿到了镜子的锁);}}}else {synchronized (mirror){ //获得镜子的锁System.out.println(this.girlName 拿到了镜子的锁);Thread.sleep(2000);synchronized (lipstick){ //两秒钟后获得口红的锁System.out.println(this.girlName 拿到了口红的锁);}}}}
}解决死锁办法不能同时抱两把锁
/*** author yxf*/
public class DeadLock {public static void main(String[] args) {Makeup makeup new Makeup(0,白雪公主);Makeup makeup1 new Makeup(1,毒皇后);makeup.start();makeup1.start();}
}//口红
class Lipstick{}//镜子
class Mirror{}//化妆
class Makeup extends Thread{//需要的资源只有一份用static来保证只有一份static Lipstick lipstick new Lipstick();static Mirror mirror new Mirror();int choice; //选择String girlName; //使用化妆品的人Makeup(int choice,String girlName){this.choice choice;this.girlName girlName;}Overridepublic void run() {try {Makeup();} catch (InterruptedException e) {e.printStackTrace();}}//化妆互相持有对方的锁就是需要拿到对方的资源private void Makeup() throws InterruptedException {if(choice 0){synchronized (lipstick){//获得口红的锁System.out.println(this.girlName 拿到了口红的锁);Thread.sleep(1000);}synchronized (mirror){//一秒钟后获得镜子System.out.println(this.girlName 拿到了镜子的锁);}}else {synchronized (mirror){//获得镜子的锁System.out.println(this.girlName 拿到了镜子的锁);Thread.sleep(2000);}synchronized (lipstick){//两秒钟后获得口红的锁System.out.println(this.girlName 拿到了口红的锁);}}}
}1.死锁避免方法
产生死锁的四个必要条件 互斥条件一个资源每次只能被一个进程使用。请求与保持条件一个进程因请求资源而阻塞时对已获得的资源保持不放。不剥夺条件进程已获得的资源在未使用完之前不能强行剥夺。虚幻等待条件若干进程之间形成一种头尾相接的循环等待资源关系。
5.Lock锁
ReentrantLock可重入锁类实现了Lock它拥有与synchronized相同的并发性和内存语义在实现线程安全的控制中比较常用的是ReentrantLock可以现实加锁释放锁。
import java.util.concurrent.locks.ReentrantLock;/*** author yxf*/
public class TestLock {public static void main(String[] args) {TestLock2 lock new TestLock2();new Thread(lock,shaking).start();new Thread(lock,xky).start();new Thread(lock,BLACK CUPID).start();}
}
class TestLock2 implements Runnable{int ticketNums 10;//定义lock锁private final ReentrantLock lock new ReentrantLock();Overridepublic void run() {while (true){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}try {lock.lock(); //加锁if (ticketNums 0){System.out.println(Thread.currentThread().getName()拿到了第ticketNums--张票);}else {break;}}finally {lock.unlock(); //解锁}}}
}synchronized和Lock的对比
Lock是显示锁手动开启和关闭锁别忘记关闭锁synchronized是隐式锁除了作用域自动释放。Lock只有代码块锁synchronized有代码块锁和方法锁。使用Lock锁JVM将话费较少的时间来调度线程性能更好。并且具有更好的扩展性提供更多子类。优先使用顺序 1. Lock 同步代码块已经进入了方法体fenpei了相应资源 同步方法在方法体之外。
6. 线程协作
线程通信-分析 这是一个线程同步问题生产者和消费者共享同一个资源并且生产者和消费者之间相互依赖互为条件。
synchronized可以阻止并发更新同一个资源实现同步。synchronized不能用来实现不同线程之间的消息传递通信。
Java中提供了及格方法解决线程之间的通信问题
方法名作用wait()表示线程一致等待知道其他线程通知与sleep不同会释放锁wait(long timeout)指定等待的毫秒数notify()唤醒一个处于等待状态的线程notifyAll()唤醒同一个对象上所有调用wait()方法的线程优先级别高的线程优先调度
注意均是Object类的方法都只能在同步方法或者同步代码块中使用否则会抛出异常lllegalMonitorStateException
利用缓冲区解决管程法生产者放入数据消费者取出数据
/*** author yxf*/
//测试生产者消费者模型--利用缓冲区解决管执法
//生产者消费者产品缓冲区
public class TestPC {public static void main(String[] args) {SynContainer container new SynContainer();new Producers(container).start();new Consumer(container).start();}
}//生产者
class Producers extends Thread{SynContainer container;public Producers(SynContainer container){this.container container;}//生产Overridepublic void run() {for (int i 1; i 100; i) {container.push(new Chicken(i));System.out.println(生产了i只鸡);}}
}//消费者
class Consumer extends Thread{SynContainer container;public Consumer(SynContainer container){this.container container;}//消费Overridepublic void run() {for (int i 1; i 100; i) {System.out.println(消费了--container.pop().id只鸡);}}
}//产品
class Chicken{int id; //生产编号public Chicken(int id) {this.id id;}
}//缓冲区
class SynContainer{//需要一个容器大小Chicken[] chickens new Chicken[10];//容器计数器int count 0;//生产者放入产品public synchronized void push(Chicken chicken){//如果容器满了就需要等待消费者消费if (count chickens.length) {//通知消费者消费生产等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果没有满我们就需要丢入产品chickens[count] chicken;count;//可以通知消费者消费了this.notify();}//消费者消费产品public synchronized Chicken pop(){//判断能否消费if(count 0){//等待生产者生产消费者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//如果可以消费count--;Chicken chicken chickens[count];//吃完了通知生产者生产this.notify();return chicken;}
}并发协作信号灯,标志位解决
/*** author yxf*/
//测试生产者消费者问题2信号灯法标志位解决
public class TestPC2 {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;}Overridepublic void run() {for (int i 1; i 34; i) {if(i%20){this.tv.play(亲爱的小孩);}else {this.tv.play(非日常派对);}}}
}//消费者 -- 观众
class Watcher extends Thread{TV tv;public Watcher(TV tv){this.tv tv;}Overridepublic void run() {for (int i 1; i 34; i) {tv.watch();}}
}//产品 -- 影视作品
class TV{//演员在拍摄的时候观众等待 T//电影上映观众观看演员等待或者拍别的电影 FString videoName; //影视作品名称boolean flag true;//表演public synchronized void play(String videoName){if (!flag) {try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(爱奇艺同步更新 videoName);//通知观众观看this.notify(); //通知唤醒this.videoName videoName;this.flag !this.flag;}//观看public synchronized void watch(){if (flag){try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(观看了videoName);//通知更新催更this.notify();this.flag !this.flag;}
}7.线程池 背景经常创建和销毁/使用量特别大的资源比如并发情况下的线程对性能影响很大。 思路提前创建好多个线程放入线程池中使用时直接获取使用完放回池中。可以避免频繁创建销毁/实现重复利用。 好处 1. 提高响应速度减少了创建新线程的时间 2. 降低资源消耗重复利用线程池中线程不需要每次创建 3. 便于线程管理 1. corePoolSize核心池的大小。 2. maximumPoolSize最大线程数。 3. keepAliveTime线程没有任务时最多保持多久时间后会终止。 ExecutorService真正的线程池接口。常见子类ThreadPoolExecutor。 1. void execute(Runnable command)执行任务/命令没有返回值一般用来执行Runnable。 2. Future submit(Callable task)执行任务有返回值一般又来执行Callable。 3. void shutdown()关闭连接池。 Executors工具类、线程池的工厂类用于创建并返回不同类型的线程池。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** author yxf*/
//测试线程池
public class TestPool {public static void main(String[] args) {//1.创建服务创建线程池//newFixedThreadPool 参数为线程池大小ExecutorService service Executors.newFixedThreadPool(10);//执行service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());service.execute(new MyThread());//2.关闭连接service.shutdown();}
}
class MyThread implements Runnable{Overridepublic void run() {System.out.println();}
}