多线程:
多任务执行,如果没有多任务,就不需要多线程
线程和进程之间的区别:
1. 多线程的创建方式:
1.继承Thread,重写run()方法 + start()开启线程
public class ThreadDemo01 extends Thread{
/*
* 多线程的线程体
*/
@Override
public void run() {
for(int i=0;i<=20;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一边敲代码...");
}
}
public static void main(String[] args) {
//创建一个线程
ThreadDemo01 th=new ThreadDemo01();
//开启线程
th.start();
//th.run(); 方法的调用,是单线程
for(int i=0;i<=20;i++){
//延时
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一边陪女朋友...");
}
}
}
2.实现Runnable接口,重写run()方法
推荐使用: 1)避免单继承的局限性 2)实现资源共享
public class ThreadDemo02 extends Object implements Runnable{
public static void main(String[] args) {
ThreadDemo02 demo=new ThreadDemo02();
//创建线程
Thread th=new Thread(demo);
//开启线程
th.start();
for(int i=0;i<=100;i++){
System.out.println("一边陪咳嗽...");
}
}
/*
* 线程体
*/
@Override
public void run() {
for(int i=0;i<=100;i++){
System.out.println("一边陪讲课...");
}
}
}
静态的资源是属于类的,多个对象共享类的静态的内容,在静态区中
成员属于对象的.多个人对象,堆内存中就存在多个成员,互补影响
12306 买票的案例
需求: 100张票 3个人买完,只卖给这三个人,但是哪一个人买了哪一张票随便,卖完就结束
多任务同时执行,3个人同时买票,互相不影响,可以选择使用多线程
实现资源共享
public class Web12306_01 implements Runnable{
//票 共享资源
int tickets=100;
public static void main(String[] args) {
Web12306_01 web=new Web12306_01();
Thread th1=new Thread(web,"张三"); //第二个参数为当前线程起名字
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
//重复的买票
while(true){
System.out.println("-----------------");
System.out.println("-----------------");
System.out.println("-----------------");
System.out.println("-----------------");
System.out.println("-----------------");
System.out.println("-----------------");
//结束循环的条件 没票了就结束 tickets<=0
// A B
if(tickets<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//买票的人:当前线程的名字 Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"票");
//tickets--;
}
}
}
模拟龟兔赛跑:
规则:兔子每跑十步修饰10ms,乌龟不休息一直跑,100步就赢了,如果有人赢了,游戏就结束了
添加标识: String winner=null; 记录冠军的名字
winnner的值判断,游戏是否结束
public class Racer02 implements Runnable{
//记录冠军的名字,winnner的值判断,游戏是否结束
String winner=null;
public static void main(String[] args) {
Racer02 race=new Racer02();
new Thread(race,"乌龟").start();
new Thread(race,"兔子").start();
}
@Override
public void run() {
//i控制步数
for(int i=1;i<=100;i++){
if("兔子".equals(Thread.currentThread().getName()) && i%10==0){
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"了...");
//检测还跑不跑下一步了 true就是结束,false继续跑
boolean flag=checkOver(i);
if(flag){
break;
}
}
}
/*
* 1.检测是否已经有人赢了
* 2.检测自己是不是赢了
*/
private boolean checkOver(int i) {
if(winner!=null){
return true;
}else{
if(i==100){
winner=Thread.currentThread().getName();
return true;
}
}
return false;
}
}
3.实现Callable接口,重写call()方法
优点: 可以返回值,可以抛出异常
缺点: 使用麻烦
public class Racer03 implements Callable<Integer>{ //泛型Integer是call方法的返回值类型
//记录冠军的名字,winnner的值判断,游戏是否结束
String winner=null;
public static void main(String[] args) throws InterruptedException, ExecutionException {
Racer03 race=new Racer03();
//1.创建执行服务 创建一个线程池,用来管理线程的开启和结束
ExecutorService server=Executors.newFixedThreadPool(2);
//2.提交执行
Future<Integer> result1=server.submit(race);
Future<Integer> result2=server.submit(race);
//3.获取返回值
int steps1=result1.get();
int steps2=result2.get();
System.out.println(steps1+"-->"+steps2);
//4.关闭服务
server.shutdown();
}
/*
* 优点:
* 可以有返回值
* 可以抛出异常
*
* 返回值:步数 一共跑了多少步
*/
@Override
public Integer call() throws InterruptedException {
//i控制步数
for(int i=1;i<=100;i++){
if("兔子".equals(Thread.currentThread().getName()) && i%10==0){
Thread.sleep(2);
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"了...");
//检测还跑不跑下一步了 true就是结束,false继续跑
boolean flag=checkOver(i);
if(flag){
return i;
}
}
return null;
}
/*
* 1.检测是否已经有人赢了
* 2.检测自己是不是赢了
*/
private boolean checkOver(int i) {
if(winner!=null){
return true;
}else{
if(i==100){
winner=Thread.currentThread().getName();
return true;
}
}
return false;
}
}
多线程可以写在不同的位置:
public class ThreadDemo04 {
//静态内部类
static class Inner implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一遍玩手机");
}
}
}
public static void main(String[] args) {
//局部内部类
class LocalInner implements Runnable{
@Override
public void run() {
for(int i=1;i<=20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一遍吃饭");
}
}
}
new Thread(new Inner()).start();
new Thread(new LocalInner()).start();
//匿名内部类
new Thread(new Runnable(){
@Override
public void run() {
for(int i=1;i<=20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一遍学习");
}
}
}).start();
//Lambda 表达式
new Thread(()->{
for(int i=1;i<=20;i++){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("一遍学习");
}
}).start();
}
}
2. 线程状态 5个
- 新生状态: 创建线程 new Thread()
- 就绪状态: 调用start(),就绪队列 中的线程等待cpu的调度
- 运行状态: cpu把时间片分配给哪一个线程,这个线程才会运行
- 阻塞状态: sleep()…
- 终止状态: 结束,死亡
注意:
1.一个线程一旦进入终止状态,没有办法恢复,重新创建也是新的线程
2.一旦进入阻塞状态,不能直接恢复到运行状态的,恢复到就绪状态
一个线程如何进入到死亡状态?
1.正常执行完毕
2.调用destroy(),stop()
3.通过添加标识判断 —推荐
一个线程如何进入到就绪状态?
1.调用start()方法
2.阻塞解除
3.yield 礼让
4.线程切换
一个线程如何进入到阻塞状态:
1.sleep()
2.join()
3.wait()
4.io操作
sleep(毫秒数) 线程睡眠|休息
抱着资源睡觉,告诉cpu在多少时间中不要把资源分配给我这个线程,因为处于阻塞状态,到时间了就恢复到就绪状态,这个时候cpu才能把资源分配给我
sleep()的使用不要在任何同步环境下,但是如果在同步环境下使用了,在休眠途中,是不会释放对象锁的
模式网络延迟
放大问题的可能性
public class StateDemo01 implements Runnable{
public static void main(String[] args) {
//倒计时
new Thread(new StateDemo01()).start(); //静态代理
//Thread 代理类 StateDemo01真实角色
}
@Override
public void run() {
for(int i=10;i>=0;i--){
if(i==0){
System.out.println("结束啦,开始放假!!!!");
return;
}
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
yield 礼让线程 高风亮节
把cpu的资源让出,让cpu重新分配,但是有可能又选择了原线程,可以选择了其他线程,给了其他线程能够获取资源的机会
public class YieldDemo02 implements Runnable{
public static void main(String[] args) {
new Thread(new YieldDemo02(),"大王").start();
new Thread(new YieldDemo02(),"小王").start();
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "---------start()");
Thread.yield(); //礼让线程
System.out.println(Thread.currentThread().getState());;
System.out.println(Thread.currentThread().getName() + "---------end()");
}
}
合并线程 ,插队线程
join() 处于阻塞状态的方法
join(ms) 等待指定毫秒数, 到点了,就阻塞等待了
public class JoinDemo03 {
public static void main(String[] args) {
new Thread(new Father()).start();
}
}
//爸爸类
class Father implements Runnable{
@Override
public void run() {
System.out.println("突然想抽烟了...");
System.out.println("让儿子去买烟...");
//儿子线程插队
Thread th=new Thread(new Son());
th.start();
try {
th.join(); //插队
} catch (InterruptedException e) {
e.printStackTrace();
}
//开启线程
System.out.println("接过儿子买的烟,抽一口,人生都幸福了...");
System.out.println("把零钱给儿子当跑腿费了...");
}
}
//儿子类
class Son implements Runnable{
@Override
public void run() {
System.out.println("拿过老爸递过来的钱,去买烟...");
System.out.println("路边看到一个游戏厅,去10s钟");
for(int i=1;i<=10;i++){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i+"s钟过去了");
}
System.out.println("赶紧去买烟...");
System.out.println("买完了,把烟给老爸..");
}
}
线程的优先级别: 1~10
默认一个线程的优先级: 5
MAX_PRIORITY 最大优先级 10
MIN_PRIORITY 最小优先级 1
NORM_PRIORITY 默认 5
setPriority() 设置线程优先级
getPriority() 获取线程优先级
优先级只能控制概率问题,不能角色到底谁先执行
获取线程状态
Thread.State getState() 返回该线程的状态。
public class PriorityDemo04 {
static class Inner implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}
public static void main(String[] args) {
/*Thread th1=new Thread(new Inner(),"A");
Thread th2=new Thread(new Inner(),"B");
Thread th3=new Thread(new Inner(),"C");
th1.setPriority(Thread.MIN_PRIORITY);
th2.setPriority(Thread.MAX_PRIORITY);
th3.setPriority(Thread.NORM_PRIORITY);
System.out.println(th1.getPriority());
System.out.println(th2.getPriority());
System.out.println(th3.getPriority());
th1.start();
th2.start();
th3.start();*/
//创建一个线程
Thread th=new Thread(()->{
for(int i=1;i<=10;i++){
if(i==5){
System.out.println(i);
try {
Thread.sleep(500);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(i+"--->"+Thread.currentThread().getState());
}
},"陈相吉");
//就绪
System.out.println(th.getState()); //NEW
th.start(); //就绪
System.out.println(th.getState()); //RUNNABLE 运行和就绪都是这个状态
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(th.getState()); //TIMED_WAITING --sleep()
//TERMINATED 终止
if(Thread.State.TERMINATED==th.getState()){
break;
}
}
}
}
3.synchronized关键字 同步锁
控制线程安全问题: ****
前提: 当多个线程同时操作同一份资源的时候,才有可能出现线程不安全问题
办法:使用同步锁 synchronized ,控制多线程执行时,某一段重要的代码,排队执行,其他代码可以同时执行,有能提高效率,有能控制安全
使用方式:
-
同步方法 : 在方法上使用synchronized关键字
成员方法 : 对象,类,资源
静态方法 : 静态的内容是属于类的,锁这个静态方法相当于锁类
比较简单,但是范围太大,可能效率会低 -
同步块
synchronized(this|类|资源){ }
this: 锁对象
类名.calss: 只有一个,不变的对象内容,在类第一次加载金内存就存在了
资源: 成员属性注意:
同步一定要同步不变的内容(自定义类的对象地址),变的内容锁不住;
锁的范围太大了,效率太低,锁的范围太小了,锁不住难点: 锁谁,this|资源|类|方法 锁的多大的范围
public class SynchronizedDemo01 { public static void main(String[] args) { new Thread(()->{ System.out.println(SingleTon.newInstance()); }).start(); new Thread(()->{ System.out.println(SingleTon.newInstance()); }).start(); } } //单例的类 class SingleTon{ //私有的,静态的,该类的引用 ,存储唯一的对象 private static SingleTon single=null; //构造器私有化 private SingleTon(){} //公共的静态的访问方式 //同步方法 public static synchronized SingleTon newInstance(){ System.out.println("---------------------"); if(single==null){ single=new SingleTon(); } return single; } //同步块 public static SingleTon newInstance(){ System.out.println("---------------------"); synchronized (SingleTon.class) { //synchronized (single) { 锁不住,会发生空指针,在第一次调用这个方法时候,single还指向null,没有执行一个对象 if(single==null){ single=new SingleTon(); } return single; } } }
同步方法
public class Web12306_05 implements Runnable{
//票 共享资源
int tickets=100;
public static void main(String[] args) {
Web12306_05 web=new Web12306_05();
Thread th1=new Thread(web,"张三"); //第二个参数为当前线程起名字
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
//重复的买票
while(true){
//结束循环的条件 没票了就结束 tickets<=0
//A B C
boolean flag=buy();
if(flag){
break;
}
}
}
//没票的流程
//同步成员方法
public synchronized boolean buy(){
System.out.println("----------------------------");
if(tickets<=0){
return true;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票的人:当前线程的名字 Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"票");
return false;
}
}
同步块 类.calss
锁类,相当于把这个类的所有对象,内容都锁住了,如果有些对象不需要锁,我们就只锁某一个对象
public class Web12306_06 implements Runnable{
//票 共享资源
int tickets=100;
public static void main(String[] args) {
Web12306_06 web=new Web12306_06();
Thread th1=new Thread(web,"张三"); //第二个参数为当前线程起名字
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
//重复的买票
while(true){
//结束循环的条件 没票了就结束 tickets<=0
//A B C
synchronized (Web12306_06.class) { //独一份的,锁之前就存在的,不变的
if(tickets<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票的人:当前线程的名字 Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"票");
}
}
}
}
同步块 对象 this
锁对象,把这个对象所有的资源全部锁住了,如果指向锁住其中的某一个资源,建议可以锁资源
public class Web12306_07 implements Runnable{
//票 共享资源
int tickets=100;
public static void main(String[] args) {
Web12306_07 web=new Web12306_07();
Thread th1=new Thread(web,"张三"); //第二个参数为当前线程起名字
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
//重复的买票
while(true){
//结束循环的条件 没票了就结束 tickets<=0
//A B C
synchronized (this) { //当前调用这个成员方法的对象
if(tickets<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票的人:当前线程的名字 Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets--+"票");
}
}
}
}
同步块 资源 (成员变量)
自定义引用数据类型的对象永远不变
public class Web12306_08 implements Runnable{
//票 共享资源 tickets就指向这个对象,不会改变
Ticket tickets=new Ticket();
public static void main(String[] args) {
Web12306_08 web=new Web12306_08();
Thread th1=new Thread(web,"张三"); //第二个参数为当前线程起名字
Thread th2=new Thread(web,"李四");
Thread th3=new Thread(web,"王五");
th1.start();
th2.start();
th3.start();
}
@Override
public void run() {
//重复的买票
while(true){
//结束循环的条件 没票了就结束 tickets<=0
//A B C
synchronized (tickets) { //tickets 变量,数值
if(tickets.nums<=0){
break;
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
//买票的人:当前线程的名字 Thread.currentThread().getName()
System.out.println(Thread.currentThread().getName()+"正在购买第"+tickets.nums--+"票");
}
}
}
}
//自定义的引用数据类型
class Ticket{
int nums=100;
}
线程通信
wait() 等待 notify()唤醒
wait() 进入到对象的等待池中进行等待,等待对方线程的唤醒,告诉cpu在等待时刻不要为线程分配资源,并且会释放对象的锁
线程进入到等待状态–>等待阻塞
sleep() 不会释放对象锁
notify() 唤醒对象等待池中的某一个线程,唤醒不代表就可以执行,必须还要获取到对象锁才可以,cpu调度才可以执行
notifyAll() 唤醒全部
要求使用在共同的对象锁的环境下,锁方法,锁块…
通过信号灯法实现生产者消费者模式
街道案例:
人走 南北 绿灯true
车走东西 红灯 false
公用街道对象 存储在信号灯
public class Demo01 extends Object{
public static void main(String[] args) {
Street street = new Street();
new Thread(new Person(street)).start();
new Thread(new Car(street)).start();
}
}
//街道
class Street{
//红绿灯 信号灯
boolean flag=true;
//南北 绿灯 true
public synchronized void ns(){
if(!flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("人走.....");
this.notify(); //唤醒
flag=false;
}
//东西 红灯false
public synchronized void we(){
if(flag){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("车走...");
notifyAll();
flag=true;
}
}
}
//人
class Person implements Runnable{
//公用的街道
Street street;
public Person(Street street) {
super();
this.street = street;
}
@Override
public void run() {
while(true){
street.ns();//人走南北
}
}
}
//车
class Car implements Runnable{
//公用的街道
Street street;
public Car(Street street) {
this.street = street;
}
@Override
public void run() {
while(true){
street.we(); //车走东西
}
}
}
注意:
死锁:容易造成死锁