1.1 线程简介
多任务→多线程
过程 VS 线程
- 程序是指令和数据的有序汇合,是一个动态的概念。
- 过程是程序的一次执行过程,是一个动静的概念。
- 过程中至多有一个线程,线程是CPU调度和执行的根本单位。
1.2 线程创立
三种创立形式:
- 继承Thread类
- 实现Runnable接口
- 实现Callable接口(理解)
1.2.1 继承Thread类
创立线程形式一:继承Thread类→重写run()办法→调用start()启动线程
//留神,线程开启不肯定立刻执行,由CPU调度执行public class TestThread1 extends Thread{ @Override public void run() { // run办法线程体 for(int i = 0;i < 20;i++) { System.out.println("我在看代码—————"+i); } } public static void main(String[] args) { //main线程,主线程 TestThread1 testThread1 = new TestThread1(); //调用start()办法开启线程,交替执行 testThread1.start(); for(int i = 0;i < 20;i++) { System.out.println("我在学习多线程—————"+i); } } }
实现多线程同步下载图片
import java.io.File;import java.net.URL;import org.apache.commons.io.FileUtils;public class TestThread2 extends Thread{ private String url,name; //网络图片地址,保留的文件名 public TestThread2(String url,String name) { this.url = url; this.name = name; } //下载图片线程执行体 @Override public void run() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url, name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { TestThread2 t1 = new TestThread2("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg"); TestThread2 t2 = new TestThread2("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg"); TestThread2 t3 = new TestThread2("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","3.jpg" t1.start(); t2.start(); t3.start(); }} //下载器class WebDownloader{ //下载办法 public void downloader(String url,String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); }catch (Exception e) { e.printStackTrace(); System.out.println("IO异样,downloader办法呈现问题"); } }}
1.2.2 实现Runnable接口
创立线程形式二:实现Runnable接口→重写run()办法→调用start()启动线程(须要Runnable接口实现类)
public class TestThread3 implements Runnable{ @Override public void run() { // run办法线程体 for(int i = 0;i < 20;i++) { System.out.println("我在看代码—————"+i); } } public static void main(String[] args) { //创立runnable接口的实现类对象 TestThread3 testThread3 = new TestThread3(); //创立线程对象,通过线程对象来开启线程,代理// Thread thread = new Thread(testThread3);// thread.start(); new Thread(testThread3).start(); for(int i = 0;i < 20;i++) { System.out.println("我在学习多线程—————"+i); } }}
import java.io.File;import java.net.URL;import org.apache.commons.io.FileUtils;//练习Thread,实现多线程同步下载图片public class TestThread2n implements Runnable{ private String url,name; //网络图片地址,保留的文件名 public TestThread2n(String url,String name) { this.url = url; this.name = name; } //下载图片线程执行体 @Override public void run() { WebDownloader1 webDownloader = new WebDownloader1(); webDownloader.downloader(url, name); System.out.println("下载了文件名为:"+name); } public static void main(String[] args) { TestThread2n t1 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg"); TestThread2n t2 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg"); TestThread2n t3 = new TestThread2n("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","3.jpg"); new Thread(t1).start(); new Thread(t2).start(); new Thread(t3).start(); }}//下载器class WebDownloader1{ //下载办法 public void downloader(String url,String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); }catch (Exception e) { e.printStackTrace(); System.out.println("IO异样,downloader办法呈现问题"); } }}
小结:
继承Thread类
- 子类继承Thread类具备多线程能力
- 启动线程:子类对象.start()
- 不倡议应用:防止OOP单继承局限性
实现Runnable接口
- 实现接口Runnable具备多线程能力
- 启动线程:传入指标对象+Thread对象.start()
- 举荐应用:防止单继承局限性,灵便不便,不便同一对象被多个线程应用
多个线程同时操作一个对象,买火车票的例子
//问题:多个线程操作同一个资源,线程不平安,数据错乱public class TestThread4 implements Runnable{ //票数 private int ticketNum = 10; @Override public void run() { while(true) { if(ticketNum<=0) { break; } try { Thread.sleep(10); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票"); } } public static void main(String[] args) { TestThread4 ticket = new TestThread4(); new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛党").start(); }}
案例:龟兔赛跑
//模仿龟兔赛跑public class Race implements Runnable{ //胜利者 private static String winner; public static void main(String[] args) { Race race = new Race(); new Thread(race,"兔子").start(); new Thread(race,"乌龟").start(); } @Override public void run() { for(int i = 0; i <= 100;i++) { //模仿兔子劳动 if("兔子".equals(Thread.currentThread().getName() )&& i%10==5) { try { Thread.sleep(56); } catch (InterruptedException e) { e.printStackTrace(); } } if("乌龟".equals(Thread.currentThread().getName() )) { try { Thread.sleep(5); } 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; }else { if(steps==100) { winner = Thread.currentThread().getName(); System.out.println("winner is "+winner); return true; } } return false; }}
1.2.3 实现Callable接口
创立线程形式三(理解即可):实现Callable接口(须要返回值类型)→重写call()办法(须要抛出异样)→创立指标对象→创立敞开服务
import java.io.File;import java.net.URL;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;import org.apache.commons.io.FileUtils;//创立形式三:实现Callable接口/* * Callable的益处: * 1.能够定义返回值 * 2.能够抛出异样 */public class TestCallable implements Callable<Boolean>{ private String url,name; //网络图片地址,保留的文件名 public TestCallable(String url,String name) { this.url = url; this.name = name; } //下载图片线程执行体 @Override public Boolean call() { WebDownloader webDownloader = new WebDownloader(); webDownloader.downloader(url, name); System.out.println("下载了文件名为:"+name); return true; } public static void main(String[] args) throws Exception { TestCallable t1 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","1.jpg"); TestCallable t2 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","2.jpg"); TestCallable t3 = new TestCallable("https://pics6.baidu.com/feed/838ba61ea8d3fd1ffe50b135beb0651894ca5f6d.jpeg?token=34d0744ffdbbc1f2d6ddea302862b052","3.jpg"); //创立执行服务 ExecutorService ser = Executors.newFixedThreadPool(3); //提交执行 Future<Boolean> r1 = ser.submit(t1); Future<Boolean> r2 = ser.submit(t2); Future<Boolean> r3 = ser.submit(t3); //获取后果 boolean rs1 = r1.get(); boolean rs2 = r2.get(); boolean rs3 = r3.get(); System.out.println(rs1); System.out.println(rs2); System.out.println(rs3); //敞开服务 ser.shutdown(); }} //下载器class WebDownloader{ //下载办法 public void downloader(String url,String name) { try { FileUtils.copyURLToFile(new URL(url), new File(name)); }catch (Exception e) { e.printStackTrace(); System.out.println("IO异样,downloader办法呈现问题"); } }}
Lamda表达式
语法:(parameters) -> expression 或 (parameters) ->{ statements; }
作用:简化代码,防止匿名外部类定义过多
Function Interface(函数式接口):只蕴含惟一一个形象办法
能够通过lamda表达式来创立函数式接口的对象
推导lambda表达式
public class TestLambda1 { //3.动态外部类 static class Like2 implements ILike{ @Override public 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{ @Override public void lambda() { System.out.println("I like lambda3"); } } like = new Like3(); like.lambda(); //5.匿名外部类,没有类的名称,必须借助接口或者父类 like = new ILike() { @Override public 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{ @Override public void lambda() { System.out.println("I like lambda"); } }
简化lambda表达式:1.省略参数类型 2.省略括号
动态代理
示例(婚庆公司)
public class StaticProxy { public static void main(String[] args) { You you = new You(); you.HappyMarry(); // new Thread(()->System.out.println("我爱你")).start();// new WeddingCompany(new You()).HappyMarry(); //代理 WeddingCompany weddingCompany = new WeddingCompany(new You()); weddingCompany.HappyMarry(); }}interface Marry{ void HappyMarry();}//实在角色class You implements Marry{ @Override public void HappyMarry() { System.out.println("结婚了,开心"); }}//代理角色class WeddingCompany implements Marry{ private Marry target; public WeddingCompany(Marry target) { this.target = target; } @Override public void HappyMarry() { before(); this.target.HappyMarry(); //实在对象 after(); } private void after() { System.out.println("结婚之后,收尾款"); } private void before() { System.out.println("结婚之前,安排现场"); } }
动态代理模式总结:
- 实在对象和代理对象都要实现同一个接口
- 代理对象代理实在对象
益处:
- 代理对象能够做很多实在对象做不了的事件
- 实在对象专一做本人的事件
1.3 线程状态
1.3.1 五大状态
1.3.2 线程进行
- 不举荐应用JDK提供的stop()、destroy()办法【已废除】
- 举荐线程本人停下来(倡议应用一标记位进行终止变量)
//测试stop//1.倡议线程失常进行-->利用次数,不倡议死循环//2.倡议应用标记位//3.不要应用stop或者destroy等过期或者JDK不倡议应用的办法public class TestStop implements Runnable{ private boolean flag = true; public static void main(String[] args) { TestStop testStop = new TestStop(); new Thread(testStop).start(); for(int i = 0; i < 1000; i++) { System.out.println("main"+i); if(i == 900) { //调用stop()办法切换标记位,让线程进行 testStop.stop(); System.out.println("线程进行"); } } } @Override public void run() { int i = 0; while(flag) { System.out.println("run ... Thread"+i++); } } public void stop() { this.flag = false; }}
1.3.3 线程休眠
sleep(毫秒)→就绪
每个对象有个锁,sleep不会开释锁
//模仿网络延时:放大问题的产生性public class TestSleep implements Runnable{ //票数 private int ticketNum = 10; @Override public void run() { while(true) { if(ticketNum<=0) { break; } //模仿延时 try { Thread.sleep(100); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票"); } } public static void main(String[] args) { TestSleep ticket = new TestSleep(); new Thread(ticket,"小明").start(); new Thread(ticket,"老师").start(); new Thread(ticket,"黄牛党").start(); }}
import java.text.SimpleDateFormat;import java.util.Date;public class TestSleep2{ public static void main(String[] args) { //模仿倒计时 try { tenDown(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //打印以后零碎工夫 Date startTime = new Date(System.currentTimeMillis()); while(true) { try { System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); Thread.sleep(1000); startTime = new Date(System.currentTimeMillis()); //更新以后工夫 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void tenDown() throws InterruptedException { int num = 3; while(true) { Thread.sleep(1000); System.out.println(num--); if(num<=0) { break; } } }}
1.3.4 线程礼让
Yield让以后线程暂停但不阻塞,转为就绪状态
礼让不肯定胜利
//测试礼让线程,礼让不肯定胜利public class TestYield { public static void main(String[] args) { // TODO Auto-generated method stub MyYield myYield = new MyYield(); new Thread(myYield,"a").start(); new Thread(myYield,"b").start(); }}class MyYield implements Runnable{ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"线程开始执行"); Thread.yield(); //礼让 System.out.println(Thread.currentThread().getName()+"线程进行执行"); } }
1.3.5 线程强制执行
Join合并线程,待此线程执行实现后,再执行其余线程,其余线程阻塞(能够看作是插队)
//测试join办法public class TestJoin implements Runnable{ public static void main(String[] args) throws InterruptedException { //启动咱们的线程 TestJoin testJoin = new TestJoin(); Thread thread = new Thread(testJoin); thread.start(); for (int i = 0; i < 500; i++) { if(i==200) { thread.join();//插队 } System.out.println("main"+i); } } @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("线程vip来了"+i); } }}
1.3.6 线程优先级
优先高的不肯定先执行,默认是5
//测试线程的优先级public class TestPriority{ public static void main(String[] args) { //主线程默认优先级 System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); MyPriority myPriority = new MyPriority(); Thread t1 = new Thread(myPriority); Thread t2 = new Thread(myPriority); Thread t3 = new Thread(myPriority); Thread t4 = new Thread(myPriority); //先设置优先级,再启动 t1.start(); t2.setPriority(1); t2.start(); t3.setPriority(4); t3.start(); t4.setPriority(Thread.MAX_PRIORITY); //MAX_PRIORITY=10 t4.start(); }}class MyPriority implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()+"--->"+Thread.currentThread().getPriority()); } }
1.3.7 守护(daemon)线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程(如,后盾记录操作日志,监控内存,垃圾回收期待)执行结束,但不必期待守护线程执行结束
//测试守护线程//上帝守护你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{ @Override public void run() { while(true) { System.out.println("上帝保佑着你"); } } }//你class You implements Runnable{ @Override public void run() { for (int i = 0; i < 36500; i++) { System.out.println("开心活着"); } System.out.println("====Good bye!===="); } }
1.4 线程同步
多个线程操作同一资源(并发)
线程同步是一种期待机制,多个须要同时拜访同一对象的线程进图该对象的期待池造成队列,期待后面线程应用结束,下一线程再应用
造成条件:队列+锁(synchronized)
1.4.1 同步办法及同步块
三个不平安案例
1.不平安的买票
//不平安的买票(线程不平安)public class UnsafeBuyTicket { public static void main(String[] args) { BuyTicket station = new BuyTicket(); new Thread(station,"苦逼的我").start(); new Thread(station,"牛逼的你们").start(); new Thread(station,"可恶的黄牛党").start(); } }class BuyTicket implements Runnable{ //票 private int ticketNum = 10; boolean flag = true; //内部进行形式 @Override public void run() { //买票 while(flag) { buy(); } } private void buy() { if(ticketNum<=0) { flag = false; return; } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //买票 System.out.println(Thread.currentThread().getName()+"拿到"+ticketNum--); } }
2.不平安的取钱
//不平安的取钱(两个人)public class UnsafeBank { public static void main(String[] args) { Account account = new Account(100,"结婚基金"); Drawing you = new Drawing(account,50,"你"); Drawing GF = new Drawing(account,100,"女朋友"); you.start(); GF.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 nowMoney; public Drawing(Account account,int drawingMoney,String name) { super(name); this.account = account; this.drawingMoney = drawingMoney; } //取钱 @Override public void run() { //判断有没有钱 if(account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //放大问题的产生性 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //卡内余额 = 余额 - 取的钱 account.money = account.money - drawingMoney; //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread() == this.getName() System.out.println(this.getName()+"手里的钱:"+nowMoney); }}
3.不平安的汇合
//线程不平安的汇合//增加到同一地位被笼罩public class UnsafeList { 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()); }}
synchronized
同步办法:public synchronized void method(int args) { }
synchronized办法管制对“对象”的拜访,每个对象对象对应一把锁,每个synchronized办法都必须取得调用该办法的锁能力执行,否则线程会阻塞,办法一旦执行,就独占该锁,直到该办法返回才开释锁,前面被阻塞的线程能力取得这个锁,继续执行
同步块:synchronized(Obj) { }
Obj称为同步监视器
- Obj能够是任何对象,然而举荐应用共享资源作为同步监视器
- 同步办法中无需指定同步监视器,因为同步办法的同步监视器就是this
public void run() { //锁的对象就是变动的量,须要增删改的对象 synchronized (account) { //判断有没有钱 if(account.money - drawingMoney < 0) { System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); return; } //放大问题的产生性 try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } //卡内余额 = 余额 - 取的钱 account.money = account.money - drawingMoney; } //你手里的钱 nowMoney = nowMoney + drawingMoney; System.out.println(account.name+"余额为:"+account.money); //Thread.currentThread() == this.getName() System.out.println(this.getName()+"手里的钱:"+nowMoney); }
CopyOnWriteArrayList
JUC就是java.util .concurrent工具包的简称。这是一个解决线程的工具包,JDK 1.5开始呈现的。Callable接口也在JUC中。
import java.util.concurrent.CopyOnWriteArrayList;//测试JUC平安类型的汇合public class TestJUC { 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(); } try { Thread.sleep(3000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(list.size()); }}
1.4.2 死锁
多个线程各自占有一些共享资源,并且相互期待其余线程占有的资源能力运行,而导致两个或者多个线程都在期待对方开释资源,都进行执行的情景,某一同步块同时领有”两个以上对象的锁“时,就可能会产生”死锁“的问题
//死锁:多个线程相互抱着对方须要的资源,而后造成僵持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{ //须要的资源只有一份,用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; } @Override public void run() { //化妆 try { makeup(); } catch (InterruptedException e) { // TODO Auto-generated catch block 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+"取得口红的锁"); } } } }}
锁中锁(多个对象相互嵌套的锁)使多个线程相互抱着对方须要的资源,而后造成僵持
解决办法:锁离开写,不要同时占有多个资源
//化妆,相互持有对方的锁,就是须要拿到对方的资源 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.4.3 Lock锁
从JDK 5.0开始,Java提供了更弱小的线程同步机制——显式定义同步锁对象来实现同步。同步锁应用Lock对象充当,Lock锁也蕴含在JUC内
ReentrantLock(可重入锁)类实现了Lock,能够显式加锁、开释锁
应用格局:
Lock lock=new ReentrantLock();lock.lock();try{ //解决工作}catch(Exception ex){ }finally{ lock.unlock(); //开释锁}
测试Lock类(买票):
import java.util.concurrent.locks.ReentrantLock;//测试Lock类public class TestLock { public static void main(String[] args) { TestLock2 testLock2 = new TestLock2(); new Thread(testLock2).start(); new Thread(testLock2).start(); new Thread(testLock2).start(); }}class TestLock2 implements Runnable{ int ticketNum = 10; //定义Lock锁 private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { while(true) { lock.lock();//加锁 try { if(ticketNum > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(ticketNum--); }else { break; } }finally { //解锁 lock.unlock(); } } }}
synchronized与Lock比照
- Lock是显式锁(手动开启和敞开锁),synchronized是隐式锁,主动开释
- 应用Lock锁,JVM将破费较少的工夫来调度线程,性能更好。并且具备更好的扩展性(提供更多子类)
1.4.4 线程通信
线程通信办法:
留神:均是Object类的办法,都只能在同步办法或者同步代码块中应用,否则会抛出异样
生产者消费者问题
解决办法:
利用缓冲区解决:管程法
//测试生产者消费者模型-->利用缓冲区解决:管程法//生产者,消费者,产品,缓冲区public class TestPC { public static void main(String[] args) { SynContainer container = new SynContainer(); new Producer(container).start(); new Consumer(container).start(); }}//生产者class Producer extends Thread{ SynContainer container; public Producer(SynContainer container) { this.container = container; } //生产 @Override public void run() { for (int i = 0; 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; } //生产 @Override public void run() { for (int i = 0; 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) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果没有满,咱们就须要丢入产品 chickens[count] = chicken; count++; //能够告诉消费者生产了 this.notifyAll(); } //消费者生产产品 public synchronized Chicken pop() { //判断是否生产 if(count==0) { //期待生产者生产 try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //如果能够生产 count--; Chicken chicken = chickens[count]; //吃完了,告诉生产者生产 this.notifyAll(); return chicken; }}
利用标记位解决:信号灯法
package com.zhg.thread;//测试生产者消费者模型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; } @Override 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; } @Override public void run() { for (int i = 0; i < 20; i++) { tv.watch(); } }}//产品-->节目class TV{ //演员表演,观众期待 T //观众观看,演员期待 F String voice;//表演的节目 boolean flag = true; //表演 public synchronized void play(String voice) { if(!flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("演员表演了:"+voice); //告诉观众观看 this.notifyAll(); //告诉唤醒 this.voice = voice; this.flag = !this.flag; } //观看 public synchronized void watch() { if(flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("观众观看了:"+voice); //告诉演员表演 this.notifyAll(); this.flag = !this.flag; }}
线程池
背景:常常创立和销毁,使用量特地大的资源,比方并发状况下的线程,对性能影响很大。
思路:提前创立好多个线程,放入线程池中,应用时间接获取,应用完放回池中。能够防止频繁创立销毁、实现反复利用。
益处:
- 进步响应速度
- 升高资源耗费
- 便于线程治理
JDK 5.0起提供了线程池相干API:ExecutorService和Executors
- ExecutorService:真正的线程池接口。
- Executor:工具类、线程池的工厂类,用于创立并返回不同类型的线程池。
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;//测试线程池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()); //2.敞开连贯 service.shutdown(); }}class MyThread implements Runnable{ @Override public void run() { System.out.println(Thread.currentThread().getName()); }}