学完东西后,要学会总结,学会记录笔记,这样才会有更大的收获
首先我们了解线程和进程的基本概念
一、概念(程序 进程 线程)
1、程序:指令集 静态概念
2、进程:操作系统 调度程序 动态概念
3:线程:在进程内多条执行路径 真正的多线程是指多个cpu
二、创建
1.1 继承Thread +run()
启动:创建类对象+对象.start()
package com.org.pc; /** * 模拟龟兔赛跑 * @author lyy * 创建多线程 继承Thread 重写run(线程体) * 使用线程创建子类对象,调用对象.start() */ public class Rabbit extends Thread{ @Override public void run() { for (int i = 0; i <100; i++) { System.out.println("兔子跑了"+ i + "步"); } } public static void main(String[] args) { Thread t1 = new Rabbit(); Thread t2 = new Tortoise(); t1.start(); t2.start(); } } class Tortoise extends Thread{ @Override public void run() { for (int i = 0; i <100; i++) { System.out.println("乌龟跑了"+ i + "步"); } } }
1.2 实现Runable+run()
启动:使用静态代理
1、创建真实角色
2、创建代理角色 Thread+引用
3、代理角色.start()
/** * 使用Runable 创建 线程 * 1、类实现Runable接口 +重写run() 真实角色类 * 2、启动多线程 使用静态代理 * 1)创建真实角色 * 2)创建代理角色+真实角色引用 * 3)调用.start() * @author lyy * */ public class Programer implements Runnable{ @Override public void run() { for (int i = 0; i < 1000; i++) { System.out.println("一边敲代码。。。。。"); } } public static void main(String[] args) { // 1)创建真实角色 Programer pro = new Programer(); // 2)创建代理角色+真实角色引用 Thread proxy = new Thread(pro); // 3)调用.start() proxy.start(); for (int i = 0; i < 1000; i++) { System.out.println("一边聊QQ。。。。。"); } } }
1.3 实现Callable(了解)
通过Callable接口实现多线程
优点:可以获取返回值
package com.org.pc; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; /** * 使用Callable创建线程 * @author lyy * */ public class Call { public static void main(String[] args) throws InterruptedException, ExecutionException { //创建线程 ExecutorService ser = Executors.newFixedThreadPool(2); Race tor = new Race("千年王八",1000); Race rabbit = new Race("小兔子",500); //获取值 Future<Integer> result1 = ser.submit(tor); Future<Integer> result2 = ser.submit(rabbit); Thread.sleep(2000);//休眠2秒 tor.setFlag(false);//停止线程体中的循环 rabbit.setFlag(false);//停止线程体中的循环 int num1 = result1.get(); int num2 = result2.get(); System.out.println("大乌龟跑了----->"+num1+"步"); System.out.println("小兔子跑了----->"+num2+"步"); //停止服务 ser.shutdown(); } } class Race implements Callable<Integer>{ private String name;//名称 private long time;//延时时间 private boolean flag = true; private int step = 0;//步数 public Race(){ } public Race(String name) { super(); this.name = name; } public Race(String name, int time) { super(); this.name = name; this.time = time; } @Override public Integer call() throws Exception { while(flag){ Thread.sleep(time);//延时 step++; } return step; } public String getName() { return name; } public void setName(String name) { this.name = name; } public long getTime() { return time; } public void setTime(long time) { this.time = time; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public int getStep() { return step; } public void setStep(int step) { this.step = step; } }
三:线程运行示意图
新生-->就绪-->运行-->阻塞-->终止
四:线程的终止(重点)
1、自然终止:线程体正常执行完毕
2、外部干涉
1)线程类中 定义 线程体使用的标识
2)线程提供使用该标识
3) 对外提供方法改变该标识
4) 外部根据条件调用该方法
package com.org.status; public class StopDemo1 { public static void main(String[] args) { Study stu = new Study(); new Thread(stu).start(); //外部改变该标识 for (int i = 0; i < 100; i++) { if(i==50){//外部干涉 stu.stop(); } System.out.println("main......"+i); } } } class Study implements Runnable{ //1)线程类中 定义 线程体使用的标识 private boolean flag =true; @Override public void run() { //2)线程提使用该标识 while(flag){ System.out.println("Study thread ....."); } } //3)、对外提供方法改变该标识 public void stop(){ this.flag = false; } }
五:阻塞
IsAlive() 判断线程是否还活着,既线程是还未终止
getPriority()获得线程的优先级数值
setPriority() 设置线程的优先级数值
setName()给线程一个名字
getName()取得线程的名字
currentThread()取得当前正在运行的线程对象也就是取得自己本身
阻塞:join yield sleep(重点)
1、Join:合并线程
package com.org.status; /** * join:合并线程 * @author lyy * */ public class JoinDemo01 extends Thread { public static void main(String[] args) throws InterruptedException { JoinDemo01 join = new JoinDemo01(); Thread t = new Thread(join);//新增 t.start();//就绪 //cpu调度运行 for (int i = 0; i < 100; i++) { if(50 == i){ t.join();//main阻塞 } System.out.println("main......"+i); } } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("join......"+i); } } }
2、Yield:暂停自己的线程
package com.org.status; import javax.sound.midi.Synthesizer; public class YieldDemo01 extends Thread{ public static void main(String[] args) { YieldDemo01 yield = new YieldDemo01(); Thread t = new Thread(yield);//新生 t.start();//就绪 //cpu调度运行 for (int i = 0; i < 100; i++) { if(i%20 == 0){ //暂停本线程main Thread.yield(); } System.out.println("main......."+i); } } @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println("yield......."+i); } } }
3、Sleep():休眠,不释放锁
1)、时间相关(倒计时)
package com.org.status; import java.text.SimpleDateFormat; import java.util.Date; /** * 倒计时 * 1、倒数10个数,一秒内打印一个 * 2、倒计时 * @author lyy * */ public class SleepDemo01 { public static void main(String[] args) throws InterruptedException { Date endTime = new Date(System.currentTimeMillis() + 10*1000); long end = endTime.getTime(); while(true){ //输出 System.out.println(new SimpleDateFormat("mm:ss").format(endTime)); //构建下一秒的时间 endTime = new Date(endTime.getTime() - 1000); //等待一秒时间 Thread.sleep(1000); //如果在十秒以内继续否则退出 if(end -10000 > endTime.getTime()){ System.out.println("ending"); break; } } } public static void test1() throws InterruptedException{ int num = 10; while(true){ System.out.println(num--); Thread.sleep(1000);//暂停 if(num <= 0){ break; } } } }
2)、模拟网络延时
package com.org.status; /** * Sleep模拟 网络延时 线程不安全的 * @author lyy * */ public class SLeepDemo02 { public static void main(String[] args) { //真实角色 Web12306 web = new Web12306(); //代理角色 Thread t1 = new Thread(web,"工程师"); Thread t2 = new Thread(web,"黄牛已"); Thread t3 = new Thread(web,"路人甲"); //启动线程 t1.start(); t2.start(); t3.start(); } } class Web12306 implements Runnable{ private int num = 80; @Override public void run() { while(true){ if(num <= 0){ break;//跳出循环 } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } }
同步:并发多个线程访问同一份资源确保资源安全 ==> 线程安全
Synchronized --> 同步
一、同步块
Synchronized(引用类型|this|类.class){
}
package com.org.syn; public class SynDemo01 { public static void main(String[] args) { //真实角色 Web123 web = new Web123(); //代理角色 Thread t1 = new Thread(web,"工程师"); Thread t2 = new Thread(web,"黄牛已"); Thread t3 = new Thread(web,"路人甲"); //启动线程 t1.start(); t2.start(); t3.start(); } } class Web123 implements Runnable{ private int num = 10; private boolean flag = true; @Override public void run() { while(flag){ test3(); } } //线程不安全 锁定资源不正确 public void test6(){ //a b c if(num <= 0){ flag =false;//跳出循环 return; } synchronized (this) { try { Thread.sleep(500);//模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程不安全 锁定资源不正确 public void test5(){ //a b c synchronized ((Integer)num) { if(num <= 0){ flag =false;//跳出循环 return; } try { Thread.sleep(500);//模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //锁定范围不正确 public void test4(){ //a b c synchronized (this) { if(num <= 0){ flag =false;//跳出循环 return; } try { Thread.sleep(500);//模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } //线程安全,锁定正确 public void test3(){ //a b c synchronized (this) { if(num <= 0){ flag =false;//跳出循环 return; } try { Thread.sleep(500);//模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } } public synchronized void test2(){ if(num <= 0){ flag =false;//跳出循环 return; } try { Thread.sleep(500);//模拟 延时 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } //线程不安全 public void test1(){ if(num <= 0){ flag =false;//跳出循环 return; } try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"抢到了"+num--); } }
二、同步方法
Synchronized
线程安全的效率慢,保证资源的正确
线程不安全的效率快
三、死锁:过多的同步容易造成死锁
package com.org.syn; /** * 过多的方法可能造成死锁 * @author lyy * */ public class SncDemo3 { public static void main(String[] args) { Object g = new Object(); Object m = new Object(); Test t1 = new Test(g,m); Test t2 = new Test(g,m); Thread proxy1 = new Thread(t1); Thread proxy2 = new Thread(t2); proxy1.start(); proxy2.start(); } } class Test implements Runnable{ Object goods; Object money; public Test(Object goods, Object money) { this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized (goods) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (money) { // System.out.println("一手给货"); } } } class test2 implements Runnable{ Object goods; Object money; public test2(Object goods, Object money) { this.goods = goods; this.money = money; } @Override public void run() { while(true){ test(); } } public void test(){ synchronized (money) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } synchronized (goods) { System.out.println("一手给货"); } } }
解决方法:生产者消费者模式
先生产,在消费
package com.org.pro; /** * 一个场景,一个共同的资源 * 生产者和消费者模式信号灯法 * wait() 等待 释放锁 sleep 不释放锁 * notify()/notifyAll() 唤醒 * 与 synchronized * @author lyy * */ public class Movie { private String pic; //信号灯 //flag --> T 生产者生产 消费者消费 生产完成后 通知消费 //flag --> f 消费者消费 生产者等待 消费完成后唤醒生产 private boolean flag = true; public synchronized void play(String pic){ if(!flag){//生产者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //开始生产 try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("生产了:"+pic); //生产完毕 this.pic = pic; //通知消费 this.notify(); //生产者停下 this.flag=false; } public synchronized void watch(){ if(flag){//消费者等待 try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } try { //开始消费 Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("消费了:"+pic); //消费完毕 //通知生产 this.notify(); //消费停止 this.flag = true; } } }
package com.org.pro; /** * 生产者 * @author lyy */ public class Player implements Runnable{ private Movie m; public Player(Movie m) { super(); this.m = m; } @Override public void run() { for (int i = 0; i < 100; i++) { if(0 == i%2){ m.play("左青龙"); }else{ m.play("右白虎"); } } } }
package com.org.pro; /** * 消费者 * @author lyy * */ public class Watcher implements Runnable{ private Movie m; public Watcher(Movie m) { super(); this.m = m; } @Override public void run() { for (int i = 0; i < 100; i++) { m.watch(); } } }
package com.org.pro; public class App { public static void main(String[] args) { //共同的资源 Movie m = new Movie(); //多线程 Player p = new Player(m); Watcher w = new Watcher(m); new Thread(p).start(); new Thread(w).start(); } }
新生--> start -->就绪-->运行-->阻塞-->终止
线程是一个好用但是也有点复杂的技术,博主会的也只是一点点皮毛,希望有机会能和各位多多学习!有什么不对的地方,请大家多多包涵!
想要更加熟练的运用线程还需要多多深入了解!