线程学习(持续更新)
作者:互联网
线程学习
线程简介
多任务:看起来多个任务同时做 但本质上我们的大脑在同一时间只做了一件事情
多线程 :增加车道 不担心道路阻塞
普通方法调用和多线程
-
普通方法调用:只有主线程一条执行路径
-
多条执行路径:主线程和子线程并行交替执行
程序 进程 线程
在操作系统中运行的程序就是进程
一个进程可以有多个线程 如视频中同时听声音 看画面..
自我理解: 程序运行->就叫进程 一个进程里有多个线程 线程是执行人物的 进程只是一个壳子 用来装载那些线程 并且系统是以进程这个壳子为单位来分配资源
核心概念
-
线程就是独立执行的路径
-
在程序运行时 即使没有自己创建线程 ,后台也会有多个线程 如主线程 gc线程;
-
main()称之为主线程, 为系统的入口, 用于执行整个程序
-
在一个进程中 如果开辟了多个线程, 线程的运行由调度器安排调度, 调度器是与操作系统紧密相关的 , 先后顺序是不能人为干预的
-
对同一份资源操作时 会存在资源抢夺的问题, 需要加入并发控制 ;
-
线程会带来额外的开销 如cpu调度时间, 并发控制开销
一. 创建线程 实现线程
1. 继承Thread类
子类继承Thread类 具备多线程能力
启动线程 :子类对象.start();
不建议使用 避免oop单继承局限性
自定义线程继承Thread类
重写run()方法 编写线程执行体
创建线程对象 调用start()方法启动线程
package kuang;
public class ThreadReview extends Thread{
//线程入口点
//Thread实现了Runnable的run 自己写的类要去重写这个run()
@Override
public void run() {
//线程体
for(int i=0;i<100;i++)
{
//try {
//Thread.sleep(1);
/} catch (InterruptedException e) {
// TODO Auto-generated catch block
// e.printStackTrace();
//}
System.out.println("子线程--"+i);
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ThreadReview t = new ThreadReview(); //创建线程对象
t.start(); //子线程执行run()方法 主线程和子线程同时进行 主线程和子线程都没停下 在同一时间只能执行一条线程(因为我们的电脑是单核的)
for(int i=0;i<100;i++)
{
System.out.println("我在学习多线程--"+i);
}
}
}
运行效果
我在学习多线程--0
我在学习多线程--1
子线程--0
我在学习多线程--2
子线程--1
我在学习多线程--3
子线程--2
我在学习多线程--4
子线程--3
我在学习多线程--5
子线程--4
我在学习多线程--6
子线程--5
我在学习多线程--7
子线程--6
我在学习多线程--8
子线程--7
我在学习多线程--9
子线程--8
我在学习多线程--10
子线程--9
我在学习多线程--11
子线程--10
我在学习多线程--12
子线程--11
我在学习多线程--13
子线程--12
我在学习多线程--14
子线程--13
我在学习多线程--15
子线程--14
我在学习多线程--16
子线程--15
我在学习多线程--17
子线程--16
我在学习多线程--18
子线程--17
我在学习多线程--19
子线程--18
我在学习多线程--20
子线程--19
子线程--20
下载网图 不会导入jar包 学完这一章再补上
package com.kuang.thread;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
//练习Thread,实现一个多线程同步下载图片
public class TestThread02 extends Thread {
private String url; //网络我图片地址
private String FileName; //保存的文件名
public TestThread02(String url, String FileName) {
this.url = url;
this.FileName = FileName;
}
//下载图片线程的执行体
@Override
public void run() {
super.run();
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url, FileName);
System.out.println("下载的文件名:" + FileName);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
TestThread02 testThread01 = new TestThread02("https://www.baidu.com/img/baidu_jgylogo3.gif", "copy1.png");
TestThread02 testThread02 = new TestThread02("https://www.baidu.com/img/baidu_resultlogo@2.png", "copy2.png");
TestThread02 testThread03 = new TestThread02("https://www.baidu.com/img/bd_logo1.png", "copy3.png");
//理想状态是先下载t1,最后t2,最后t3
//实质上是几乎同时执行,不一定是这个顺序
testThread01.start();
testThread02.start();
testThread03.start();
}
}
//下载器
class WebDownloader {
//下载方法
public void downloader(String url, String filename) throws IOException {
try {
FileUtils.copyURLToFile(new URL(url), new File(filename));
} catch (IOException e) {
e.printStackTrace();
System.out.println("IO异常");
}
}
}
//运行结果
//下载的文件名:copy3.png
//下载的文件名:copy2.png
//下载的文件名:copy1.png
2.实现Runnable接口
实现接口Runnable 具有多线程能力
启动线程 传入目标对象 ; Thread类对象.start();
推荐使用 :避免单继承局限性
灵活方便 方便同一个对象被多个线程使用
定义MyRunnable类实现Runnable接口
实现run()方法 编写线程执行体
创建线程对象 调用start()方法启动线程
package kuang;
public class MyRunnable implements Runnable{
public void run() {
for(int i=0;i<50;i++) {
System.out.println("子线程---"+i);
}
}
public static void main(String args[]) {
Runnable r = new MyRunnable();//创建Runnable接口的实现类对象
Thread t =new Thread(r);//创建线程对象 通过线程对象开启线程 ---代理
t.start();
// new Thread(new MyRunnable()).start();
for(int i=0;i<100;i++) {
System.out.println("main线程--"+i);
}
}
}
案例 买火车票
多个线程共享同一资源可能会发生冲突
package kuang;
public class MyTask implements Runnable{
private static int ticketNum = 0 ;//好像不写static也行 因为那些线程用到都是同一个对象MyTask
@Override
public void run() {
// TODO Auto-generated method stub
while(true) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if(ticketNum>10) {
break;
}
System.out.println(Thread.currentThread().getName()+"买到了第"+(ticketNum++)+"票");
}
}
public static void main(String args[]) {
MyTask mt = new MyTask();//一个接口的实现对象可以交给多个线程类对象使用 方便 如果用自己写Thread类的方法的话 需要写三个 但还都是一样的方法
Thread t1 = new Thread(mt,"小明");//第二个参数是线程的名字
Thread t2 = new Thread(mt,"老师");
Thread t3 = new Thread(mt,"黄牛");
t1.start();
t2.start();
t3.start();
}
}
案例 龟兔赛跑
package kuang;
public class Race1 implements Runnable{
private static String winner = null;
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<=100;i++)
{
if(Thread.currentThread().getName()=="兔子") {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
boolean flag = GameOver(i);
if(flag) break;/**跳出循环 结束循环 线程体的任务结束了 故线程也就结束了 结束了比赛**/
//先判断 后输出 有可能这步走不出来(比赛已经结束)
System.out.println(Thread.currentThread().getName()+"走了第"+i+"步");
}
}
private boolean GameOver(int step) {
// TODO Auto-generated method stub
if(winner!=null) return true;//为败者准备的
if(step>=100) {
winner = Thread.currentThread().getName();
System.out.println(winner+"走了第100步"+" "+"winner is"+winner);
return true;
}
return false;
}
public static void main(String args[]) {
Race1 r = new Race1();
new Thread(r,"兔子").start();
new Thread(r,"乌龟").start();
}
}
Callable接口 了解即可
-
可以有返回值
-
可以抛出异常
-
关于并发编程的
package com.kuang.thread;
import java.util.concurrent.*;
/**
*Callable<Boolean>表示接口需要实现的Call()方法返回值类型
*/
public class TestCallable01 implements Callable<Boolean> {
private String url; //网络我图片地址
private String FileName; //保存的文件名
public TestCallable01(String url, String FileName){
this.url = url;
this.FileName = FileName;
}
//下载图片线程的执行体
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
try {
webDownloader.downloader(url,FileName);
System.out.println("下载的文件名:" + FileName);
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestCallable01 t1 = new TestCallable01("https://www.baidu.com/img/baidu_jgylogo3.gif","copy1.png");
TestCallable01 t2 = new TestCallable01("https://www.baidu.com/img/baidu_resultlogo@2.png","copy2.png");
TestCallable01 t3 = new TestCallable01("https://www.baidu.com/img/bd_logo1.png","copy3.png");
//1:创建执行服务:
//param: 有创建三个线程
ExecutorService ser = Executors.newFixedThreadPool(3);
//2:提交执行
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//3:获取结果
boolean rs1 = r1.get();
boolean rs2 = r2.get();
boolean rs3 = r3.get();
System.out.println(rs1);
System.out.println(rs2);
System.out.println(rs3);
//4:关闭服务
ser.shutdown();
}
}
//
下载的文件名:copy1.png
下载的文件名:copy3.png
下载的文件名:copy2.png
true
true
true
静态代理
静态代理:线程的底部实现原理*
真实对象和代理对象都要实现同一个接口
代理对象要代理真实角色[真实角色以参数传进去]
好处:代理对象可以做很多真实对象做不了的事情 真实对象可以专注做自己的事情
package kuang;
/**静态代理模式**/
//静态代理模式:
//代理对象与真实对象都要实现同一个接口
//代理对象要代理真是角色[参数传进去]
/**好处:代理对象可以做很多真实对象做不了的事情 真实对象可以专注做自己的事情**/
//注意同一个包下的类不能重名
public class StaticProxyTest {
public static void main(String args[]) {
//Youx you = new Youx();
//WeddingCompanyx wdx = new WeddingCompanyx(you);
//wdx.HappyMarry();
new WeddingCompany(new Youx()).HappyMarry();
}
}
//接口 一个特殊的类 类名后面不带括号!
//静态代理模式:
//代理对象与真实对象都要实现同一个接口
//代理对象要代理真是角色[参数传进去]
//每个编译单元(文件)都只能有一个public类。这表示,每个编译单元都有单一的公共接口,用public类来表现的
interface Marryx{
void HappyMarry();
}
/**真实对象 你去结婚**/
class Youx implements Marryx{
public void HappyMarry() {
System.out.println("shc今天结婚了 超开心!");
}
}
/**代理角色 帮助你结婚**/
class WeddingCompanyx implements Marryx{
private Marryx target;
//You实现了Marru接口 即可交由接口变量对象管理 并且接口对象只能带调用接口中有的函数
//实例变量如果不初始化会自动赋默认值 所以不初始化也不报错
public WeddingCompanyx(Marryx target) {
this.target = target;
}
public void HappyMarry() {
before();//代理对象多做的事情
this.target.HappyMarry();///通过代理角色Company来调用[真实对象]You的结婚方法
after();//代理对象多做的事情
}
private void after() {
// TODO Auto-generated method stub
System.out.println("收尾款");
}
private void before() {
// TODO Auto-generated method stub
System.out.println("布置现场");
}
}
new Thread(new Runnable(){public void run(){..}}).start(); new Thread(()->{System.out.println();}).start(); new WeddingCompany(new You()).HappyMarry(); Thread -Runnable 代理对象 实现了Runnable接口 new Runnbale() -- You 真实对象 实现了Runnabe接口 (HappMarry和start/run 是覆盖的接口的方法) 静态代理:线程的底部实现原理
Lambda表达式
-
避免内部类定义过多
-
其实质属于函数式编程的概念
-
为什么使用Lambda表达式?
避免匿名内部类定义过多
代码简洁
去掉了一堆没有意义的代码 只留下核心的逻辑
-
函数式接口的定义:
在任何接口中 如果只包含唯一一个抽象方法 那么他就是一个函数式接口
public interface Runnable{ public abstract void run(); }
对于函数式接口 我们可以通过Lambda表达式来创建该接口的对象
-
Lambda推导
package kuang03; public class LambdaTest { //3.静态内部类(如果不是静态就不能new这个类? 我得补补前面了) static class Like2 implements Ilike{ @Override public void Lambda() { System.out.println("I like Lambda2"); } } public static void main(String[] args) { // TODO Auto-generated method stub new Like().Lambda(); new Like2().Lambda(); //4. 局部内部类 class like3 implements Ilike{ @Override public void Lambda() { System.out.println("I like Lambda3"); } } new like3().Lambda(); //5.匿名内部类 没有名字 必须借助接口或者父类 Ilike s = new Like() { @Override public void Lambda() { System.out.println("I like Lambda---匿名类"); } }; s.Lambda(); //6.Lambda简化 Ilike l = ()->{ System.out.println("I like Lambda---Lamda表达式"); }; l.Lambda(); } } //1.函数式接口 interface Ilike{ public abstract void Lambda(); } //2.外部类 class Like implements Ilike{ public void Lambda() { System.out.println("I like Lambda"); } } //用接口的引用类对象去管理Ilove(实现接口的类)的实际对象 抽象类不能产生对象
//运行结果 I love you-->521 I love you-->1314 I love you-->1000 1098
-
Lambda简化案例
package kuang03; /** * 总结: * lambda表达式只能由一行代码的情况下才能简化成为一行 如果有多行 要用代码块{}包裹 * 前提是接口必须是函数式接口-[即只有一个方法] * 多个参数也可以去掉参数类型,要去掉就都去掉 必须加括号 * @author de'l'l *@version */ public class LambdaTest2 { public static void main(String[] args) { // TODO Auto-generated method stub /** new,接口,方法名不用写了 ()里面放传给方法的参数 {}写方法的内容 Lambda表达式 (参数)->结果 参数是方法需要参数 结果是方法的内容**/ //1.Lambda简化 Love s = (int a,int b)->{ System.out.println("I love you-->"+a); }; s.love(521,520); //简化2.可以省去传给方法的参数类型 要省就都省 Love k = (a,b)->{ System.out.println("I love you-->"+a); System.out.println("I love you-->"+b); }; k.love(1314,1000); //简化3.方法为一句话时 可以省去花括号 但不推荐 Love g = (a,b)->System.out.println(a+b); g.love(99,999); } } interface Love{ // void love(int a); void love(int a,int b); }
-
new,接口,方法名不用写了
-
()里面放传给方法的参数 {}写方法的内容
-
Lambda表达式 (参数)->结果
-
参数是方法需要参数 结果是方法的内容
线程状态
五大状态
-
创建状态 :Thread t = new Thread();
-
就绪状态 : 当调用start()方法 线程立即进入就绪状态 但不意味着立即执行
-
阻塞状态 : 当调用wait()/sleep()方法或者同步锁定时,线程进入阻塞状态 就是代码不往下执行.(我觉得这就是说这不是退出了这个线程 也不是说从线程体(方法/执行任务)的代码块出来了 只是卡在那里不往下走了) 阻塞事件解除后,重新进入就绪状态 等待cpu调度执行
-
运行状态 : 进入就绪状态 线程才真正执行线程体的代码块
-
死亡状态 : 线程中断或结束 一旦进入死亡状态 就不能再次启动
线程方法
setPriority(int new Priority) //更改线程优先级 static void sleep(long mills) //在指定的毫秒数内 让当前正在执行的线程体休眠 void join() //等待该线程终止 static void yield //暂停正在执行的线程对象 并执行其他线程 void interrupt() //中断线程 别用这个方法 boolean isAlive() //判断线程是否处于活动状态
停止线程
-
不推荐使用JDK提供的stop() destroy等方法(已废弃)
-
推荐让线程自己停下来
-
建议使用一个人标志位进行终止变量 当flag = false 则终止线程运行
样例
//模板 public TestStop implements Runnable{ //1.设置一个标识位 private boolean flag = true; //2.线程体使用该标识 public void run(){ while(flag){ ... } } //3.对外提供方法改变标识 public void stop(){ this.flag = flase; } public static void main(String args[]){ Teststop teststop = new TestStop();//这个对象既可用TestStop引用管理,也可以用Runnable管理 new Thread(teststop).start();//线程进入就绪状态 teststop.stop();//利用自己imlements Runnable的时候写的stop方法来结束线程 而不是调用Thread类本身的stop方法 } }
案例 不看也罢hh
package kuang03; /** * 测试stop * 1.建议线程正常停止 * 2.建议使用标志变量 * 3.不要使用stop或destroy等过时或者JDK不建议使用的的方法 * @author de'l'l * */ //接口类写法 public class TestStop implements Runnable{ /**设置一个标识位**/ private boolean flag = true; @Override public void run() { // TODO Auto-generated method stub int i=0; //如果放在while里面就会一直定义 while(flag) { System.out.println("run...Thread"+i++); } } //runnable接口:函数式接口 只有一个abstract的run方法 无stop方法 /**为外部提供一个方法改变标识位**/ //设置一个公开的方法转换标识位 结束线程 public void stop() { this.flag = false; } public static void main(String args[]) { TestStop teststop = new TestStop(); new Thread(teststop).start();//teststop implements 了Runnable接口 那么他就可以被当作Runnbale类型来使用 Thread需要一个Runnable 所以可以传进去 for(int i=0;i<1000;i++) { System.out.println("main--"+i); if(i==900) { // 调用stop方法 使线程停止 teststop.stop();//teststop的flag变成flase run方法里的while终止 TestStop提供的run()方法结束 线程结束 System.out.println("thread停止了"); } } } } //继承自Thread类写法 //public class TestStop extends Thread{ // // private boolean flag = true; // // @Override // public void run() { // int i=1; // while(flag) { // System.out.println("run...Thread"+i++); // } // } // // public void stopp() { // this.flag = false; // } // // public static void main(String[] args) { // // TODO Auto-generated method stub // Thread t = new TestStop(); // t.start(); // for(int i=0;i<2000;i++) { // System.out.println("main线程--"+i); // if(i==900) { // t.stop(); // System.out.println("t线程停止了"); // } // } // } // //}
线程休眠_sleep
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在异常InterruptedException
-
sleep时间达到后线程进入就绪状态
-
sleep可以模拟网络延时 倒计时等
-
每个对象都有一个锁 sleep不会释放锁
-
模拟网络延时 扩大问题的发生性
-
模拟倒计时
-
更新时间
模拟网络延时 扩大问题发生性
package kuang; //发现问题:多个线程操作统一资源 的情况下 数据紊乱 线程不安全 public class TestThread3 implements Runnable{ private int ticketNum = 10; public void run() { while(true) { if(ticketNum<=0) break; try { //模拟延时 Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNum--+"张票"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { // TODO Auto-generated method stub TestThread3 task = new TestThread3(); new Thread(task,"小明").start(); new Thread(task,"老师").start(); new Thread(task,"黄牛党").start(); } }
模拟倒计时
public class TestSleep{ private static int num = 10; public static void tenDown throws InterruptedException(){ while(true){ Thread.sleep(1000); System.out.println(num--); if(num<=0) break; } } public void static void main(String args[]){ try{ tenDown(); }catch(...){} } }
获取当前系统时间!!!很重要
public class TestSleep{ public static void main(String args[]){ //获取当前系统时间 Date startTime = new Date(System.currentTimeMills());//mill n.工厂 while(true){ try{ Thread.sleep(1000); // 格式化工厂 参数格式(小时 分钟 毫秒).format(时间); System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime)); startTime = new Date(System.currentTimeMills()); } } } }
线程礼让
-
礼让线程 让当前正在执行的线程暂停 但不阻塞
-
将线程从运行状态转化为就绪状态
-
让cpu重新调度,礼让不一定成功!看cpu心情
package kuang03; public class YieldTest { public static void main(String[] args) { // TODO Auto-generated method stub MyYield y = new MyYield(); new Thread(y,"t1").start(); new Thread(y,"t2").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()+"结束执行"); } }
join
尽量少用
-
插队
-
Join合并线程 代词线程执行完成后 再执行其他线程
public class TestJoin implements Runnable{ public static void main(String[] args) throws InterruptedException { // TODO Auto-generated method stub Thread t = new Thread(new TestJoin()); t.start(); for(int i=0;i<10;i++) { if(i==8) { t.join(); } System.out.println("main"+i); } } @Override public void run() { // TODO Auto-generated method stub for(int i=0;i<100;i++) { System.out.println("VIP来了"+i); } } } 运行结果 main0 main1 main2 VIP来了0 main3 main4 main5 VIP来了1 VIP来了2 VIP来了3 VIP来了4 VIP来了5 main6 VIP来了6 VIP来了7 main7 VIP来了8 VIP来了9 VIP来了10 VIP来了11 VIP来了12 VIP来了13 ... VIP来了97 VIP来了98 VIP来了99 main8 main9
线程状态观测
-
Thread.State
-
Thread.State state = t.getState();
-
System.out.println(state);
线程状态
-
NEW 尚未启动的线程
-
RUNNABLE 在JAVA虚拟中执行的线程处于此状态
-
BLOCKED 被阻塞等待监视器锁定的线程处于此状态
-
WAITING 正在等待另个一个线程执行特定动作的线程中处于此状态
-
TIMED_WAITING 正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
-
TERMINATED 已退出的线程处于此状态(死亡)
-
package kuang03; import java.lang.Thread.State; //观察测试线程状态 public class TestState { public static void main(String[] args) { // TODO Auto-generated method stub Thread t = new Thread(()->{ for(int i=0;i<6;i++) { try { Thread.sleep(1000); System.out.println(i); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println("\\\\\\\\"); }); /**获取线程状态**/ Thread.State state = t.getState(); System.out.println(state);//new //启动后 t.start(); state = t.getState();//runnable System.out.println(state); //运行时 while(state!=Thread.State.TERMINATED) {//只要线程不终止 就一直输出状态 try { Thread.sleep(50); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } state=t.getState();//waited 因为线程中有sleep System.out.println(state); } t.start();//线程一旦死亡之后的线程不能在开启 } }
NEW //Thread t = new Thread(()->{}); 刚被new出来 RUNNABLE //t。start() TIMED_WAITING //线程中有sleep TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING 0 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING 1 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING 2 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING 3 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING RUNNABLE //sleep的间断--在执行线程体 //sleep为阻塞事件 阻塞事件结束后 重新进入就绪状态 等待cpu调度执行 变成运行状态 4 TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING TIMED_WAITING 5 \\\\ TERMINATED Exception in thread "main" java.lang.IllegalThreadStateException at java.base/java.lang.Thread.start(Thread.java:792) at kuang03.TestState.main(TestState.java:44)
线程优先级
先设置优先级 后启动线程
-
Java提供一个线程调度器来监视程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行
-
线程优先级用数字表示1~10
-
Thread.MIN_PRIORITY = 1;
-
Thread.MAX_PRIORITY = 10;
-
Thread.NORM_PRIORITY = 5;
-
-
获取优先级:getPriority() 获取优先级:setPriority(int xx);
-
优先级低只是意味着获得调度的概率低 并不是优先级低就不会被调用了 这都是看CPU的调度 [有可能性能倒置]
/** * The minimum priority that a thread can have. */ public static final int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public static final int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public static final int MAX_PRIORITY = 10; /** * Returns a reference to the currently executing thread object. * * @return the currently executing thread. */ /** * Changes the priority of this thread. * <p> * First the {@code checkAccess} method of this thread is called * with no arguments. This may result in throwing a {@code SecurityException}. * <p> * Otherwise, the priority of this thread is set to the smaller of * the specified {@code newPriority} and the maximum permitted * priority of the thread's thread group. * * @param newPriority priority to set this thread to * @throws IllegalArgumentException If the priority is not in the * range {@code MIN_PRIORITY} to * {@code MAX_PRIORITY}. * @throws SecurityException if the current thread cannot modify * this thread. * @see #getPriority * @see #checkAccess() * @see #getThreadGroup() * @see #MAX_PRIORITY * @see #MIN_PRIORITY * @see ThreadGroup#getMaxPriority() */ public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } } /** * Returns this thread's priority. * * @return this thread's priority. * @see #setPriority */ public final int getPriority() { return priority; }
package kuang03; //测试优先级 public class TestPriority { public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"-----"+Thread.currentThread().getPriority()); Thread t1 = new Thread(new MyPriority());//代理:把线程体丢进去 让线程替他跑 Thread t2 = new Thread(new MyPriority()); Thread t3 = new Thread(new MyPriority()); Thread t4 = new Thread(new MyPriority()); Thread t5 = new Thread(new MyPriority()); Thread t6 = new Thread(new MyPriority()); //先设置优先级 后启动 t1.start(); t2.setPriority(2); t2.start(); t3.setPriority(9); t3.start(); t4.setPriority(Thread.MAX_PRIORITY);//10 注意是!!Thread.!!别忘了加类名 t4.start(); //因为/** // * The maximum priority that a thread can have. // */ // public static final int MAX_PRIORITY = 10; t5.setPriority(11); t5.start(); t6.setPriority(-1); t6.start(); } } class MyPriority implements Runnable{ @Override public void run() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()+"---"+Thread.currentThread().getPriority()); } } 我的不知道为啥结果总是性能倒置 main====>5 3====>5 1====>5 2====>1 4====>10
守护线程
人生不过三万天
开心点儿 朋友
-
线程分为 用户线程 和 守护线程
-
虚拟机必须确保用户线程执行完毕
-
虚拟机不用等待守护线程执行完毕
-
如,后台记录操作日志,监控内存,垃圾回收等待..
package kuang03; public class TestDaemon { public static void main(String[] args) { // TODO Auto-generated method stub God god = new God(); You you = new You(); Thread t = new Thread(god); //正常的线程都是用户线程 t.setDaemon(true); //默认为false表示是用户线程 正常的线程都是用户线程 t.start(); new Thread(you).start();//你 用户线程 } } //上帝 class God implements Runnable{ @Override public void run() { // TODO Auto-generated method stub while(true) { System.out.println("上帝保佑着你"); } } }
线程同步
-
并发:同一个对象被多个线程同时操作
-
线程同步:
-
处理多线程问题时,多个线程访问同一个对象 并且某些线程还想修改这个对象,这时候我们就需要线程同步,线程同步其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用
-
-
队列 和 锁 -----安全性
-
形成条件:队列+锁
-
-
由于同一进程的多个线程共享同一块储存空间 在带来方便的同时,也带来了访问冲突的问题,为了保证数据在方法中被访问时的正确性,在访问时加入锁机制synchronized,当一个线程获得对象的排他锁 独占资源,其他线程必须等待(这个线程)使用完对象后释放锁即可,存在以下问题:
-
一个线程持有锁会导致其他所有需要此锁的线程挂起
-
在多线程竞争下,加锁,释放锁会导致比较多的上下文切换 和调度延时,引起性能问题(为了安全性 一定会引起性能问题 鱼和熊掌不可兼得)
-
如果一个优先级高的线程等待一个优先级低的线程释放锁,会导致优先级倒置,引起性能问题
三大不安全样例
1.买票
package kuang03; //不安全的买票 有负数 public class UnSafeBuyTicket { public static void main(String[] args) { // TODO Auto-generated method stub BuyTicket by = new BuyTicket(); new Thread(by,"帅气的我").start(); new Thread(by,"可恶的你").start(); new Thread(by,"黄牛们").start(); //WRONG!!! 应该对同一种票数进行操作 而我们的票数定义的又不是static 所以必须使用同一个BUyTicket对象!! //线程自然应当是每次new一个新的(因为是不同人买) 而实现了接口的类的对像应该是同一个(要执行的线程体) //如果票数定义为static的话 则可以是使用不同对象 // new Thread(new BuyTicket(),"帅气的我").start(); // new Thread(new BuyTicket(),"臭臭的你").start(); // new Thread(new BuyTicket(),"黄牛们").start(); } } class BuyTicket implements Runnable{ //票 private static int ticketNum = 10; private boolean flag = true; //买票 @Override public void run() { // TODO Auto-generated method stub while(flag) { //模拟买票延时 buy(); } } //如何买票 public void buy() { if(ticketNum<=0) { flag = false; return ;//别忘了return!!!! 发现没票之后立刻return 不然又要多买 } try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"买到了第"+(ticketNum--)+"张票"); } }
可恶的你买到了第10张票 帅气的我买到了第9张票 黄牛们买到了第8张票 可恶的你买到了第7张票 帅气的我买到了第6张票 黄牛们买到了第5张票 帅气的我买到了第3张票 可恶的你买到了第4张票 黄牛们买到了第2张票 帅气的我买到了第1张票 可恶的你买到了第0张票 黄牛们买到了第-1张票
-
2.取钱
package kuang03; //人去银行取钱 public class UnSafeBank { public static void main(String[] args) { // TODO Auto-generated method stub Account account = new Account("结婚基金",101); Drawing you = new Drawing("you",account,50);//对同一个account进行操作 Drawing she = new Drawing("she",account,100); you.start(); she.start(); } } //账户 class Account{ //账户名 //余额 String name ; private int money; public Account(String name,int money) { this.name = name; this.money = money; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } //模拟取款 class Drawing extends Thread{ // private String name;收钱人的名字 不需要!!! 我们把线程的名字用人名命名即可 private int nowMoney;//手里的钱 private int drawingMoney;//要取的钱 private Account account;//从哪个账户里取 //Thread类中没有三个参数的构造函数 所以要自己写 public Drawing(String name,Account account,int drawingMoney) { //调用父类的构造函数 new Thread("名字"); 线程名字 super(name);//!!!!!!! extends Thread 为线程赋名字 this.account = account; this.drawingMoney = drawingMoney; } @Override public void run() { if(account.getMoney()<this.drawingMoney) { //这里的this.getName()<--->Thread.currentThread().getName() //因为Drawing类继承于Thread类 自然继承了所有public方法 //而在子类中getName方法有没有重写 所以this.(即该类对象.)引用的就是父类Thread的getName() System.out.println(account.getName()+"账户钱不够了"+this.getName()+"没取出来"); return ;//记得return啊 } /**Thread.sleep()放大问题的发生性**/ //出错原因:"各自把数据拷贝到各自的内存" try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } account.setMoney(account.getMoney() - drawingMoney); nowMoney = drawingMoney; System.out.println(account.getName()+"余额为"+account.getMoney()); System.out.println(this.getName()+"手里的钱为:"+nowMoney); } }
//在new Thread时可以给线程名字 //而不是在写Runnable接口的时候给线程起名字 //new Thread(接口,"名字"); //接口与"名字"不用自己写参数接收 会直接调用父类Thread的构造函数 结婚基金余额为-49 结婚基金余额为-49 she手里的钱为:100 you手里的钱为:50
3.不安全集合
package kuang03; import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnSafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for(int i=0;i<10000;i++) { //每次循环开启一个线程 往同一list里加东西 new Thread(()->{ list.add(Thread.currentThread().getName()); }).start(); //结果小于10000原因?:两个线程在同一时间对同一个List的同一个位置进行操作 //两个东西加到同一个位置 所以后加入的会覆盖先覆盖的 所以小于10000 } Thread.sleep(100); System.out.println(list.size()); } }
9994
同步方法
-
由于我们可以通过private关键字来保证数据对象只能被方法访问,所以我们只需要针对方法提出一套机制,这套机制就是sychronized关键字,它包括两种用法:synchronized方法和synchronize块
-
同步方法:public synchronized void method(int args){}
-
-
synchronized方法控制对象的访问,每一个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会被阻塞 方法一旦执行,就独占该锁,知道该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行
-
缺陷:若将一个大的方法申明为synchronized将会影响效率
-
同步方法弊端:方法里面需要修改的内容才需要锁,锁的太多 浪费资源,只读的地方不用上锁 修改的地方上锁就行
同步块
-
同步块:synchronized(Obj){}
-
Obj 称之为同步监视器
-
Obj可以是任何对象,但是推荐使用共享资源作为同步监视器
-
同步方法中无需指定同步监视器,因为同步方法的同步监视器就是this,就是这个对象本身,或者是class【反射中讲解】
-
-
同步监视器的执行过程
-
第一个线程访问,锁定同步监视器,执行其中代码
-
第二个线程访问,发现同步监视器被锁定,无法访问
-
第一个线程访问完毕,发现同步监视器没有锁,然后锁定并访问
-
第二个线程访问,发现同步监视器没有锁,然后锁定并访问
-
买票
package kuang03; //不安全的买票 有负数 public class UnSafeBuyTicket { public static void main(String[] args) { // TODO Auto-generated method stub BuyTicket by = new BuyTicket(); new Thread(by,"帅气的我").start(); new Thread(by,"可恶的你").start(); new Thread(by,"黄牛们").start(); //WRONG!!! 应该对同一种票数进行操作 而我们的票数定义的又不是static 所以必须使用同一个BUyTicket对象!! //线程自然应当是每次new一个新的(因为是不同人买) 而实现了接口的类的对像应该是同一个(要执行的线程体) //如果票数定义为static的话 则可以是使用不同对象 // new Thread(new BuyTicket(),"帅气的我").start(); // new Thread(new BuyTicket(),"臭臭的你").start(); // new Thread(new BuyTicket(),"黄牛们").start(); } } class BuyTicket implements Runnable{ //票 private static int ticketNum = 10; private boolean flag = true; //买票 @Override public void run() { // TODO Auto-generated method stub while(flag) { //模拟买票延时 buy(); } } //如何买票 //synchronized 同步方法 锁的是this public synchronized void buy() { if(ticketNum<=0) { flag = false; return ;//别忘了return!!!! 发现没票之后立刻return 不然又要多买 } try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"买到了第"+(ticketNum--)+"张票"); } }
取钱
synchronized块可以锁任何对象-----synchronized(对象){ ...} 我们应该锁增删改查的对象
package kuang03; //人去银行取钱 public class UnSafeBank { public static void main(String[] args) { // TODO Auto-generated method stub Account account = new Account("结婚基金",1000); Drawing you = new Drawing("you",account,50);//对同一个account进行操作 Drawing she = new Drawing("she",account,100); you.start(); she.start(); } } class Account{ //账户名 //余额 String name ; private int money; public Account(String name,int money) { this.name = name; this.money = money; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getMoney() { return money; } public void setMoney(int money) { this.money = money; } } //模拟取款 class Drawing extends Thread{ // private String name;收钱人的名字 不需要!!! 我们把线程的名字用人名命名即可 private int nowMoney;//手里的钱 private int drawingMoney;//要取的钱 private Account account;//从哪个账户里取 public Drawing(String name,Account account,int drawingMoney) { super(name);//!!!!!!! extends Thread 为线程赋名字 this.account = account; this.drawingMoney = drawingMoney; } @Override //这里对方法run()上锁没有用 两个对像 You 和She 各自开启一条线程 对同一个对象account进行修改访问 应该对account上锁 /**synchronized 默认锁的是this 是所在的这个类的对象本身**/ public void run() { /**synchronized块可以锁任何对象**/ /**我们应该锁增删改查的对象**/ synchronized(account) { if(account.getMoney()<this.drawingMoney) { //这里的this.getName()<--->Thread.currentThread().getName() //因为Drawing类继承于Thread类 自然继承了所有public方法 //而在子类中getName方法有没有重写 所以this.(即该类对象.)引用的就是父类Thread的getName() System.out.println(account.getName()+"账户钱不够了"+this.getName()+"没取出来"); return ;//记得return啊 } /**Thread.sleep()放大问题的发生性**/ //出错原因:"各自把数据拷贝到各自的内存" //停了1秒 you和she都走到了这里 两人会接尽同时对同一对象进行操作 //自我理解:停了1s you只对account.getMoney()和drawingMoney进行了判断操作 并没有改变余额(account的money) 所以she判断时用的也是这个数 //而计算时 you:101-50=51; she:51-100=-49; //所以出错了 try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } account.setMoney(account.getMoney() - drawingMoney); nowMoney = drawingMoney; System.out.println(account.getName()+"余额为"+account.getMoney()); System.out.println(this.getName()+"手里的钱为:"+nowMoney); } } } 结婚基金余额为950 you手里的钱为:50 结婚基金余额为850 she手里的钱为:100
List
package kuang03; import java.util.ArrayList; import java.util.List; //线程不安全的集合 public class UnSafeList { public static void main(String[] args) throws InterruptedException { List<String> list = new ArrayList<String>(); for(int i=0;i<10000;i++) { //每次循环开启一个线程 往同一list里加东西 new Thread(()->{ synchronized(list) { list.add(Thread.currentThread().getName()); } }).start(); //结果小于10000原因?:两个线程在同一时间对同一个List的同一个位置进行操作 //两个东西加到同一个位置 所以后加入的会覆盖先覆盖的 所以小于10000 } Thread.sleep(100); System.out.println(list.size()); } } 10000
JUC CopyOnWriteArrayList
package kuang03; import java.util.concurrent.CopyOnWriteArrayList; //JUC安全类型的集合 public class TestJUC { public static void main(String[] args) { // TODO Auto-generated method stub 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()); } } 10000
就相当于我们上面改进的集合·
List<String> list = new ArrayList<String>(); for(int i=0;i<1000;i++) { new Thread(()->{ synchnorized(list){ list.add(list.add(Thread.currentThread.getName());) } }).start(); }
死锁
-
多个线程各自占有一些公共资源,并且相互等待其他线程占有的资源才能运行,
而导致两个或者多个线程都在等待对方释放资源。都停止执行的情形,某一个同步块同时拥有”两个以上对象的锁“时,就可能会发生”死锁“的问题
-
死锁避免方法
-
产生死锁的四个必要条件
-
互斥条件:一个资源每次只能被一个进程请求使用。
-
请求与保持条件:一个进程因资源请求而阻塞时,对以获得的资源保持不放
-
不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
-
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
-
-
上面列出了死锁的四个必要条件 我们只要想办法批结其中的一个或多个条件,就可以避免死锁的发生
-
package kuang03; //死锁:多个线程互相抱着对方需要的资源 然后形成僵持 public class DeadLock { public static void main(String[] args) { // TODO Auto-generated method stub new MakeUp("灰姑娘",0).start(); new MakeUp("白雪公主",1).start(); } } class LipStick{ } class Mirror{ } class MakeUp extends Thread{ // private String name ;//化妆的人 private int choice;//选择 //猜测:如果这里写的是覆盖Runnable接口写法的话就不用static 因为丢进两个线程的是同一个对象 private static LipStick lipstick = new LipStick(); private static Mirror mirror = new Mirror(); public MakeUp(String name,int choice){ // this.name = name; super(name);//化妆的人作为线程的名字 this.choice = choice; } public void run() { //化妆 try { makeup(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //化妆 互相持有对方的锁 就是需要拿到对方的资源 private void makeup() throws InterruptedException { // TODO Auto-generated method stub if(choice==0) { synchronized(lipstick) {//获得口红的锁 System.out.println(this.getName()+"获得空红的锁"); Thread.sleep(2000); // synchronized(mirror) {//一秒钟后获得镜子 // System.out.println(this.getName()+"获得镜子的锁"); // } //灰姑娘抱着口红的锁不放 还要去拿镜子的锁 //但是想要拿到镜子的锁就必须先让白雪公主方下镜子的锁 但如果想让她放下镜子的锁 他就必须先拿到口红的锁 但口红锁现在又放不掉 所以死循环 //解决方法 把那这段synchronized代码放在这个synchronized外面 这样就可以顺利执行完第一段synchronized代码块 释放锁 } synchronized(mirror) {//一秒钟后获得镜子 System.out.println(this.getName()+"获得镜子的锁"); } } else { synchronized(mirror) {//获得镜子的锁 System.out.println(this.getName()+"获得镜子的锁"); Thread.sleep(2000); synchronized(lipstick) {//一秒钟获得口红的锁 System.out.println(this.getName()+"获得口红的锁"); } //白雪公主抱着镜子的锁不放 还要去拿口红的锁 } } } }
Lock
-
从JDK 5.0开始,Java提供了更强大的线程同步机制,通过显式定义同步锁对像来实现同步,同步锁使用Lock对象充当
-
Java.util.concurrent.locks.Lock【名字叫可重入锁】接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应该先获得Lock对象
-
ReentrantLock类实现了Lock,它拥有与stnchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁,释放锁。
买票例子
package kuang03; import java.util.concurrent.locks.ReentrantLock; public class TestLock { public static void main(String[] args) { // TODO Auto-generated method stub BuyTickets by = new BuyTickets(); new Thread(by).start(); new Thread(by).start(); new Thread(by).start(); } } class BuyTickets implements Runnable{ private static int num = 10; /**定义Lock锁**/ private final ReentrantLock lock = new ReentrantLock(); @Override public void run() { // TODO Auto-generated method stub while(true) { try { lock.lock();/**加锁**/ if(num<=0) break; try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(num--); }finally { lock.unlock();/**显式上锁之后一定显式释放锁*/ } } } } //能明显感觉出是一个一个出来的 10 9 8 7 6 5 4 3 2 1
Lock锁模板
class A{ private final ReentrantLock lock = new ReentrantLock(); public void m(){ lock.lock();//显式上锁 try{ //一定会是线程安全的代码 //保证是线程安全的代码 } finally{ lock.unlock();//释放锁 //如果同步代码有异常 要将unlock()写入finally语句块 } } }
synchronized 与 Lock的对比
-
Lock是显式锁(手动开启和关闭锁,别忘记关闭锁) synchronized,出了作用域自动释放
-
Lock只有代码块锁,synchronized有代码块锁和方法锁
-
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的拓展性(提供更多的子类)
-
优先使用顺序:
-
Lock》同步代码块(已经进入方法体,分配了相应资源)》同步方法(在方法体之外)
-
线程通信
生产者消费者模式
-
应用场景:生产者和消费者问题
-
假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中的产品取走消费
-
假设仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止
-
如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,知道仓库中再次放入产品为止
-
Java提供了几个方法解决线程之间的通信问题
-
wait()-----------------------------表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁
-
wait(long timeout)--------指定等待的毫秒数
-
notify()----------------------唤醒一个处于等待状态的线程
-
notifyAll()------------------唤醒同一个对象上所有调用wait()方法,优先级高的线程优先调度
-
-
注意:均是Object类的方法,都只能在 同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException
-
线程通信-分析
-
这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
-
对于生产者,没有生产产品之前,要通知消费者等待,而生产了产品之后,又需要马上通知消费者消费
-
对于消费者,在消费之后,要通知生产者已经结束消费,需要生产新的产品以供消费
-
在生产者消费者问题中,仅有synchronized是不够的
-
wait() notify()
-
容器法
-
生产者 只管生产
-
消费者 只管消费
-
容器(存放产品)(在临界时)通知谁该生产谁该消费
package kuang03; //生产者 消费者 产品 缓冲区 //生产者只管生产 //消费者只管消费 //通过缓冲区交互(何时消费何时生产) //方法.wait() 表示线程一直等待 直到其他线程通知 与sleep不同 会释放锁 //方法.notifyAll() 唤醒同一个对象上所有调用wait()的方法,优先级高的线程优先调度 public class TestPC { public static void main(String[] args) { // TODO Auto-generated method stub 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++) { System.out.println("生产了"+i+"只鸡"); container.push(new Chicken(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]; //容器计数器 private static int count = 0; //synchronized:同一时间只有一个线程能对这同一个container对象进行操作 改变它的count //生产者投入产品 public synchronized void push(Chicken chicken) { //如果容器满了,就需要等待消费者消费 if(count==chickens.length) { //生产者等待 try { this.wait(); }catch(InterruptedException e) { e.printStackTrace(); } } //如果没有满 需要放入产品 chickens[count++]=chicken;//count保持指向下一位放鸡的地方 //可以通知消费者消费了 this.notifyAll();//通知正在wait()的方法不用等待了 } //消费者消费产品 public synchronized Chicken pop() { //判断能否消费 if(count==0) { //消费者等待生产者生产 try { this.wait(); }catch(InterruptedException e) { e.printStackTrace(); } } //如果可以消费 Chicken chicken = chickens[--count]; //吃完了 通知生产者生产 this.notifyAll(); //返回消费的鸡 return chicken; } }
生产了0只鸡 生产了1只鸡 消费了--->0只鸡 消费了--->1只鸡 生产了2只鸡 生产了3只鸡 消费了--->2只鸡 消费了--->3只鸡 生产了4只鸡 生产了5只鸡 消费了--->4只鸡 生产了6只鸡 消费了--->5只鸡 消费了--->6只鸡 生产了7只鸡 生产了8只鸡 消费了--->7只鸡 生产了9只鸡 消费了--->8只鸡 生产了10只鸡 消费了--->9只鸡 消费了--->10只鸡 生产了11只鸡 生产了12只鸡 消费了--->11只鸡 生产了13只鸡 消费了--->12只鸡 生产了14只鸡 消费了--->13只鸡 生产了15只鸡 消费了--->14只鸡 生产了16只鸡 消费了--->15只鸡 消费了--->16只鸡 生产了17只鸡 生产了18只鸡 生产了19只鸡 消费了--->17只鸡 消费了--->19只鸡 生产了20只鸡 消费了--->18只鸡 消费了--->20只鸡 生产了21只鸡 生产了22只鸡 生产了23只鸡 消费了--->21只鸡 生产了24只鸡 消费了--->23只鸡 消费了--->24只鸡 生产了25只鸡 生产了26只鸡 消费了--->22只鸡 消费了--->26只鸡 消费了--->25只鸡 生产了27只鸡 生产了28只鸡 生产了29只鸡 消费了--->28只鸡 生产了30只鸡 消费了--->29只鸡 消费了--->30只鸡 消费了--->27只鸡 生产了31只鸡 生产了32只鸡 生产了33只鸡 生产了34只鸡 消费了--->31只鸡 生产了35只鸡 消费了--->34只鸡 消费了--->35只鸡 生产了36只鸡 生产了37只鸡 生产了38只鸡 生产了39只鸡 生产了40只鸡 消费了--->33只鸡 生产了41只鸡 消费了--->40只鸡 生产了42只鸡 消费了--->41只鸡 消费了--->42只鸡 消费了--->39只鸡 生产了43只鸡 生产了44只鸡 消费了--->38只鸡 生产了45只鸡 消费了--->44只鸡 生产了46只鸡 生产了47只鸡 生产了48只鸡 生产了49只鸡 生产了50只鸡 生产了51只鸡 消费了--->45只鸡 消费了--->51只鸡 生产了52只鸡 消费了--->50只鸡 生产了53只鸡 生产了54只鸡 消费了--->52只鸡 生产了55只鸡 生产了56只鸡 消费了--->54只鸡 消费了--->55只鸡 消费了--->53只鸡 消费了--->49只鸡 消费了--->56只鸡 消费了--->48只鸡 消费了--->47只鸡 生产了57只鸡 消费了--->46只鸡 生产了58只鸡 生产了59只鸡 生产了60只鸡 生产了61只鸡 生产了62只鸡 生产了63只鸡 生产了64只鸡 消费了--->57只鸡 消费了--->63只鸡 消费了--->62只鸡 消费了--->64只鸡 消费了--->61只鸡 消费了--->60只鸡 生产了65只鸡 消费了--->59只鸡 生产了66只鸡 消费了--->65只鸡 生产了67只鸡 消费了--->66只鸡 生产了68只鸡 消费了--->67只鸡 生产了69只鸡 消费了--->68只鸡 生产了70只鸡 消费了--->69只鸡 生产了71只鸡 消费了--->70只鸡 生产了72只鸡 消费了--->71只鸡 生产了73只鸡 消费了--->72只鸡 生产了74只鸡 消费了--->73只鸡 生产了75只鸡 消费了--->74只鸡 生产了76只鸡 消费了--->75只鸡 消费了--->76只鸡 生产了77只鸡 消费了--->58只鸡 消费了--->77只鸡 消费了--->43只鸡 生产了78只鸡 消费了--->37只鸡 生产了79只鸡 消费了--->78只鸡 消费了--->79只鸡 消费了--->36只鸡 消费了--->32只鸡 生产了80只鸡 生产了81只鸡 消费了--->80只鸡 生产了82只鸡 消费了--->81只鸡 消费了--->82只鸡 生产了83只鸡 生产了84只鸡 消费了--->83只鸡 生产了85只鸡 消费了--->84只鸡 生产了86只鸡 消费了--->85只鸡 生产了87只鸡 消费了--->86只鸡 生产了88只鸡 消费了--->87只鸡 生产了89只鸡 消费了--->88只鸡 生产了90只鸡 消费了--->89只鸡 生产了91只鸡 消费了--->90只鸡 生产了92只鸡 消费了--->91只鸡 生产了93只鸡 消费了--->92只鸡 生产了94只鸡 消费了--->93只鸡 生产了95只鸡 消费了--->94只鸡 生产了96只鸡 消费了--->95只鸡 生产了97只鸡 消费了--->96只鸡 生产了98只鸡 消费了--->97只鸡 生产了99只鸡 消费了--->98只鸡 消费了--->99只鸡
通信法
-
用一个标志变量flag 来标志哪个线程该wait 哪个该进行
package kuang03; public class TestPc2 { public static void main(String[] args) { // TODO Auto-generated method stub TV tv = new TV(); new Player(tv).start(); new Watcher(tv).start(); } } //演员与观众都需要一个tv //就像producer和consumer都需要一个container //生产者-->演员 class Player extends Thread{ TV tv; public Player(TV tv) { this.tv=tv; } @Override public void run() { for(int i=0;i<20;i++) { // System.out.println("") 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; //表演 //养成习惯 只要涉及到并发 就直接synchronized 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;//转换标志位 } }
演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活 演员表演了:王牌对王牌播放中 我观看了:王牌对王牌播放中 演员表演了:抖音,记录美好生活 我观看了:抖音,记录美好生活
线程池
-
背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大
-
思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用,类似生活中的交通工具
-
好处:
-
提高响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中的线程 不需要每次都创建)
-
便于线程创建(....)
-
corePoolSize:核心池的大小
-
maximumPoolSize:最大线程数
-
keepAliveTime:线程没有任务时最多保持多长时间后会终止
-
-
-
JDK 5.0起提供了线程池相关API ExecutorService和Executors
-
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
-
void execute(Runnable command):执行任务、命令,没有返回值,一般用来执行Runnable
-
<T>Future<T> submit(Callable<T> task):执行任务,有返回值,一般有用来执行Callable
-
void shutdown():关闭连接池
-
-
Executors:工具类,线程池的工厂类,用于创建并返回不同类型的线程池
package kuang03; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //测试线程池 public class TestPool { public static void main(String[] args) { // TODO Auto-generated method stub //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() { // TODO Auto-generated method stub System.out.println(Thread.currentThread().getName()); } }
总结
创建线程
package kuang03; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadReview { public static void main(String[] args) { // TODO Auto-generated method stub new MyThread1().start(); new Thread(new MyThread2()).start(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new MyThread3()); //放Runnable或者Thread都可以 // 泛型约束 泛型约束 new Thread(futureTask).start(); //获取返回值 try { Integer integer = futureTask.get(); System.out.println((int)integer); } catch (InterruptedException | ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class MyThread1 extends Thread{ @Override public void run() { System.out.println("MyThread1"); } } class MyThread2 implements Runnable{ @Override public void run() { // TODO Auto-generated method stub System.out.println("MyThread2"); } } //实现Callable接口 class MyThread3 implements Callable<Integer>{//Callable需要一个返回值 @Override public Integer call() throws Exception{ System.out.println("MyThread3");//方法体 return 100;//需要一个返回值 } }
CopyOnWriteList..Lock.....等
标签:---,Thread,更新,public,学习,线程,new,只鸡 来源: https://www.cnblogs.com/4-Prestar/p/14501805.html