Process 和 Tread
创建多线程方法一
1 package main.com; 2 3 4 //创建进程方法一:继承 Thread 类,重写 run() 方法,调用 start() 方法开启线程 5 6 //总结:注意,线程开启不一定立即执行由 CPU 调度执行 7 public class TestThread1 extends Thread{ 8 9 @Override 10 public void run(){ 11 //run 方法线程体 12 for (int i = 0; i < 20; i++) { 13 System.out.println("runing..." + i); 14 } 15 16 } 17 18 public static void main(String[] args) { 19 //main 线程,即主线程 20 21 TestThread1 tt1 = new TestThread1(); 22 //调用start() 方法开启线程 23 tt1.start(); 24 25 for (int i = 0; i < 200; i++) { 26 System.out.println("main....." + i); 27 } 28 29 } 30 }
实践
1 package com.xun; 2 3 import org.apache.commons.io.FileUtils; 4 5 import java.io.File; 6 import java.io.IOException; 7 import java.net.URL; 8 9 //练习Thread,实现多线程同步下载图片 10 public class TestThread2 extends Thread{ 11 private String url; //网络图片地址 12 private String name; //保存的文件名 13 14 public TestThread2(String url,String name){ 15 this.url = url; 16 this.name = name; 17 } 18 19 @Override 20 public void run(){ 21 WebDownloder webDownloder = new WebDownloder(); 22 webDownloder.downloader(url,name); 23 System.out.println("下载文件名为"+name); 24 } 25 26 public static void main(String[] args) { 27 TestThread2 t1 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105120-p9.jpg","1.jpg"); 28 TestThread2 t2 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105121-p9.jpg","2.jpg"); 29 TestThread2 t3 = new TestThread2("http://www.ikuimg.com/img/pic/2203/1-220331105122-p9.jpg","3.jpg"); 30 31 t1.start(); 32 t2.start(); 33 t3.start(); 34 35 } 36 } 37 38 //下载器 39 class WebDownloder{ 40 public void downloader(String link, String name){ 41 try { 42 URL url = new URL(link); 43 //url.openConnection().setRequestProperty("User-Agent", "Mozilla/4.76"); 44 FileUtils.copyURLToFile(url,new File(name)); 45 } catch (IOException e) { 46 e.printStackTrace(); 47 System.out.println("IO异常,downloder方法异常"); 48 } 49 } 50 }
创建多线程方法二:
1 package com.xun; 2 3 //创建线程方式2:实现 runable 接口,重写run方法,实行线程需要丢入 runable 接口实现类,调用start 方法 4 5 public class TestThread3 implements Runnable{ 6 @Override 7 public void run(){ 8 //run 方法调用线程体 9 for (int i = 0; i < 20; i++) { 10 System.out.println("runing..."); 11 } 12 } 13 14 public static void main(String[] args) { 15 //main 线程,即主线程 16 17 TestThread3 tt3 = new TestThread3(); 18 19 //创建线程对象,通过线程对象来开启我们的线程 20 // Thread thread = new Thread(tt3); 21 // tt3.start(); 22 23 new Thread(tt3).start(); 24 for (int i = 0; i < 200; i++) { 25 System.out.println("main....." + i); 26 } 27 28 } 29 }
小结:
继承Thread类:
子类继承Thread类具备多线程能力
启动线程:子类对象.start()
不建议使用:避免OOP单继承局限性
实现Runable接口
实现接口Runable具有多线程能力
启动线程:传入目标对象+Thread对象.start()
推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
实例,初识并发
1 package com.xun; 2 3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题 4 public class TestThread4 implements Runnable{ 5 6 private int ticketNums = 10; //票数 7 8 @Override 9 public void run(){ 10 while(ticketNums>0){ 11 try { 12 Thread.sleep(200); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票"); 17 } 18 } 19 20 public static void main(String[] args) { 21 TestThread4 tt4 = new TestThread4(); 22 23 new Thread(tt4,"张三").start(); 24 new Thread(tt4,"李四").start(); 25 new Thread(tt4,"王五").start(); 26 } 27 }
实例:龟兔赛跑,一个对象,多个线程使用
1 package com.xun; 2 3 //发现问题,多个线程操作同一个资源,线程不安全,数据紊乱,并发问题 4 public class TestThread4 implements Runnable{ 5 6 private int ticketNums = 10; //票数 7 8 @Override 9 public void run(){ 10 while(ticketNums>0){ 11 try { 12 Thread.sleep(200); 13 } catch (InterruptedException e) { 14 e.printStackTrace(); 15 } 16 System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"张票"); 17 } 18 } 19 20 public static void main(String[] args) { 21 TestThread4 tt4 = new TestThread4(); 22 23 new Thread(tt4,"张三").start(); 24 new Thread(tt4,"李四").start(); 25 new Thread(tt4,"王五").start(); 26 } 27 }
通过静态代理了解线程实现原理
1 package com.xun; 2 //通过静态代理了解线程底部实现原理 3 4 //静态代理模式总结 5 //真实对象和代理对象都要实现同一个接口 6 //代理对象要代理真实对象 7 //好处: 8 //代理对象可以做很多真实对象做不了的事 9 //真实对象专注做自己的事 10 public class StaticProxy { 11 12 public static void main(String[] args) { 13 You you = new You(); 14 15 new Thread(()-> System.out.println("I love you!")).start(); 16 17 new WeddingCompany(you).HappyMarry(); 18 19 //you.HappyMarry(); 20 21 // WeddingCompany weddingCompany = new WeddingCompany(you); 22 //weddingCompany.HappyMarry(); 23 } 24 25 } 26 27 interface Marry{ 28 void HappyMarry(); 29 } 30 31 //真实角色,你要结婚 32 class You implements Marry{ 33 @Override 34 public void HappyMarry(){ 35 System.out.println("你要开心的结婚了!"); 36 } 37 } 38 39 //代理角色,帮你结婚 40 class WeddingCompany implements Marry{ 41 42 private Marry target; 43 44 public WeddingCompany(Marry target){ 45 this.target=target; 46 } 47 48 @Override 49 public void HappyMarry(){ 50 before(); 51 this.target.HappyMarry(); 52 after(); 53 } 54 55 private void before(){ 56 System.out.println("结婚前布置"); 57 } 58 59 private void after(){ 60 System.out.println("婚后结尾款"); 61 } 62 }
函数式接口Functional Interface
定义:
任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口
对于函数式接口,我们可以通过lambda表达式来创建接口的对象
Lamda表达式 //JDK8新增特性
意义:
避免匿名内部类定义过多
可以让你的代码看起来很简洁
去掉了一堆没有意义的代码,只留下核心逻辑
1 package com.xun.lambda; 2 3 public class TestLambda1 { 4 5 //3、静态内部类 6 static class Like2 implements ILike{ 7 8 @Override 9 public void Lambda() { 10 System.out.println("I like lambda2"); 11 } 12 } 13 public static void main(String[] args) { 14 ILike like = new Like1(); 15 like.Lambda(); 16 17 like = new Like2(); 18 like.Lambda(); 19 20 //4、局部内部类 21 class Like3 implements ILike{ 22 23 @Override 24 public void Lambda() { 25 System.out.println("I like lambda3"); 26 } 27 } 28 29 like = new Like3(); 30 like.Lambda(); 31 32 //5、匿名内部类 33 like = new ILike() { 34 @Override 35 public void Lambda() { 36 System.out.println("I like lambda4"); 37 } 38 }; 39 like.Lambda(); 40 41 //6、用lambda简化 42 like = ()->{ 43 System.out.println("I like lambda5"); 44 }; 45 like.Lambda(); 46 } 47 } 48 49 //1、定义一个函数式接口 50 interface ILike{ 51 void Lambda(); 52 } 53 54 //2、实现类 55 class Like1 implements ILike{ 56 57 @Override 58 public void Lambda() { 59 System.out.println("I like lambda1"); 60 } 61 }
线程方法:
方法 | 说明 |
setPriority(int new Priority) | 更改线程的优先级 |
static void sleep(long millis) | 在指定毫秒数内让当前正在执行的线程进入休眠 |
void join() | 等待该线程终止(插队) |
static void yield() | 暂停当前正在执行的线程对象,并执行其他线程 |
void interrupt() | 中断线程(一般不用) |
boolean isAlive() | 测试线程是否处于活动状态 |
线程停止:
1 package com.xun.state; 2 3 public class TestStop implements Runnable{ 4 5 //设置标志位 6 private boolean flag = true; 7 8 @Override 9 public void run() { 10 int i=0; 11 while(flag){ 12 System.out.println("runing..."+i++); 13 } 14 15 } 16 17 private void stop(){ 18 this.flag = false; 19 } 20 21 public static void main(String[] args) { 22 TestStop testStop = new TestStop(); 23 new Thread(testStop).start(); 24 25 for(int i=0;i<1000;i++){ 26 System.out.println("main..."+i); 27 if(i==900){ 28 //调用自己的stop方法 29 testStop.stop(); 30 System.out.println("线程该停止了"); 31 } 32 } 33 } 34 }
线程休眠:
sleep 存在异常InterruptedException;
可以模拟网络延时;
每个对象都有一把锁,sleep不会释放锁
线程礼让(yield)
让当前执行线程暂停,但不阻塞,从运行态变为就绪态
让cpu重写调度,礼让不一定成功
线程强制执行(join)
join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
可以想象成插队
1 package com.xun.state; 2 3 public class TestJoin implements Runnable{ 4 5 @Override 6 public void run() { 7 for (int i = 0; i < 1000; i++) { 8 System.out.println("vip runing..."+i); 9 } 10 } 11 12 public static void main(String[] args) throws InterruptedException { 13 TestJoin testJoin = new TestJoin(); 14 Thread thread = new Thread(testJoin); 15 thread.start(); 16 17 for (int i = 0; i < 200; i++) { 18 System.out.println("main ..."+i); 19 if(i==50){ 20 System.out.println("vip coming "); 21 thread.join(); 22 } 23 } 24 } 25 }
线程观察状态:
package com.xun.state; public class TestState { public static void main(String[] args) { 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); //观察启动后 thread.start();//启动 state = thread.getState(); System.out.println(state); while(state!=Thread.State.TERMINATED){//只要线程没终止就一直执行 try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } state = thread.getState(); System.out.println(state); } } }
线程中断或结束后,一旦进入死亡状态,就不能再启动了。
线程优先级(priority)
1~10; get、set 方法。main方法默认优先级为5
优先级低只表示调度概率低,并非绝对,主要看cpu调度
守护(daemon)线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕
虚拟机不用等待守护线程执行完毕
如:后台记录操作日志,监控内存,垃圾回收等待
1 package com.xun.state; 2 3 public class TestDaemon { 4 public static void main(String[] args) { 5 God god = new God(); 6 You you = new You(); 7 Thread thread = new Thread(god); 8 thread.setDaemon(true); //设置为守护线程 9 thread.start(); 10 11 new Thread(you).start(); 12 } 13 } 14 15 class God implements Runnable{ 16 17 @Override 18 public void run() { 19 while(true){ 20 System.out.println("god is alive"); 21 } 22 } 23 } 24 25 class You implements Runnable{ 26 27 @Override 28 public void run() { 29 for (int i = 0; i < 36500; i++) { 30 System.out.println("you are alive "); 31 } 32 System.out.println("GoodBye,World!"); 33 } 34 }
线程同步
同一个资源,多个人想使用,最天然的解决办法就是排队,一个一个来
处理多线程问题是,多个线程访问同一个对象(并发),并且某些线程想修改这个对象。这时候我们就需要线程同步。线程同步其实就是一个等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。
形成条件:队列 + 锁
由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入 锁机制 synchronized,当一个线程获得对象的排他锁,独占资源,其他线程必须等待使用后释放锁即可。存在以下问题:
一个线程持有锁会导致其他所有需要此锁的线程挂起;
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换和调度延时,引起性能问题;
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题。
线程不安全的3个例子
1 package com.xun.syn; 2 3 //不安全的买票 4 //有重票和负数 5 public class UnSafeBuyTicket { 6 public static void main(String[] args) { 7 BuyTicket buyTicket = new BuyTicket(); 8 9 new Thread(buyTicket,"ant").start(); 10 new Thread(buyTicket,"bord").start(); 11 new Thread(buyTicket,"cat").start(); 12 13 } 14 15 } 16 17 class BuyTicket implements Runnable{ 18 19 //票 20 private int ticketNum = 10; 21 //外部停止方式 22 private boolean flag = true; 23 24 @Override 25 public void run() { 26 //买票 27 while(flag){ 28 buy(); 29 } 30 31 } 32 private void buy(){ 33 if(ticketNum<=0){ 34 flag=false; 35 return ; 36 } 37 //模拟延时 38 try { 39 Thread.sleep(100); 40 } catch (InterruptedException e) { 41 e.printStackTrace(); 42 } 43 System.out.println(Thread.currentThread().getName()+"拿到票"+ticketNum--); 44 } 45 }
1 package com.xun.syn; 2 3 public class UnSafeBank { 4 public static void main(String[] args) { 5 Account account = new Account(100,"Marry money"); 6 7 Drawing you = new Drawing(50,"you",account); 8 Drawing gril = new Drawing(100,"gril",account); 9 10 you.start(); 11 gril.start(); 12 } 13 } 14 15 //账户 16 class Account{ 17 String name; 18 int money; 19 20 public Account(int money,String name){ 21 this.money = money; 22 this.name = name; 23 } 24 } 25 26 //银行 27 class Drawing extends Thread{ 28 Account account; 29 int drawingMoney; 30 int nowMoney; 31 32 public Drawing(int drawingMoney,String name,Account account){ 33 super(name); 34 this.account=account; 35 this.drawingMoney=drawingMoney; 36 } 37 38 //取钱 39 @Override 40 public void run(){ 41 //判断有没有钱 42 if(account.money-drawingMoney<0){ 43 System.out.println(Thread.currentThread().getName()+"钱不够,取不了"); 44 return; 45 } 46 try { 47 Thread.sleep(100); 48 } catch (InterruptedException e) { 49 e.printStackTrace(); 50 } 51 account.money-=drawingMoney; 52 nowMoney+=drawingMoney; 53 54 System.out.println(account.name+"余额为"+account.money); 55 //Thread.currentThread().getName()等价于this.getName(),因为继承自Thread 56 System.out.println(this.getName()+"手里的钱"+nowMoney); 57 } 58 }
package com.xun.syn; import java.util.ArrayList; import java.util.List; //线程不安全的集合,加到同一位置了 public class UnSafeList { public static void main(String[] args) { List<String> list = new ArrayList<>(); 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()); } }