1.程序、进程、线程
2. Java线程的创建(重点)
2.1 线程Thread类的使用
//继承Thread类,重写run方法,调用start开启线程
public class TestThread1 extends Thread {
@Override//重写父类的Thread的run方法
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();//创建一个线程对象
testThread1.start();//调用start方法,开启线程。与主线程同时执行,是交替执行
//testThread1.run();//run方法。没有启动多线程,run跟普通方法一样,先运行run在接下来运行
for (int i = 0; i < 20; i++) {
System.out.println("我在学习多线程main:"+i);
}
}
}
- 案例:Thread练习,实现多线程下载网图
//Thread练习,实现多线程下载网图
public class TestThread1_test extends Thread{
private String url;//网络图片地址
private String name;//保存的文件名
public TestThread1_test(String url , String name)//创建构造器,初始化数值
{
this.url = url;
this.name=name;
}
@Override//多线程重写run方法
public void run()
{
WebDoenloader webDoenloader = new WebDoenloader();
webDoenloader.downloader(url,name);
System.out.println("下载的文件名为:"+ name);
}
public static void main(String[] args)
{//main主线程
TestThread1_test t1 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "1.jpg");
TestThread1_test t2 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "2.jpg");
TestThread1_test t3 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "3.jpg");
t1.start();
t2.start();
t3.start();
}
}
//下载器,定义一个下载器的类。 一个文件中只能有一个public class的类
class WebDoenloader
{
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.2 线程 Runnable接口(重点)
2.2.1runnable接口的基本使用 + 下载图片案例
r u n n a b l e 接口的使用 \textcolor{red}{runnable接口的使用} runnable接口的使用
package com.Thread.demo01;
//Runnable接口实现线程,重写run方法,创建一个Thread线程对象,将Runnable接口的实现对象丢入Thread对象,调用Thread的start
//推荐使用Runnable接口,因为Thread就是对了Runnale接口的实现
public class TestThread2 implements Runnable{//接入Runnable接口
@Override//重写run方法
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run重写方法!"+i);
}
}
public static void main(String[] args) {
//创建一个TestThread2的runnable接口实例化对象
TestThread2 testThread2 = new TestThread2();
//创建一个Thread对象,通过hread对象对象来开启我们的线程,代理
Thread thread = new Thread(testThread2);//可以把runnable丢进Thread()内,也就是丢进构造器内
thread.start();//开启线程
//可以把上面两行代码简写为:new Thread(testThread2).start();//这种就是静态代理
for (int i = 0; i < 20; i++) {
System.out.println("main方法"+i);
}
}
}
- 案例: r u n n a b l e 接口实现图片下载 \textcolor{red}{案例:runnable接口实现图片下载} 案例:runnable接口实现图片下载
package com.Thread.demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//用runnable练习,实现多线程下载网图
public class TestThread2_test implements Runnable{
private String url;//网络图片地址
private String name;//保存的文件名
public TestThread2_test(String url , String name)//创建构造器
{
this.url = url;
this.name=name;
}
@Override//多线程重写run方法
public void run() {
WebDoenloader2 webDoenloader = new WebDoenloader2();
webDoenloader.downloader(url,name);
System.out.println("下载的文件名为:"+ name);
}
public static void main(String[] args) {//main主线程
TestThread1_test t1 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "1.jpg");
TestThread1_test t2 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "2.jpg");
TestThread1_test t3 = new TestThread1_test("https://www.kuangstudy.com/assert/course/c1/13.jpg" , "3.jpg");
//runnable的启动
new Thread(t1).start();
new Thread(t2).start();
new Thread(t3).start();
}
}
//下载器,定义一个下载器的类。 一个文件中只能有一个public class的类
class WebDoenloader2
{
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.2.2 Runnable小节 + Runnable同一个对象被多个线程使用(重要)
- 实例:买火车票的例子,多个线程同时操作同一个对象 \textcolor{red}{实例:买火车票的例子,多个线程同时操作同一个对象} 实例:买火车票的例子,多个线程同时操作同一个对象
//多个线程同时操作同一个对象
//买火车票的例子
//延时可以不加。这边加了延时发现问题,多个线程操控同一资源条件下,线程不安全,数据紊乱
public class TestThread_test2 implements Runnable{
private int ticketNum=10;//火车的票数
@Override//重写线程
public void run() {
while (true)
{
if(ticketNum<=0)
{
break;
}
/*
try {
Thread.sleep(200);//线程的延时 200毫秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
*/
System.out.println(Thread.currentThread().getName()+"-->拿到了第:"+ ticketNum +" 张票");
//Thread.currentThread().getName()获得当前执行线程的名字,下面有给起名
ticketNum=ticketNum-1;
}
}
public static void main(String[] args) {
TestThread_test2 t = new TestThread_test2();//只需要new一个。对象创建一个TestThread2的runnable接口实例化对象
new Thread(t,"小明").start();//Thread线程可以起名字,就是线程的名字
new Thread(t,"小王").start();
new Thread(t,"小红").start();
}
}
实例:龟兔赛跑 \textcolor{red}{实例:龟兔赛跑} 实例:龟兔赛跑
//runnable案例龟兔赛跑
//注意:当其中一方到达20时候,会把名字赋给winner, winner是一个类变量。两者都受到同一个winner的值影响
public class TestThread_test3 implements Runnable{
private static String winner;
@Override
public void run() {
for (int i = 1; i <= 20; i++) {
//模拟兔子睡觉
if (Thread.currentThread().getName().equals("兔子")) ;
{
try {
Thread.sleep(15);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果比赛结束了 就停止程序
boolean flag = gameover(i);
if(flag){
System.out.println(flag+Thread.currentThread().getName()+"break");
break;
}
System.out.println(Thread.currentThread().getName() + " 跑了"+ i +"米");
}
}
//判断比赛是否结束
public boolean gameover(int steps)
{
if (winner != null)//存在是否有胜利者
{
return true;
}
if (steps==20)
{
winner = Thread.currentThread().getName();
System.out.println("winner is " +winner+ steps);
return true;
}
return false;
}
public static void main(String[] args) {
TestThread_test3 t = new TestThread_test3();
new Thread(t,"乌龟").start();
new Thread(t,"兔子").start();
}
}
2.3 Callable接口(了解即可)
- C a l l a b l e 接口用于实现图片的下载 \textcolor{red}{Callable接口用于实现图片的下载} Callable接口用于实现图片的下载
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//实现callable接口
public class TestThread3 implements Callable<Boolean> {//1.实现callable接口。Callable接口需要一个返回值
private String url;
private String name;
public TestThread3(String url, String name)//定义构造器,初始化值
{
this.name = name;
this.url = url;
}
@Override//2.重写call方法
public Boolean call(){
WebDownload webDoenloader = new WebDownload();
webDoenloader.download(url,name);
System.out.println("下载的文件名为:"+ name);
return true;
}
//main主线程运行
public static void main(String[] args) throws ExecutionException, InterruptedException {
TestThread3 t1 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
"1.jpg");//太长了。 3. 创建目标对象
TestThread3 t2 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
"2.jpg");//太长了。 3. 创建目标对象
TestThread3 t3 = new TestThread3("https://www.kuangstudy.com/assert/course/c1/13.jpg" ,
"3.jpg");//太长了。 3. 创建目标对象
ExecutorService ser = Executors.newFixedThreadPool(3);//4.创建执行服务,有几个线程丢几个
Future<Boolean> result1 = ser.submit(t1);//5.提交执行(有几个线程提交几个)
Future<Boolean> result2 = ser.submit(t2);//提交执行
Future<Boolean> result3 = ser.submit(t3);//提交执行
boolean r1 = result1.get();//获取结果,直接alt+回车抛出异常
boolean r2 = result2.get();
boolean r3 = result3.get();
System.out.println(r1);
System.out.println(r2);
System.out.println(r3);
ser.shutdownNow();//6.关闭服务
}
}
//下载器
class WebDownload{
public void download(String url, String name)
{
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (IOException e) {
e.printStackTrace();//打印错误的方法
System.out.println("Io异常");
}
}
}
3. Lamda表达式
3.1 Lamda表达式的基本知识
-
Lamda表达式实质:属于函数式编程
-
Lamda表达式意义:1.避免匿名内部类定义过多
2.可以使代码变得更加的简洁
3.去掉了一些没有意义的代码,只留下了核心的逻辑
例子 I_like like6 = ()-> {
System.out.println("匿名内部类I like lambda 6");
};
3.2 Lanbda的基本使用(从静态内部类,局部内部类,匿名内部类中一步一步简化到Lanbda中使用)
- Lamda表达式学习的关键: F u n c t i o n a l I n t e r f a c e ————函数式接口 \textcolor{red}{Functional Interface————函数式接口} FunctionalInterface————函数式接口
- 函数式接口定义: 一个接口中只有一个抽象方法,就是一个函数式接口 \textcolor{red}{一个接口中只有一个抽象方法,就是一个函数式接口} 一个接口中只有一个抽象方法,就是一个函数式接口
对于函数式接口,我们可以通过lambda表达式来创建该接口的对象
package com.Thread.Lambda;
//一步一步简化从2.放在外部之中,到3.静态内部类中,到4.创建一个局部内部类中, 到5.匿名内部类 最后到Lanbda表达式
//lambda表达式只用函数式接口,一个接口中只有一个抽象方法,就是一个函数式接口
//我们只用一次,可以就放在同一页中,不用分开再重新定义。
//1.定义一个函数式接口
interface I_like{//在同一页中不用加public
void lamda();
}
//2.实现类,来实现接口
class Like2 implements I_like{//在同一页中不用加public
@Override
public void lamda() {
System.out.println("外部类I like lambda2 ");
}
}
public class TestLambda1 {
//3.静态内部法
//使用静态内部法,将实现类放入静态内部类中。静态内部类需要在类前面加static
static class Like3 implements I_like{//在同一页中不用加public
@Override
public void lamda() {
System.out.println("静态内部类 I like lambda 3");
}
}
public static void main(String[] args) {
//4.局部内部类
class Like4 implements I_like{//局部内部类。将接口的实现类放在psvm之中,或者放在main方法中。
@Override
public void lamda() {
System.out.println("局部内部类I like lambda 4");
}
}
I_like like2 = new Like2();//创建一个接口对象.接口的引用指向实现类的引用。实现类在外部
like2.lamda();
I_like like3 = new Like3();//静态内部类创建对象。一个接口对象,但是这个接口实现类在内部
like3.lamda();
I_like like4 = new Like4();//局部内部类。
like4.lamda();
//5.匿名内部类,没有类的名称,必须借助接口或者父类
I_like like5 = new I_like(){//直接展开重写的方法
@Override
public void lamda() {
System.out.println("匿名内部类I like lambda 5");
}
};
like5.lamda();
//6.用lanbdas表达式简化,在匿名局部变量上进一步简化。接口的引用指向实现类的引用
//lambda表达式只用函数式接口,一个接口中只有一个抽象方法,就是一个函数式接口
I_like like6 = ()-> {
//去掉了:“直接展开重写的方法 new I_like() { @Override public void lambda”
//也就是去掉到方法括号之前
System.out.println("匿名内部类I like lambda 6");
};
like6.lamda();
}
}
//这上面只适合写一行重写方法,想要多可以这样写(Thread也是重写Runnable接口的) //5.6线程监控状态的补充
Thread thread = new Thread(()->{//lambda方法,不用另外定义run重写方法
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(1_000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
Lamvda表达式的进一步简化(这个例子是带有参数的)
package com.Thread.Lambda;
//Lambda的另外一个例子,带有参数的
interface Ilove{//1.定义接口
void love(int a);
}
class Love2 implements Ilove{//2.外部实现类来实现接口
@Override
public void love(int a) {
System.out.println(a + "--->I love you外部类");
}
}
public class TestLambda2 {
static class Love3 implements Ilove{//3.静态局部类来实现接口
@Override
public void love(int a) {
System.out.println(a + "--->I love you 静态局部类");
}
}
public static void main(String[] args) {
class Love4 implements Ilove{//4.局部内部类
@Override
public void love(int a) {
System.out.println(a + "--->I love you 局部内部类");
}
}
Ilove love2 = new Love2();//外部类
love2.love(2);
Ilove love3 = new Love3();//静态局部类
love3.love(3);
Ilove love4 = new Love4();//静态局部类
love4.love(4);
Ilove love5 = new Ilove(){//5.匿名局部类。 必须借助接口或者父类
@Override
public void love(int a) {
System.out.println(a + "--->I love you 匿名局部类");
}
};
love5.love(5);
Ilove love6 = (int a) -> {//6.lambda表达式。 必须借助接口或者父类
System.out.println(a + "--->I love you lambda表达式");
};
love6.love(6);
Ilove love7 =(a)-> {//7.lambda表达式,进一步简化,简化参数类型
System.out.println(a + "--->I love you lambda表达式进一步简化参数类型");};
love7.love(7);
Ilove love8 =a-> {//8.lambda表达式,进一步简化,简化参数类型,简化括号
System.out.println(a + "--->I love you lambda表达式进一步简化参数类型,简化括号");};
love7.love(8);
//9.lambda表达式,进一步简化,简化参数类型,简化括号,简化花括号。
//只能代码为一行时候,才能简化花括号
Ilove love9 =a-> System.out.println(a + "--->I love you lambda表达式进一步简化参数类型,简化括号,简化花括号");
love7.love(9);
}
}
/*
总结:
1.lambda表达式,只有一行代码才能简化成一行,如果有多行,就必须用花括号(代码块)。
2。lambda表达式必须是函数式接口
3.多个参数要去掉的话必须全部去掉定义类型。比如(int a,int b)变成a,b。而不能(a,int b)。建议多个参数时候加括号
*/
4.静态代理模式(在 2.2 Runnable中提到过代理)
/*
静态代理模式的总结:
1.真实对象和代理对象,都要实现同一个接口
2.代理对象要代理真实角色。代理对象要有真实对象的参数
比如下文:private Marry target; 使用构造器传入真实角色,然后使用真实角色的重写部分
好处:1.代理对象可以做很多真实对象做不了的事情
2.真实对象专注做自己的事情
*/
interface Marry{//定义一个接口
void happyMarry();
}
//我是一个真实角色
class You implements Marry{
@Override
public void happyMarry() {
System.out.println("lu结婚了,超开心");
}
}
//婚庆公司代理角色,帮助代理
class WweddingCompany implements Marry{
private Marry target;//目标结婚对象
public WweddingCompany(Marry target) {//构造器
this.target = target;//使用这个类的 Marry 类型的target,target这边传的是You,
}
@Override
public void happyMarry() {
before();
this.target.happyMarry();//执行You的重写部分
after();
}
private void after() {//结婚之前
System.out.println("结婚之前");
}
private void before() {//结婚之后
System.out.println("结婚之后");
}
}
public class demo01 {
public static void main(String[] args) {
You you = new You();//创建真实对象的变量
WweddingCompany wweddingCompany = new WweddingCompany(you);//真实对象传入代理对象的构造器,进行初始化
wweddingCompany.happyMarry();
//上面三行可以简写为:new WweddingCompany(new You()).happyMarry();
/*
new WweddingCompany(new You()).happyMarry();
WweddingCompany是一个代理,代理new You(),调用WweddingCompany里面的happyMarry()方法
new Thread(()-> System.out.println("我爱你")).start();
Thread是一个代理,代理()-> System.out.println("我爱你"),调用Thread里面的start()方法
()-> System.out.println("我爱你")用了Lanbda表达式,它可以表现为实现Runnable接口的实现,Thread也是Runnable接口的实现。
所以Thread才可以代理。
*/
}
}
5. 线程状态
5.1 线程的运动状态与运行
- 五大状态:创建、就绪、运行、阻塞、死亡
5.2 停止线程方法(使用标志位)
public class Stop implements Runnable{//Stop应用到Runnable接口之中
//1.设置一个标识位
private boolean flag = true;
//2.设置一个方法来停止线程
public void stop()
{
this.flag = false;
}
@Override
public void run() {//重写接口的方法
int i = 1;
System.out.println("线程被启动了");
//3.线程体使用该标志
while(flag)
{
System.out.println("run thread "+ (i++));
}
}
public static void main(String[] args) {
Stop s = new Stop();
new Thread(s).start();//启动线程了
for (int i = 0; i < 100; i++) {
System.out.println("main跑了:" + i);
if (i==50)
{
s.stop();
System.out.println("线程终止");
}
}
}
}
5.3 线程的休眠方法(Sleep)
- S l e e p 案例:模拟倒计时 \textcolor{red}{Sleep案例:模拟倒计时} Sleep案例:模拟倒计时
import java.text.SimpleDateFormat;
import java.util.Date;
//模拟倒计时
public class Sleep2 {
//模拟倒计时
public void tenDown() throws InterruptedException {
int num=10;
while (true)
{
Thread.sleep(1000);//每1秒钟倒计时一次
System.out.println(num--);
if (num<=0)
{
break;
}
}
}
public static void main(String[] args) {
Sleep2 sleep2 = new Sleep2();
try {
sleep2.tenDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
S l e e p 案例:每隔一秒打印当前时间 \textcolor{red}{Sleep案例:每隔一秒打印当前时间} Sleep案例:每隔一秒打印当前时间
import java.text.SimpleDateFormat;
import java.util.Date;
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();
}
}
}
S l e e p 案例:模拟网络延时 ( 这个火车票案例,与 2.2 的 R u n n a b l e 一样,只是加了 s l e e p 休眠线程 ) \textcolor{red}{Sleep案例:模拟网络延时(这个火车票案例,与2.2的Runnable一样,只是加了sleep休眠线程)} Sleep案例:模拟网络延时(这个火车票案例,与2.2的Runnable一样,只是加了sleep休眠线程)
//模拟网络延时:放大系统的发生性。
public class Sleep implements Runnable{
private int ticketNum=10;//火车的票数
@Override//重写线程
public void run() {
while (true)
{
if(ticketNum<=0)
{
break;
}
try {//
Thread.sleep(200);//线程的延时 200毫秒
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName()+"-->拿到了第:"+ ticketNum +" 张票");
//Thread.currentThread().getName()获得当前执行线程的名字,下面有给起名
ticketNum=ticketNum-1;
}
}
public static void main(String[] args) {
Sleep t = new Sleep();//只需要new一个。对象创建一个TestThread2的runnable接口实例化对象
//多个线程操作一个对象,会不安全
new Thread(t,"小明").start();//Thread线程可以起名字,就是线程的名字
new Thread(t,"小王").start();
new Thread(t,"小红").start();
}
}
5.4 线程的礼让(yield)
- 礼让:申请CPU重新调度
- 线程礼让案例
//创建一个其它的类来实现Runnable接口
//线程的礼让.礼让不一定成功
public class Yield_test{
public static void main(String[] args) {
MyYield yieldTest = new MyYield();
new Thread(yieldTest,"a").start();
new Thread(yieldTest,"b").start();
}
}
class MyYield implements Runnable{//定义一个类去继承Runnable
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
- 上面那个案例另一种写法
//自身类实现Runnable接口
//线程的礼让.礼让不一定成功
public class Yield_test implements Runnable{//使public class类去继承Runnable
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
public static void main(String[] args) {
Yield_test yieldTest = new Yield_test();
new Thread(yieldTest,"a").start();
new Thread(yieldTest,"b").start();
}
}
5.5线程的强制执行方法(Join,插队)
//线程的强制执行 Join
//一开始run与main主线程是并发执行的 ,但是Join强制让main进入阻塞,优先运行run,等run运行完之后才继续运行main
public class Join implements Runnable{
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
Join join = new Join();
Thread thread = new Thread(join);
thread.start();
//主线程运动
for (int i = 0; i < 100; i++) {
System.out.println("main:"+i);
if (i==20)
{
System.out.println("threat join");
thread.join();//强制线程thread插队,thread先执行,要等thread执行完,才能走其它的线程。
}
}
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("run :"+ i);
}
}
}
5.6 线程的状态监测
Thread thread = new Thread();//创建一个新线程的对象
Thread.State state = thread.getState();//获取线程的状态
public class TestState {
public static void main(String[] args) throws InterruptedException {
//1.new 状态
Thread thread = new Thread(()->{//lambda方法,不用另外定义run重写方法
for (int i = 0; i < 4; i++) {
try {
Thread.sleep(1_000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
System.out.println("//");
});
//观察状态
Thread.State state = thread.getState();//new
System.out.println(state);
//2.观察启动状态
thread.start();//启动线程
state = thread.getState();
System.out.println(state);//RUN
while (state != Thread.State.TERMINATED)//直线线程不终止,就一直输出线程的状态
{
Thread.sleep(50);
state = thread.getState();
System.out.println(state);
}
//3.观察终止状态
state = thread.getState();
System.out.println(state);
}
}
5.7线程的优先级
package com.Thread.state;
//线程的优先级 有1-10 ,不设定是默认5
//Thread.MAX_PRIORITY最高级10 Thread.MIN_PRIORITY 1 Thread.NORM_PRIORITY 5
//不定义线程的名字的话,Thread.currentThread().getName()获取线程名默认是从Thread 0开始
public class Priority {
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 0开始
Thread t1 = new Thread(myPriority,"t1");
Thread t2 = new Thread(myPriority,"t2");
Thread t3 = new Thread(myPriority,"t3");
Thread t4 = new Thread(myPriority,"t4");
Thread t5 = new Thread(myPriority,"t5");
Thread t6 = new Thread(myPriority,"t6");
Thread t7 = new Thread(myPriority,"t7");
Thread t8 = new Thread(myPriority,"t8");
Thread t9 = new Thread(myPriority,"t9");
Thread t10 = new Thread(myPriority,"t10");
//先设置线程的优先级,再启动
t1.setPriority(1);
t1.start();
t2.setPriority(2);
t2.start();
t3.setPriority(3);
t3.start();
t4.setPriority(4);
t4.start();
t5.setPriority(5);
t5.start();
t6.setPriority(6);
t6.start();
t7.setPriority(7);
t7.start();
t8.setPriority(8);
t8.start();
t9.setPriority(9);
t9.start();
t10.setPriority(Thread.MAX_PRIORITY);
t10.start();
}
}
class MyPriority implements Runnable{
@Override
public void run() {
//Thread.currentThread().getPriority()获取当前线程的优先级
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority());
}
}
5.8守护(daemon)线程
//测试守护线程
//上帝守护这你
public class Deamon {
public static void main(String[] args) {
God god = new God();//创建God对象
You you = new You();//创建you对象
Thread thread = new Thread(god);
//设定保护线程。默认是false,表示用户线程,正常的线程都是用户线程。要自己设定true
//自己设定true就是守护线程
thread.setDaemon(true);
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 < 30; i++) {//人一生36500天
System.out.println("你一生都开心的活着");
}
System.out.println("Goodbye world");//过完一生
}
}
6.线程同步 synchronized(重点)
6.1并发 和 线程同步基本概念
-
并发:同一个对象被多个线程操控 , 会造成不安全的现象 \textcolor{red}{并发:同一个对象被多个线程操控,会造成不安全的现象} 并发:同一个对象被多个线程操控,会造成不安全的现象。
-
线程的同步就是用来解决并发问题 \textcolor{red}{线程的同步就是用来解决并发问题} 线程的同步就是用来解决并发问题
//并发例子
TestSleep ticket = new TestSleep();
//ticket对象被t1,t2,t3多个线程控制。
Thread t1 = new Thread(ticket,"a");
Thread t2 = new Thread(ticket,"b");
Thread t3 = new Thread(ticket,"c");
t1.start();
t2.start();
t3.start();
//或者另一种写法
/*
new Thread(ticket,"a").start();
new Thread(ticket,"b").start();
new Thread(ticket,"c").start();
*/
- 线程同步的基本概念 \textcolor{red}{线程同步的基本概念} 线程同步的基本概念
6.2 队列 与 锁(线程同步的形成条件)
线程同步形成条件 : 队列 + 锁。让线程排成队列,当一个线程访问的时候,用锁防止其它线程访问,使线程按照队列按顺序访对象。
锁:每一个对象都有属于自己的一把锁。
6.3线程同步的三大不安全案例
- 买票的不安全案例
//不安全的买票
//同一对象可以理解为票总数,当多个线程都去共用一个对象时,就是去买票,票数会减少,可能会出现拿到同一张票,抢票的现象。
//线程不安全,可能会出现买了负数的票。当都剩下最后一张的时候,他们都以为买了最后一张,但是只有一个人买到,其他人是0,或者-1
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"a").start();
new Thread(buyTicket,"b").start();
new Thread(buyTicket,"c").start();
}
}
class BuyTicket implements Runnable{
int ticketnum = 10;//票
boolean flag = true;//外部停止方式
@Override
public void run() {
//买票
while (flag)
{
try {
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
private void buy() throws InterruptedException {
//判断是否有票
if (ticketnum<=0) {
flag = false;
return;
}
Thread.sleep(100);
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketnum--);
}
}
- 银行的不安全取钱
//不安全的取钱
//两个人去银行
//看他们同时访问了哪一个对象,这个对象的里面的值可能会出现不安全的现象
public class UnsafeBank {
public static void main(String[] args) {
Acount acount = new Acount(100,"结婚基金");
Drawing man = new Drawing(acount,50,"男孩");
Drawing girl = new Drawing(acount,100,"女孩");
man.start();
girl.start();
}
}
//账户
class Acount{
int money;//余额
String name;
public Acount(int money,String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Acount acount;//账户
//取了多少钱
int drawMoney;
//现在手里多少钱
int nowmoney;
public Drawing(Acount acount,int drawMoney,String name){
super(name);
this.drawMoney=drawMoney;
this.acount=acount;
}
//重写run方法来表示取钱操作
@Override
public void run() {
if(acount.money-drawMoney<0)//判断账户里的钱是否够
{
System.out.println(Thread.currentThread().getName()+"钱不够了取不了");
return;
}
//卡内余额 = 余额 - 你取的钱
acount.money=acount.money-drawMoney;
//sleep放大安全问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
nowmoney =nowmoney +drawMoney;
//你手里的钱
System.out.println("账户余额:"+acount.name+" = "+acount.money);
//Thread.currentThread().getName() = getName() Drawing继承了Thread,可以用它的方法
//Thread.currentThread()返回的还是Thread ,所以this=Thread.currentThread().
System.out.println(getName()+"手里的钱 "+nowmoney);
}
}
- 集合list的不安全案例改写(没学过,后面从书上补)
//还没学过,到时候看书补一下
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();//传入一个泛型
for (int i = 0; i < 100; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
System.out.println(list.size());
}
}
6.4 synchronized用法 保证线程的同步
-
synchronized是一把锁
-
synchronized方法有两种用法:synchronized方法 与 synchronized块
6.4.1 synchronized同步方法
买票的不安全案例改写
//不安全的买票用synchronized方法进行同步
//同一对象可以理解为票总数,当多个线程都去共用一个对象时,就是去买票,票数会减少,可能会出现拿到同一张票,抢票的现象。
//线程不安全,可能会出现买了负数的票。当都剩下最后一张的时候,他们都以为买了最后一张,但是只有一个人买到,其他人是0,或者-1
public class UnsafeBuyTicket {
public static void main(String[] args) {
BuyTicket buyTicket = new BuyTicket();
new Thread(buyTicket,"a").start();
new Thread(buyTicket,"b").start();
new Thread(buyTicket,"c").start();
}
}
class BuyTicket implements Runnable{
int ticketnum = 10;//票
boolean flag = true;//外部停止方式
@Override
public void run() {
//买票
while (flag)
{
try {
Thread.sleep(100);
buy();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
//============================================================
//synchronized同步方法,锁的是this对象,对象本身
private synchronized void buy() throws InterruptedException {
//判断是否有票
if (ticketnum<=0) {
flag = false;
return;
}
//买票
System.out.println(Thread.currentThread().getName()+"拿到"+ticketnum--);
}
}
6.4.2 synchronized同步块
银行的不安全取钱
package com.Thread.syn;
//不安全的取钱,使用synchronized同步块来进行锁处理
//两个人去银行
//看他们同时访问了哪一个对象,这个对象的里面的值可能会出现不安全的现象
public class UnsafeBank {
public static void main(String[] args) {
Acount acount = new Acount(1000,"结婚基金");
Drawing man = new Drawing(acount,50,"男孩");
Drawing girl = new Drawing(acount,100,"女孩");
man.start();
girl.start();
}
}
//账户
class Acount{
int money;//余额
String name;
public Acount(int money,String name) {
this.money = money;
this.name = name;
}
}
//银行:模拟取款
class Drawing extends Thread{
Acount acount;//账户
//取了多少钱
int drawMoney;
//现在手里多少钱
int nowmoney;
public Drawing(Acount acount,int drawMoney,String name){
super(name);
this.drawMoney=drawMoney;
this.acount=acount;
}
//重写run方法来表示取钱操作
//synchronized(obj) 用synchronized方法块来锁定方法内的代码,obj是同步监视器,用共享资源作为同步监视器
//锁的对象就是变化的量,需要增删改查的对象
@Override
public void run() {
synchronized (acount)//锁的对象就是变化的量,需要增删改查的对象
{
if(acount.money-drawMoney<0)//判断账户里的钱是否够
{
System.out.println(Thread.currentThread().getName()+"钱不够了取不了");
return;
}
//卡内余额 = 余额 - 你取的钱
acount.money=acount.money-drawMoney;
//sleep放大安全问题的发生性
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
nowmoney =nowmoney +drawMoney;
//你手里的钱
System.out.println("账户余额:"+acount.name+" = "+acount.money);
//Thread.currentThread().getName() = getName() Drawing继承了Thread,可以用它的方法
//Thread.currentThread()返回的还是Thread ,所以this=Thread.currentThread().
System.out.println(getName()+"手里的钱 "+nowmoney);
}
}
}
集合list的不安全案例改写(没学过,后面从书上补)
//还没学过,到时候看书补一下
import java.util.ArrayList;
import java.util.List;
public class UnsafeList {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();//传入一个泛型
for (int i = 0; i < 10000; i++) {
new Thread(()->{
synchronized(list) {//synchronized块,监视的对象时list
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}
6.5 死锁
死锁:多个线程各自占用一些共享资源,并且互相等待其它线程占有的资源,而导致多个线程都在等待对方释放资源,都停止执行的情况。或者某一个同步块拥有"两个以上的对象的锁"
package com.Thread;
//死锁:多个线程互相抱着对方需要的资源,然后形成僵持
public class Deadlock {
public static void main(String[] args) {
Makeup g1 = new Makeup(0, "红");
Makeup g2 = new Makeup(1, "绿");
g1.start();
g2.start();
}
}
//口红
class Liostick{}
//镜子
class Mirror{}
//化妆
class Makeup extends Thread{
int choice;
String girlName;
Makeup(int choice,String girlName)//构造器初始化值
{
this.choice = choice;
this.girlName = girlName;
}
//需要的口红,镜子的资源只有一份,用static只有一份
static Liostick liostick = new Liostick();
static Mirror mirror = new Mirror();
//化妆,互相持有对方的锁,需要对方的资源
private void makeup() throws InterruptedException {
if (choice==0){
synchronized (liostick)//获得口红的锁
{
System.out.println(this.girlName+"获得口红的锁");
Thread.sleep(1000);
//拿到了口红,还想继续去拿镜子,两个线程僵持着产生死锁
// synchronized (mirror){//一秒钟后获得镜子的锁
// System.out.println(this.girlName+"获得镜子的锁");
}
//死锁解决。先去拿了口红,拿完口红就释放掉口红(就是口红放回去),然后再去拿镜子,拿完在释放掉。
synchronized (mirror){
System.out.println(this.girlName+"获得镜子的锁");
}
}
else {
synchronized (mirror)//获得口红的锁
{
System.out.println(this.girlName+"获得镜子的锁");
Thread.sleep(2000);
//拿到了镜子,还想继续去拿口红,两个线程僵持着产生死锁
// synchronized (liostick){//一秒钟后获得镜子的锁
// System.out.println(this.girlName+"获得口红的锁");
}
//死锁解决。先去拿了镜子,拿完镜子就释放掉镜子(就是镜子放回去),然后再去拿口红,拿完口红在释放掉。
synchronized (liostick){
System.out.println(this.girlName+"获得口红的锁");
}
}
}
@Override
public void run() {
try {
makeup();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
6.6 Lock锁(和synchronized一样也可以进行线程同步)
package com.Thread;
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 =100;
//定义lock锁private final私人常量,更加安全
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true)
{
//加锁要写在try外面,不安全代码块卸载try里面。解锁放在finally里面
lock.lock();//加锁
try {
if (ticketnum>0)
{
// try {
// Thread.sleep(500);
// } catch (InterruptedException e) {
// throw new RuntimeException(e);
// }
System.out.println(Thread.currentThread().getName()+ " "+ticketnum--);
}else {
break;
}
} finally {
lock.unlock();
}
}
}
}
7.线程协作通信(生产者与消费者)
7.1线程通信的分析
7.2线程通信的方法
7.3线程通信的二种解决方式(管程法、交通灯法)
7.3.1 管程法
//测试:生产者与消费者模型-->利用缓冲区解决;管程法
//需要四个对象:生产者,消费者,产品,缓冲区
//缓冲区
class SynContainer{
//需要一个容器大小,十只鸡
Chicken[] chickens = new Chicken[10];
//容器计数器
int count =0;
//生产者生产产品。生产与消费线程控制产品数量,需要用到同步synchronized
public synchronized void push(Chicken chicken){
//如果容器满了,就等待消费者消费
while(count==chickens.length){//唤醒不能用if,因为唤醒后不会执行if的语句,而能执行while语句
//生产者等待
try {
this.wait();//如果已经满了,生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有满就丢入产品
chickens[count] = chicken;
count++;
//可以通知消费者消费 this.notifyAll();唤醒这个对象上所以等待的线程
this.notifyAll();
}
//消费者消费产品。生产与消费线程控制产品数量,需要用到同步synchronized
public synchronized Chicken pop(){
//判断能够消费
while (count==0)
{
//等待生产者生产,消费者等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果可以消费就消费
count--;
Chicken chicken = chickens[count];//消费的那只鸡
//通知生产者生产
this.notifyAll();
return chicken;
}
}
//生产者
class Productor extends Thread{
//生产者需要容器
SynContainer container;
public Productor(SynContainer container)//构造器
{
this.container=container;
}
//生产
@Override
public void run() {
for (int i = 0; i < 20; 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 < 20; i++) {
System.out.println("消费了第几只鸡:"+ container.pop().id+"只鸡");
}
}
}
//产品:鸡
class Chicken{
int id;//产品编号
public Chicken(int id) {
this.id = id;
}
}
public class TestPC {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Productor(synContainer).start();
new Consumer(synContainer).start();
}
}
7.3.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 < 10; 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{
String voice;//表演节目
boolean flag = true;//为真演员录播,为假 演员等待
//演员录播,观众等待 T
//观众观看,演员等待 F
//演员表演
public synchronized void play(String voice)
{
if (flag==false)//观众观看,演员等待
{
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演员表演了:"+ voice);//演员表演了voice
this.voice=voice;
//通知观众观看
this.flag=!this.flag;
this.notifyAll();//唤醒观众
}
//观众观看
public synchronized void watch()
{
if (flag==true)//flag==true 演员表演,观众等待
{
try {
this.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
System.out.println("观众观看了:" +voice);
this.flag=!this.flag;
this.notifyAll();
}
}
8.线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//线程池
public class TestPool {
public static void main(String[] args) {
//1.创建服务,创建线程池,里面可以放十个线程
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{
@Override
public void run() {
for (int i = 0; i < 4; i++) {
System.out.println(Thread.currentThread().getName()+"= "+i);
}
}
}