Java.Thread
1.多线程 流程
线程实现重点 状态 同步重点 通信
2.概念
程序是指令和数据的有序集合,是一个静态的概念
进程 是一个程序的执行过程,是一个动态的概念
线程:一个程序运行后至少有一个进程,一个进程里面包含了多个线程
模拟多线程:cpu只能执行一个代码,同一个cpu在不同的线程中快速切换,就有执行的错觉
真实多线程:多个cpu,或多核,如服务器 芯片
多个线程并发执行的时候,我们要让线程有规律的执行来共同完成一件任务,比如A线程的执行动作是做包子,B线程执行的任务是吃包子。
并发和并行:两个和多个事件在同一个时间段发生
并行:特指两个或多个事件在同一时间(同时)发生。
3.多线程核心概念:
4.线程创建;
4.1 继承 Thread类 重点
4.2 实现Runnable接口 重点
4.3 Callable接口 理解
4.1 Thread使用 Thread 是Runable接口的实现类
创建线程方式,继承thread类 ,重写run方法 ,调用start开启线程。
格式1: 可以在任意方法的方法体内部Thread t=new Thread()
{
重写run()方法
}
t.start();
格式:
自定义多线程类
重写run()方法快捷键Ctrl+O,编写程序执行体
创建线程对象,调用start()方法启动线程
public class Threadintro extends Thread{
// 创建线程方式,继承thread类 ,重写run方法 ,调用start开启线程
//test1
@Override
public void run() {
for (int i = 0; i < 200; i++) {
System.out.println("我在看代码"+i);
}
}
public static void main(String[] args) {
//main主线程
//创建一个线程对象
Threadintro t=new Threadintro();
//调用start开启线程
// t.start();
/*
* 我在学习多线程191
我在学习多线程192
我在看代码143
我在学习多线程193
我在看代码144
我在学习多线程194
我在学习多线程195
* * 两条线程交替执行*/
t.run(); //先执行重写的方法 再执行main里面的线程
//test2
for (int i = 0; i < 200; i++) {
System.out.println("我在学习多线程"+i);
}
/*总结 线程开启,不一定立即执行,由CPU调度*/
}
}
总结 :线程开启,不一定立即执行,由CPU调度。
举例子:网图下载
package demo01;
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 Thread1 extends Thread{
private String URL; //网络图片地址
private String name; //保存文件名
//构造方法
public Thread1(String name, String URL) {
this.URL = URL;
this.name = name;
}
//下载图片线程执行体
@Override
public void run() {
WebDownloader w=new WebDownloader();
w.download(URL,name);
System.out.println("下载的文件名为:"+name);
}
//主方法
public static void main(String[] args) {
Thread1 t1=new Thread1("1.jpg","https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3100081661,125115249&fm=11&gp=0.jpg");
Thread1 t2=new Thread1("2.jpg","http://images.china.cn/attachement/jpg/site1000/20090515/0019b91ebfca0b770a9113.jpg");
//多线程启动
t1.start();
t2.start();
}
}
//下载器
class WebDownloader{
//下载方法
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异常,download方法出现问题");
}
}
4.2Runable接口的使用
创建线程方式2 实现Runable(),执行线程需要丢入Runnable接口的实现类
格式:定义Runable接口的实现类
实现run()方法,编写线程执行体 就是在run里面写一些要用到的方法
创建线程对象Thread,并放入Runable的实现类对象作为参数,然后调用start方法启动线程
package demo01;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
//练习 Runnable 实现多线程同步下载图片 //继承Runnable接口
public class runable implements Runnable{
private String URL; //网络图片地址
private String name; //保存文件名
//构造方法
public runable(String name, String URL) {
this.URL = URL;
this.name = name;
}
//下载图片线程执行体
@Override
public void run() {
WebDownloader w=new WebDownloader();
w.download(URL,name);
System.out.println("下载的文件名为:"+name);
}
//主方法
public static void main(String[] args) {
runable t1=new runable("1.jpg","https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3100081661,125115249&fm=11&gp=0.jpg");
runable t2=new runable("2.jpg","http://images.china.cn/attachement/jpg/site1000/20090515/0019b91ebfca0b770a9113.jpg");
//多线程启动
new Thread(t1).start();
new Thread(t2).start();
}
}
//下载器
class WebDownloader{
//下载方法
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异常,download方法出现问题");
}
}
}
总结; thread和runable区别
4.3并发问题
并发问题:
发现:对多个线程操作同个资源的情况下,线程不安全,数据紊乱。
三个人同时买火车票例子
用到方法:1. Thread.sleep(xx毫秒) 需要 try catch方法
2. Thread.currentThread().getName()
百度的: Thread.currentThread() 是指获取当前运行的线程对象 例如: Thread.currentThread().getName() 就是当前线程的名字
3.(Thread.currentThread().getName().equals(“兔子”)线程的名字,可以对他进行操作。
ackage demo2;
/*多线线程同时操作同一个对象*/
//买火车票例子
public class test implements Runnable{
//票数
private int ticketNums=10;
@Override
public void run() {
while(true)
{
if(ticketNums<=0)
{
break;
}
//模拟延时
try {
Thread.sleep(500); //延时需要一个tay catch
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了"+ticketNums--+"张票");
}
}
public static void main(String[] args) {
test t=new test();
//线程执行
new Thread(t,"小明").start();
new Thread(t,"黄牛").start();
new Thread(t,"人上人").start();
}
}
/*
并发问题发 现,对个线程操作同个资源的情况下,线程不安全,数据紊乱
小明拿到了10张票
人上人拿到了10张票
黄牛拿到了10张票
*/
4.4龟兔赛跑 总结 运用多线程 Runnable
我写得
/*模拟龟兔赛跑
*1.用runnable接口来实现
*
* */
public class test1 implements Runnable{
private int steptu=100;
private int stepgui=100;
public test1() {
}
public test1(int steptu, int stepgui) {
this.steptu = steptu;
this.stepgui = stepgui;
}
@Override
//兔子
public void run() {
for (; steptu>=0; steptu-=2) {
//在某一个时间。兔子想睡觉了
if (steptu==50)
{
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//跑
System.out.println(Thread.currentThread().getName()+"兔子离终点还有"+steptu+"步数");
gameover();
}
}
//乌龟
public int gui()
{
//乌龟
for (; stepgui>=0;stepgui--) {
//爬
System.out.println(Thread.currentThread().getName()+"乌龟离终点还有"+stepgui+"步数");
gameover();
}
return stepgui;
}
//游戏结束
public void gameover()
{
if (stepgui==0) {
System.out.println("乌龟赢了");
System.out.println("兔子输了");
}
}
public static void main(String[] args) {
//跑步开始
Runnable r=new test1();
//兔子乌龟
new Thread(r,"兔子").start();
new test1().gui();
//判定胜利者
new test1().gameover();
}
}
老师写的
package demo2;
public class Race1 implements Runnable{
@Override
//模拟龟兔赛跑
public void run() {
for (int i = 0; i <=100; i++) {
// 模拟兔子休息,让兔子每10步休息0.2s
if(Thread.currentThread().getName().equals("兔子")&& i%10==0)
{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag=gameOver(i);
//如果比赛结束了
if(flag)
{
break;
}
System.out.println(Thread.currentThread().getName()+"跑了————>"+i+"步数");
}
}
//判断是否完成比赛
private String Winner;
private Boolean gameOver(int steps)
{
//哪个进程是先走到100就胜利
if(Winner!=null) //已经有胜利者了
{
return true;
}
if (steps >= 100) {
Winner = Thread.currentThread().getName();
System.out.println("Winner is" + Winner);
return true;
}
return false;
}
public static void main(String[] args) {
//定义一个赛道
Race1 race=new Race1();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}
总结:多个程序走一条路
5 . Callable接口 【了解即可】
1.实现接口,需要返回值类型
2.重写call方法,抛出异常 call和run一样
3.创建目标对象
4.创建执行服务
5.提交执行 提交目标对象
6.获取结果 要抛出异常
7.关闭服务
package demo3;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;
//线程创建方式三:Callable接口
// 1.实现接口,需要返回值类型
public class Callabletest implements Callable {
private String URL; //网络图片地址
private String name; //保存文件名
//2.重写call方法,抛出异常 call和run一样
@Override
public Boolean call() throws Exception {
//下载图片线程执行体
WebDownloader w=new WebDownloader();
w.download(URL,name);
System.out.println("下载的文件名为:"+name);
return true;
}
//构造方法
public Callabletest(String name, String URL) {
this.URL = URL;
this.name = name;
}
//主方法
public static void main(String[] args) throws ExecutionException, InterruptedException {
//3.创建目标对象
Callabletest c1=new Callabletest("1.jpg","https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3100081661,125115249&fm=11&gp=0.jpg");
Callabletest c2=new Callabletest("2.jpg","http://images.china.cn/attachement/jpg/site1000/20090515/0019b91ebfca0b770a9113.jpg");
//4.创建执行服务
ExecutorService ser= Executors.newFixedThreadPool(2); //要执行的任务数量
//5.提交执行 提交目标对象
Future<Boolean> f1=ser.submit(c1);
Future<Boolean> f2=ser.submit(c2);
//6.获取结果 要抛出异常
boolean bo1= f1.get();
boolean bo2= f2.get();
//7.关闭服务
ser.shutdownNow();
}
}
//下载器
class WebDownloader{
//下载方法
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异常,download方法出现问题");
}
}
}
6.Lamda表达式
1.函数式接口
Functional interface
任何接口,只要包含唯一一个抽象方法,他就是函数式接口
对于函数式接口,我们可以通过Lamda表达式来创建接口的对象
lamda表达式推演 从复杂到极简 lamda表达式不需要实现类 lambda表达式是根据匿名内部类推演出来的。
/*
Lamda表达式推演
*/
// 函数式接口
public interface Lamdatest {
void likemethod();
}
//1.实现类
class ilike implements Lamdatest{
@Override
public void likemethod() {
System.out.println("I like Lamda 实现类");
}
//2.静态内部类 实现接口
static class staticclass implements Lamdatest{
@Override
public void likemethod() {
System.out.println("I like Lamda 静态内部类");
}
}
public static void main(String[] args) {
//1.实现接口
new ilike().likemethod();
//2.静态内部类
staticclass st=new staticclass();
st.likemethod();
//成员内部类调用
outer.innner oi=new outer().new innner();
oi.likemehtod();
//通过方法调用局部方法
new outer().Localinnermehtod();
//5.匿名内部类 拿接口来举例
Lamdatest la=new Lamdatest() {
@Override
public void likemethod() {
System.out.println("I like Lamda 匿名内部类" );
}
};
la.likemethod();
//6 lamda 简化 去掉=右边的 式子和方法名
Lamdatest La=()-> {
System.out.println("I like Lamda Lamda " );
};
La.likemethod();
}
}
//3.成员内部类
// 外部类
class outer{
//内部类
class innner {
//内部方法
public void likemehtod() {
System.out.println("I like Lamda成员内部类");
}
}
//局部方法
public void Localinnermehtod(){
//4.局部内部类
class Localinnerclass implements Lamdatest{
@Override
public void likemethod() {
System.out.println("I like lamda 局部内部类");
}
}
//局部内部类匿名对象调用方法
new Localinnerclass().likemethod();
}
}
lamda表达式的几种简化方式
总结:
1.Lamda表达式只能有一行代码的情况下才能简化为一行:如果有多行 就要代码块包裹
2.lamda表达式的前提是函数式接口
3.多个参数可以去掉参数类型,要去掉就都去掉不能(a,int b),只能(a,b)
package demo4;
public class Lamdatest1 {
public static void main(String[] args) {
//匿名内部类
love lo=new love() {
@Override
public void lovemethod(int a) {
System.out.println(" I love you");
}
};
//1.简化参数类型
love lo1 =( a)->
{
System.out.println(" I love you1");
};
lo1.lovemethod(520);
//2.简化小括号
love lo2= a ->
{
System.out.println(" I love you2");
};
lo2.lovemethod(520);
//3.简化花括号
love lo3= a ->
System.out.println(" I love you3");
lo3.lovemethod(520);
//4.如果一个实现类已经实现了接口 Lamda表达式为
Lamdatest lamdatest= (()-> System.out.println("i "));
}
}
/*
总结:Lamda表达式只能有一行代码的情况下才能简化花括号:如果有多行 就要代码块包裹
lamda表达式的前提是函数式接口
多个参数可以去掉参数类型,要去掉就都去掉不能(a,int b),只能(a,b)
*/
//接口
interface love
{
void lovemethod(int a);
}
7.静态代理
静态代理模式总结
1,真实对象和代理对象都要实现同一个接口
2.代理对象要代理真实角色
好处:代理对象可以做真实对象做不了的事情
真实对象专注做自己的事情
比如说你要结婚,你去找中介,你和中介都要同时实现结婚这个接口,你干你 能做得事情,发请帖,中介去帮你订婚纱,酒宴什么的。
interface Marry
{
void HappyMerry();
}
//真实对象
class you implements Marry
{
@Override
public void HappyMerry()
{
System.out.println("结婚了");
}
}
//中介公司 代理对象 帮你做事情
class WeddingCompany implements Marry
{
private Marry target;
public WeddingCompany()
{
}
//把你传给婚庆公司
public WeddingCompany(Marry target)
{
this.target=target; //然后this,target=you
}
@Override
public void HappyMerry() {
before();
target.HappyMerry();
after();
}
private void before()
{
System.out.println("婚礼准备工作");
}
private void after()
{
System.out.println("收尾款");
}
}
public class statictest {
public static void main(String[] args) {
//把你传给婚庆公司
WeddingCompany weddingCompany = new WeddingCompany(new you());
weddingCompany.HappyMerry();
//用lambda表达式简化 线程的启动
// thread类代理Runnable接口 源码 public class Thread implements Runnable
new Thread (()-> System.out.println("我爱你")).start();
//中介公司代理你
new WeddingCompany(new you()).HappyMerry();
}
}
8.线程的五大状态
8.1停止线程 利用标志位在某个点可以停止线程
测试stop
建议线程正常停止,在某一次的时候停止
设置一个标志位
不使用JDK的stop或者destroy等过时的方法
public class test1 implements Runnable{
//1.设置一个标志位
private boolean flag=true;
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
System.out.println("线程正在运行 "+i);
if (flag=false)
{
break;
}
}
}
//2.设置一个公开的方法停止线程 转换标志位
public void stop(boolean judge)
{
this.flag=judge;
}
public static void main(String[] args) {
test1 t=new test1();
new Thread(t).start();
//调用stop方法,在某一个时间段停止
for (int i = 0; i < 100; i++) {
if (i==1)
{
t.stop(false);
System.out.println("线程停止");
}
System.out.println("MAIN"+i);
}
}
}
8.2线程休眠 Thread.sleep静态方法
线程休眠Thread.sleep存在异常,作用;可以模拟网络延时,计时器
模拟计时器
import java.text.SimpleDateFormat;
import java.util.Date;
public class countdown {
public void tenDown() throws InterruptedException
{
int num=10;
while(true)
{
//打印系统当前时间
Date time=new Date(System.currentTimeMillis());
//定义格式 通过SimpleDateFormat new SimpleDateFormat("格式").format(Date date);
//按照格式打印时间
System.out.println(new SimpleDateFormat("HH:mm:ss").format(time));
//休眠1s
Thread.sleep(1000);
System.out.println(num--);
if(num==0)
{
break;
}
}
}
//模拟倒计时
public static void main(String[] args)
{
try
{
new countdown().tenDown();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
8.3线程礼让 Thread.field 静态方法
让正在执行的线程暂停,但是不阻塞
将线程从运行状态转为就绪状态
让CPU重新调度,礼让不一定成功,看CPU心情
线程礼让 两个线程赛跑 就是让一个线程跑到一半,就回到出发点重新开始比(正常是一个线程跑完再到另外一个线程跑)
public class ThreadYield {
public static void main(String[] args) {
testyield t=new testyield();
new Thread(t,"a").start();
new Thread(t,"b").start();
}
}
class testyield implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield(); //线程礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
/*
b线程开始执行 CPU太快了礼让一直成功
a线程开始执行
b线程停止
a线程停止
*/
}/*
线程礼让 两个线程赛跑 就是让一个线程跑到一半,就回到出发点重新开始比(正常是一个线程跑完再到另外一个线程跑)
*/
public class ThreadYield {
public static void main(String[] args) {
testyield t=new testyield();
new Thread(t,"a").start();
new Thread(t,"b").start();
}
}
class testyield implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield(); //线程礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
/*
b线程开始执行 CPU太快了礼让一直成功
a线程开始执行
b线程停止
a线程停止
*/
}/*
线程礼让 两个线程赛跑 就是让一个线程跑到一半,就回到出发点重新开始比(正常是一个线程跑完再到另外一个线程跑)
*/
8.4线程合并Thread. join
此线程执行完之后,其他线程才能执行
//测试join方法 想象一下是插队
public class Threadjoin implements Runnable {
//线程体
@Override
public void run()
{
for (int i = 0; i < 100; i++)
{
System.out.println("线程VIP" + i);
}
}
public static void main (String[]args) throws InterruptedException
{
//线程启动
Threadjoin t = new Threadjoin();
Thread thread = new Thread(t, "a");
thread.start();
//主线程
for (int i = 0; i < 100; i++)
{
if (i == 50)
{
thread.join(); //main线程阻塞
}
System.out.println("Main" + i);
}
}
}
8.5线程的6个状态以及检测过程
NEW
至今尚未启动的线程处于这种状态。
RUNNABLE
正在 Java 虚拟机中执行的线程处于这种状态。
BLOCKED
受阻塞并等待某个监视器锁定的线程处于这种状态。
WAITING
正在等待另一个线程来执行某一特定操作的线程处于这种状态。
等待唤醒案例
void wait()
在其他线程调用此对象的notify方法或notifyAll方法前,导致当前线程等待
放弃CPU的执行,进入waiting状态
void notify()
唤醒在此对象监视器上等待的单个线程。
注意:
保证等待和唤醒的线程只能有一个执行 要用到同步代码块
同步使用的锁对象必须保证唯一
只有锁对象才能调用wait和notify方法
每一个要等待和要唤醒的线程必须用同步代码块包裹起来
总结:A,B两个线程同时执行,若想要A先执行,B后执行.那就让A开始运行时候直接等待,B结束运行时候结束A的等待。
import java.util.Objects;
import java.util.Scanner;
public class testwaitting {
public static void main(String[] args)
{
//创建锁对象 唯一
Object obj = new Object();
//创建一个顾客线程
Thread t=new Thread() {
@Override
public void run() {
synchronized (obj) {
System.out.println("告知老板包子的数量");
//调用wait方法,放弃cpu的执行,进入到waiting状态
try {
//进入等待状态
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("包子已经做好了,开吃");
}
}
};
t.start();
//老板
new Thread()
{
@Override
public void run()
{
//花5s做包子
try
{
Thread.sleep(5000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
//保证等待和唤醒线程只能有一个执行,需要用到同步技术
synchronized (obj)
{
System.out.println("包子做好了");
//唤醒
obj.notify();
}
}
}.start();
}
}
TIMED_WAITING
正在等待另一个线程执行动作达到指定等待时间的线程处于这种状态。
进入到TImeWaiting计时等待有两种方式:
1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态
2.使用wait(long m)方法,wait方法如果在毫秒值结束后,还没有被唤醒,就会自动醒来,线程唤醒进入,Runnable/Blocked状态
唤醒方法:
1.void notify() 唤醒此对象监视器上等待的单个线程
void notifyAll() 唤醒此对象监视器上等待的单个线程
TERMINATED
已退出的线程处于这种状态。
线程的监测过程
//线程的检测过程
public class teststate {
//线程体用lambda表达式
public static void main(String[] args) throws InterruptedException {
Thread thread=new Thread(()->
{ //在线程体里面创建for循环
for (int i = 0; i < 5; i++) {
try {
//每1s停一下
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.println(i);
}
});
//1.观察状态
Thread.State state = thread.getState(); //new
System.out.println(state);
//2.观察启动后
thread.start(); //启动线程就绪状态,并不是立即调度执行
state= thread.getState(); //观察状态
System.out.println(state); //Runnable
while (state!=Thread.State.TERMINATED)//只要线程不终止,就一直输出状态
{
Thread.sleep(100);
state=thread.getState();
System.out.println(state);//输出状态
}
//线程不能结束后再启动一次。启动一次就死亡
thread.start();
}
}
8.6.设置线程的优先级
注意:
1.优先级的设置只是让优先级高的线程被调用的概率变大,而不是必定调用优先级高的先执行。
2.优先级低只是意味着获得调度的概率低,并不是优先级低就不会被调用了
3.主线程默认优先级5 优先级的设定在start调度前面
4.从第二个进程开始,先设置优先级然后再启动
5.优先级是从0-10的
方法:
Thread.currentThread().getPriority()
Thread对象.setPriority(Thread.NORM_PRIORITY);
public class testPriority {
public static void main(String[] args) {
//主线程默认优先级5 优先级的设定在start调度前面
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority() );
Priority p=new Priority();
Thread t1=new Thread(p);
Thread t2=new Thread(p);
Thread t3=new Thread(p);
Thread t4=new Thread(p);
Thread t5=new Thread(p);
Thread t6=new Thread(p);
t1.start();
//从第二个进程开始,先设置优先级然后再启动
t2.setPriority(2);
t2.start();
t3.setPriority(6);
t3.start();
t4.setPriority(Thread.NORM_PRIORITY);
t4.start();
t5.setPriority(Thread.MAX_PRIORITY);
t5.start();
t6.setPriority(Thread.MIN_PRIORITY);
t6.start();
}
}
class Priority implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+Thread.currentThread().getPriority() );
}
}
8.7用户线程和守护线程
线程分为用户线程和守护线程
虚拟机必须确保用户线程执行完毕,不用等待守护线程执行完毕(用户线程结束了,守护线程过一会儿也会结束)
package demo6;
//测试守护线程
public class daemontest {
//主线程
public static void main(String[] args) {
god g=new god();
You you=new You();
Thread thread=new Thread(g);
thread.setDaemon(true);//默认false是用户线程,正常的线程都是用户线程
thread.start(); //上帝守护线程启动
new Thread(you).start(); //用户线程
}
}
//上帝守护你
//线程体1
class god implements Runnable
{
@Override
public void run() {
while(true)
{
System.out.println("god bless you");
}
}
}
//线程体2
class You implements Runnable
{
@Override
public void run() {
for (int i = 0; i < 365000; i++) {
if(i==364999)
{
System.out.println("goodbye world");
break;
}
System.out.println("你开心的活着");
}
}
9 线程同步 多个线程操作在同一个资源
并发:同一个对象被多个线程同时操作 如一堆人去抢显卡
同步形成条件:队列+锁
多个线程去抢同一个资源 会出现安全问题
package synchronizetest;
//不安全的案例 买票
//线程不安全 有负数
public class Example1 {
public static void main(String[] args) {
//启动线程
Buyticke buyticke=new Buyticke();
Thread thread1=new Thread(buyticke,"小a");
Thread thread2=new Thread(buyticke,"小b");
Thread thread3=new Thread(buyticke,"小c");
thread1.start();
thread2.start();
thread3.start();
}
}
//买票的线程
class Buyticke implements Runnable
{ //票的数量
private int ticket = 10;
//立flag来控制线程运行
boolean flag=true;
@Override
public void run()
{
while(flag)
{
try { //延时 防止单线程跑
Thread.sleep(100);
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//买票
private void buy() throws InterruptedException {
if(ticket<=0)
{
flag=false;
return;
}
//模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket+"张");
ticket--;
}
}
问题:
出现了重复的票和不存在的票 ?
1.thread1 thread2 thread3同时执行要了买到第 ticket张票,但还没有进行–。
3个线程一起抢夺cpu的执行权 谁抢到 谁就执行,但是执行到了Thread.Sleep就失去了cpu的执行权
假设t2线先睡醒了,假设thread2就买到第1张票了,ticket–,然后thread1醒了就买第0张票,再然后thread醒了就卖-1张票
总结:线程安全问题不能产生
我们可以让一个线程在访问共享数据的时候,无论是否失去了CPU执行权,让其他线程等待,当这个线程买完票之后,其他线程接着买。所以,我们要用到同步操作。
有三种方式完成同步操作:
1.同步代码块
2.同步方法
3.锁机制
9.1 第一种方案 使用同步代码块 synchronize(锁对象){
把能让共享资源增删减除的代码放入代码块中
}
注意:
1.通过代码块中的锁对象,可以使用任意的对象
2.但是必须保证多个线程使用的锁对象是同一个,所以要在run方法外面创建锁对象 Object obj=new Object();
3.锁对象作用:把同步代码块锁住,只让一个线程在同步代码块中执行
package synchronizetest;
//不安全的案例 买票
//线程不安全 出现了重复的票和不存在的票
public class Example1 {
public static void main(String[] args) {
//启动线程
Buyticke buyticke=new Buyticke();
Thread thread1=new Thread(buyticke,"小a");
Thread thread2=new Thread(buyticke,"小b");
Thread thread3=new Thread(buyticke,"小c");
thread1.start();
thread2.start();
thread3.start();
}
}
//买票的线程
class Buyticke implements Runnable
{ //票的数量
private int ticket = 10;
//立flag来控制线程运行
boolean flag=true;
//创建锁对象obj
Object obj=new Object();
@Override
public void run()
{
while(flag)
{
//把访问到共享资源的代码块放同步代码块j中
synchronized (obj)
{
try { //延时 防止单线程跑
Thread.sleep(100);
buy();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//买票
private void buy() throws InterruptedException {
if(ticket<=0)
{
flag=false;
return;
}
//模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket+"张");
ticket--;
}
}
-
总结: 同步技术原理:
使用了一个锁对象,也叫同步锁,也叫对象监视器总结:同步中的线程,没有执行完毕不会释放锁,同步外的线程没有锁,进不去同步代码块
同步包装了只能有一个线程在同步执行共享数据,保证了安全性,但是效率会降低。
‘
9.2 第二种方案同步方法
格式:public synchronized 返回值类型 方法名(参数列表){
//把访问到共享数据的代码抽出来放入其中
}
然后run方法再调用这个方法
/*
* 第二种方式 同步方法*/
public class Example1 {
public static void main(String[] args) {
//启动线程
Buyticke buyticke=new Buyticke();
Thread thread1=new Thread(buyticke,"小a");
Thread thread2=new Thread(buyticke,"小b");
Thread thread3=new Thread(buyticke,"小c");
thread1.start();
thread2.start();
thread3.start();
}
}
//买票的线程
class Buyticke implements Runnable
{ //票的数量
private int ticket = 10;
//立flag来控制线程运行
boolean flag=true;
//创建同步方法把涉及到共享济源的代码放入其中
public synchronized void tongbumethod() throws InterruptedException
{
while(flag)
{
buy();
}
}
//买票
private void buy() throws InterruptedException {
if(ticket<=0)
{
flag=false;
return;
}
//模拟延时
Thread.sleep(100);
System.out.println(Thread.currentThread().getName()+"买到了第"+ticket+"张");
ticket--;
}
@Override
public void run()
{ //调用同步方法
try
{
tongbumethod();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
补充;静态同步方法:
格式:public static synchronized 返回值类型 方法名(参数列表){
//把访问到共享数据的代码抽出来放入其中
}
静态同步方法的锁对象不是this,是本类的class文件对象(反射),this是穿件对象后产生的,静态方法优于对象
9.3 第三种方式: 使用lock锁
java.util.concurrent.locks
Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作。
使用步骤
void lock () 获取锁
void unlock() 释放锁
ReentrantLock 实现类 实现了lock接口
使用步骤:1.在成员位置创建一个ReentrantLock 对象
2. 在可能出现线程安全问题的代码前调用Lock接口中的方法Lock获取锁
3.在可能出现线程安全问题的代码后调用Lock接口中的方法unLock释放锁 (最好放在finally里面,这样无论程序是否异常,都会把锁释放)
10.线程池 里面可以封装多个线程
线程池的使用:
1.static ExecutorService newFixedThreadPool(int nThreads)
创建一个可重用固定线程数的线程池
参数:int nThread 创建线程池中包含的线程数量
返回值: ExecutorServiced的接口,返回的是ExecutorService接口的实现类对象 ,
我们可以使用ExecutorService接口来接收
java.util.concurrent.ExecutorService
2.用来从线程池中获取线程,调用start方法,执行线程任务
3. submit(Runnable task)
提交一个 Runnable 任务用于执行,
关闭或者销毁线程池的方法
4. void shutdown();
线程池使用步骤:
1.线程池使用的工厂类Executors 里面提供的静态方法newFixedThreadPool 创建一个可重用固定线程数的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit传递线程任务(Runnable接口的实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池,(不建议使用)
代码实现
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPool {
/*
线程池使用步骤:
1.线程池使用的工厂类Executors 里面提供的静态方法newFixedThreadPool 创建一个可重用固定线程数的线程池
2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
3.调用ExecutorService中的方法submit传递线程任务(Runnable接口的实现类),开启线程,执行run方法
4.调用ExecutorService中的方法shutdown销毁线程池,(不建议使用)
*/
public static void main(String[] args) {
//1.线程池使用的工厂类Executors 里面提供的静态方法newFixedThreadPool 创建一个可重用固定线程数的线程池
ExecutorService es= Executors.newFixedThreadPool(2);
// 3.调用ExecutorService中的方法submit床底线程任务(实现类),开启线程,执行run方法
//线程池会一直开启,使用完了线程,就会自动把线程归还给线程池,线程可以继续使用
es.submit(new Runnabletest());
es.submit(new Runnabletest());
es.submit(new Runnabletest());
es.submit(new Runnabletest());
// 4.调用ExecutorService中的方法shutdown销毁线程池,(不建议使用)
es.shutdown();
}
}
//2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
class Runnabletest implements Runnable
{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"NB");
}
}