线程举例:
String家族:
StringBuilder线程非同步,不安全,但是效率高。
StringBuffer线程同步,安全性高

复习总结:

  1. 进程和线程:操作系统中运行的每一个任务对应一个进程,当一个程序进入内存运行时,即变成了一个进程。进程是操作系统进行组员分配和调度的一个独立单元。
    线程是进程的执行单元。
  2. 线程的状态:新生态;就绪态;运行态;阻塞态;死亡态
  3. start和run方法:让线程开始新的任务的时候,我们调用start方法,让cpu自己执行run方法,只需要调用start方法告诉cpu自己已经准备好了。
  4. 创建线程的两种方法:
    继承Thread类。重写run方法
    实现Runnable接口创建线程
  5. sleep()方法:使当前线程让出cpu,留出一定时间给其他线程执行的机会。
    注意调用sleep方法时,要处理异常
  6. 设置线程的优先级setPriority:
    设置优先级只是说让这个线程可以抢到cpu时间片的概率增大,但是不意味着一定可以抢到cpu时间片,值是[0,10],默认是5
  7. 线程礼让:指当前运行状态的线程释放自己的cpu资源,由运行状态回到就绪状态,然后和另一个线程重新开始一起抢夺cpu时间片,谁抢到还不一定
  8. 临界资源抢夺问题:多个线程共享的资源成为临界资源。
    多线程同时去访问临界资源的时候,可能会出现问题。
  9. 为了解决临界资源问题,出现了锁。当一个线程在访问临界资源的时候,上一把锁,其他线程先等待,前一个线程释放锁以后,其他线程才可以操作临界资源。
    锁synchronized有两种方法:
    同步代码块:在代码块上增加synchronized 修饰符
    同步方法:类方法增加synchronized 修饰符
  10. 死锁
  11. 死锁解决办法使用wait方法,唤醒wait使用notify和notifyAll方法

一. 线程的基础

  1. 几个重要的概念:
  • 程序:可以理解为是一组静态的代码
  • 进程:正在进行的程序,静态的代码 运行起来
  • 线程:正在执行程序中的小单元
    线程是进程中的小单元。
  1. 举个例子理解一下:

聚餐
聚餐之前, 班级大扫除。
扫除需要分配任务,任务写在纸上,列一个清单。这是一个静态的。
一声令下,开始扫除,班级扫除这件事就是-----进程。
每一个同学都做自己的事情,并发执行,互相不影响。

  1. 如何在Java中创建线程: 让线程执行,多线程
    掌握每一个线程的几种不同状态,及状态之间如何切换:

如何实现线程
自定义一个类
继承Thread 实现Runnable
重写run方法
调用start方法 让线程进入就绪状态 需要注意的是start方法Thread类中

  1. 实现线程的过程

    1.自己描述一个类
    2.继承父类Thread
    3.重写run方法
    4.new一个线程对象 调用start()方法 让线程进入就绪状态

实现一个跑步的小例子
多个人同时跑步:苏炳添;博尔特 ;加特林

//一个继承线程的类
public class RunningMan extends Thread {private String name;//构造方法,两个,想用哪个用哪个。public RunningMan(){}public RunningMan(String name){this.name=name;}//重写run方法public void run(){for(int i=1;i<=100;i++){System.out.println(this.name+"跑到第"+i+"米啦");}}
}

主方法测试一下:

public class TestMain {public static void main(String[] args){//1.创建一个线程对象RunningMan r1 = new RunningMan("苏炳添");RunningMan r2 = new RunningMan("博尔特");RunningMan r3 = new RunningMan("加特林");//2.调用start方法  让线程进入就绪状态  按顺序逐个执行r1.start();//从Thread类中继承过来的方法r2.start();r3.start();//如果掉run,那就是单线程了,按照顺序执行了。//我们调用stat是告诉线程,我们准备好了,让cpu自己run。}
}

实现过程很简单,但是有没有什么问题?

Java中 继承,单继承
extends
Person extends Animal
我想让人去多线程,但是发现java是单继承,人已经继承了动物,就不能再继承线程了,那咋么去多线程呢?所以有第二种,多线程。

  1. 实现线程的另一种方法过程

    1.自己描述一个类
    2.实现一个父接口Runnable
    3.重写run方法
    4.new一个线程对象 需要创建Thread将自己的对象包起来 然后调用start()

public class RunningMan implements Runnable {private String name;public RunningMan(){}public RunningMan(String name){this.name=name;}//重写run方法public void run(){for(int i=1;i<=100;i++){System.out.println(this.name+"跑到第"+i+"米啦");}}
}

主方法:

public class TestMain {public static void main(String[] args){//1.创建一个线程对象RunningMan r1 = new RunningMan("苏炳添");RunningMan r2 = new RunningMan("博尔特");RunningMan r3 = new RunningMan("加特林");//2.调用start方法  让线程进入就绪状态  按顺序逐个执行//我们必须调用start,但是不是线程没有start,所以我们构建一个线程对象。Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);Thread t3 = new Thread(r3);t1.start();t2.start();t3.start();}
}

二. 线程之生产消费者模型

  1. 线程并发访问时,可能会产生线程安全问题

生产消费者模型。生产者,生产产品存入仓库,消费者消费产品,去仓库中取走产品。需要三个类,仓库,消费者以及生产者。
想验证一下,因为消费者是多线程访问仓库,是否会发生抢夺资源的现象发生。

仓库类:

public class Warehouse { //仓库里面的集合  存放元素private ArrayList<String> list = new ArrayList<>();//向集合内添加元素的方法public void add(){if(list.size()<20) {list.add("a"); //仓库元素够20个,就不再往里面存元素了。}else{return;//让方法执行到这里就结束方法     }      }//从集合内获取元素的方法public void get(){if(list.size()>0) {list.remove(0); //拿走元素}else{return;//集合越界的问题}}}

生产者类:因为生产者是多线程生产的,所以要继承Thread。

public class Producer extends Thread {//为了保证生产者 和消费者使用同一个仓库对象  添加一个属性private Warehouse house;public Producer(Warehouse house){this.house=house;}//生产者的run方法  一直向仓库内添加元素public void run(){//方法重写,要求名字和参数列表必须一致,所以不能传参,所以我们可以考虑将house放到属性里。while(true){ //死循环,一直往里放house.add(); System.out.println("生产者存入了一件货物");try {Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}}}
}

消费者:

public class Consumer extends Thread{//为了保证生产者 和消费者使用同一个仓库对象  添加一个属性private Warehouse house;public Consumer(Warehouse house){this.house=house;}//消费者的方法  一直从仓库内获取元素public void run(){while(true){house.get();System.out.println("消费者拿走了一件货物");try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}}
}

多线程并发很有可能两个都来拿元素,很有可能发生资源抢劫。
右下角这种方式可能会产生线程安全问题,只剩下一个库存了,两个消费者恰好判断出仓库有文件,又同时一起取了文件,但是只有一个文件,所以会出现异常。也就是线程不安全。

写一个主方法测试一下:

public class TestMain {public static void main(String[] args){Warehouse house = new Warehouse();//里面有一个ArrayList线程非安全Producer p = new Producer(house);Consumer c1 = new Consumer(house);Consumer c2 = new Consumer(house);//以上三个线程p.start();c1.start();c2.start();}
}

运行时,会发生异常。说明刚才的分析没有错误。多线程并发,会发生安全问题。

通过这个模型 成功的演示出了 线程安全的问题
两个消费者 同时访问同一个仓库对象 仓库内只有一个元素的时候
两个消费者并发访问 会有可能产生抢夺资源的问题

  1. 自己解决一下线程安全的问题
  • 让仓库对象被线程访问的时候 仓库对象被锁定;
  • 仓库对象只能被一个线程访问 其他的线程处于等待状态。
  • 使用特征修饰符synchronized,也称之为线程安全锁 , 同步 , 一个时间点只有一个线程访问。
  • 两种形式写法:
    1.将synchronized关键字 放在方法的结构上
    public synchronized void get(){}
    锁定的是调用方法时的那个对象
    2.将synchronized关键字,放在方法(构造方法 块)的内部
    public void get(){好多代码synchronized(对象,写this就行了){好多代码  //只有这句比较重要,只有执行这块代码的时候才锁定对象。}好多代码}

所以修改仓库类:

public class Warehouse {//单例设计模式//仓库里面的集合  存放元素private ArrayList<String> list = new ArrayList<>();//向集合内添加元素的方法public synchronized void add(){if(list.size()<20) {list.add("a");}else{//return;//让方法执行到这里就结束方法}}//从集合内获取元素的方法public synchronized void get(){if(list.size()>0) {list.remove(0);//集合越界的问题}else{//return;try {this.notifyAll();this.wait();//仓库对象调用wait  不是仓库对象等待  访问仓库的消费者线程进入等待状态} catch (InterruptedException e) {e.printStackTrace();}}}
}
  1. 我们觉得return不是很好
    对于生产者,生产发现超过20,不是不在生产了,而是等一会,一会在继续生产,所以直接return不好。
    对于消费者,发现数量为0 的时候,也不是不再取了,而是等一会,一会再取,所以return不好。
  • 应该让线程的不同状态来回切换
    执行 等待 执行 等待
  • wait() Object类中的方法
    对象.wait(); 不是当前的这个对象wait
    是 访问当前这个对象的线程wait
    将return换成如下的代码:
       try {this.wait();//仓库调用wait 不是仓库对象等待  访问仓库的生产者线程进入等待状态} catch (InterruptedException e) {e.printStackTrace();}}

但是单纯这样,会产生一个类似假死状态
所有的线程都进入等待状态 没有线程做事。所以可以使用下面的notify方法。

  • notifynotifyAll Object类中的方法
    代码为:
       try {this.notifyAll();this.wait();//仓库调用wait 不是仓库对象等待  访问仓库的生产者线程进入等待状态。} catch (InterruptedException e) {e.printStackTrace();}}

但是唤醒是将消费者和生产者都唤醒了,生产者和消费者哪一个先开始,我们还是不确定,我们是希望生产者先开始,但是这不是我们能决定的。所以考虑设置优先级,如下:

  • p.setPriority(10); p.getPriority();
public class TestMain {public static void main(String[] args){Warehouse house = new Warehouse();//里面有一个ArrayList线程非安全Producer p = new Producer(house);//设置线程的优先级别1-10p.setPriority(10); //给生产者级别高些,优先抢资源。Consumer c1 = new Consumer(house);Consumer c2 = new Consumer(house);p.start();c1.start();c2.start();}
}

笔试题

  • 程序 进程 线程 概念的区别
  • 线程的创建方式
  • 线程的几种状态 如何切换
  • sleep方法 ; wait方法的区别:
    1.类: Thread类 ;Object类
    2.调用: 静态 类名.对象.
    3.理解: 哪个位置调用 哪个线程等待 ;对象调用方法 访问对象的其他线程等待
    4.唤醒:不需要别人 ;需要其他对象调用notify唤醒
    5.锁:不会释放锁 ;等待后会释放锁

三. join方法&死锁&Timer

  1. join方法:
    设计一个模型
    1.有两个线程 One Two two加入到one里面
    2.设计模型时候 two线程在one的run里面创建 保证两个有先后顺序
    3.two.join(); 无参数==0 有参数==2000

A跑几步,B跑几步,然后遇到独木桥,然后让B先过独木桥。


但是,我们以前只能让线程进入就绪状态,不能控制线程的顺序,现在,我们想让A加入到B线程中,怎么做呢?

线程一:

public class ThreadOne extends Thread {public void run(){System.out.println("thread-one start");ThreadTwo two = new ThreadTwo();two.start();//一线程运行的时候,二线程才创建//所以一线程应该执行的比二线程早,控制了顺序。try {two.join();//线程2加入线程1里面//如果填一个时间,那么一线程就等这么长时间,就执行了,就不管二线程是否执行完毕了//  two.join(2000);} catch (InterruptedException e) {e.printStackTrace();}//一线程等待二线程,二线程休息了5000msSystem.out.println("thread-one end");}
}

线程二:

public class ThreadTwo extends Thread {public void run(){System.out.println("thread-two start");try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("thread-two end");}
}

所以主方法:

public class TestMain {public static void main(String[] args){ThreadOne one = new ThreadOne();one.start();}
}

结果:
thread-one start
thread-two start
thread-two end
thread-one end

在two执行的过程中,one等待的过程中,three将two对象锁定,那么one没能推two,one会怎样呢?会不会一直在那一直等待呢?
Three:

public class ThreadThree extends Thread{private ThreadTwo two;public ThreadThree(ThreadTwo two){this.two=two;}public void run(){//在two执行的过程中  one等待的过程中   three将two对象锁定System.out.println("thread-three start");synchronized(two){System.out.println("two is locked");try {Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("two is free");}System.out.println("thread-three end");}
}

整体过程,one线程先执行,one线程执行的时候建立了two线程,然后遇到独木桥,one让two先执行,two.join(2000),one在这数数,2000ms。two执行需要5000ms,two还没有执行完毕,one的2000ms已经数完了,在one想要推two的时候,发现two被three拿走了锁定了,tow被three老鹰叼走了。所以one没能推two,one就在这一直等着,等到two回来,然后推two一把,才能继续执行。
线程二:

public class ThreadTwo extends Thread {public void run(){System.out.println("thread-two start");ThreadThree three = new ThreadThree(this);three.start();try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("thread-two end");}
}

结果:
one 启动
two 启动
three 启动
two就join啦
2000之后 one想要将two从自己的线程内剔除
发现 two对象不在自己的手里 因为对象被three线程锁定啦 10000
one只能等待 threee将two对象释放后 才能踢掉。
结果:

thread-one start
thread-two start
thread-three start
thread-two locked
thread-two end   two虽然被叼走了,但是实际上还在执行,所以他先执行完毕了因为只需要5000ms就能执行完毕,二别锁定10000ms,所以two可以执行完毕
two is free
thread-three end
thread-one end

synchronized锁 非常的厉害,
一旦对象被锁定 不释放的情况下 其他的对象都需要等待,
有可能会产生一个死锁的效果。

  1. 死锁

    一个小箱子,有俩个元素。两个人都想去拿东西,而且拿两个。可能一人拿了一个,想等着对方释放,但是对方又不释放,产生的现象称之为死锁。

死锁的效果
模拟一个模型 演示死锁。哲学家就餐的问题,上图左侧的桌子。有可能产生死锁,但是也有可能有的人快,就拿到了一双。

筷子只有属性:

public class Chopstick {private int num;public Chopstick(int num){this.num=num;}public int getNum(){return this.num;}
}

哲学家:

public class Philosopher extends Thread{private String pname;//哲学家的名字private Chopstick left;private Chopstick right;//private long time;public Philosopher(String pname,Chopstick left,Chopstick right,long time){this.pname = pname;this.left = left;this.right = right;}public void run(){synchronized (left) {System.out.println(this.pname+"拿起了左手边的"+this.left.getNum()+"筷子");synchronized (right){System.out.println(this.pname+"拿起了右手边的"+this.right.getNum()+"筷子");System.out.println(this.pname+"开始狼吞虎咽的吃起来啦");}}}
}

主方法:

public class TestMain {public static void main(String[] args){Chopstick c1 = new Chopstick(1);Chopstick c2 = new Chopstick(2);Chopstick c3 = new Chopstick(3);Chopstick c4 = new Chopstick(4);Philosopher p1 = new Philosopher("哲学家a",c2,c1);Philosopher p2 = new Philosopher("哲学家b",c3,c2);Philosopher p3 = new Philosopher("哲学家c",c4,c3);Philosopher p4 = new Philosopher("哲学家d",c1,c4);p1.start();p2.start();p3.start();p4.start();}
}
//有可能死锁,一人拿到了一支筷子,但是也有可能不产生死锁问题,有人拿的快,吃到了饭。

想要解决死锁的问题:
1.礼让---->产生时间差
2.不要产生对象公用的问题

哲学家类:

public class Philosopher extends Thread{private String pname;//哲学家的名字private Chopstick left;private Chopstick right;private long time; //上来先睡一会儿public Philosopher(String pname,Chopstick left,Chopstick right,long time){this.pname = pname;this.left = left;this.right = right;this.time = time;}public void run(){try {Thread.sleep(time);} catch (InterruptedException e) {e.printStackTrace();}synchronized (left) {System.out.println(this.pname+"拿起了左手边的"+this.left.getNum()+"筷子");synchronized (right){System.out.println(this.pname+"拿起了右手边的"+this.right.getNum()+"筷子");System.out.println(this.pname+"开始狼吞虎咽的吃起来啦");}}}
}

主方法:

public class TestMain {public static void main(String[] args){Chopstick c1 = new Chopstick(1);Chopstick c2 = new Chopstick(2);Chopstick c3 = new Chopstick(3);Chopstick c4 = new Chopstick(4);Philosopher p1 = new Philosopher("哲学家a",c2,c1,0);Philosopher p2 = new Philosopher("哲学家b",c3,c2,3000);Philosopher p3 = new Philosopher("哲学家c",c4,c3,0);Philosopher p4 = new Philosopher("哲学家d",c1,c4,3000);//对面的人可以同时吃,所以可以设置睡觉时间相同。p1.start();p2.start();p3.start();p4.start();}
}

java学习基础课之线程(渡一教育)(十七)相关推荐

  1. java学习笔记15--多线程编程基础2

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note15.html,转载请注明源地址. 线程的生命周期 1.线程的生命周期 线程从产生到消亡 ...

  2. java学习基础课(渡一教育)(一)

    记录自己看渡一教育免费java基础课的学习过程. 第四课:基本数据类型 数据类型:分为基本数据类型和引用数据类型 基本数据类型 8个 4整型 byte short int long64 2浮点型 fl ...

  3. java学习基础课之数组(渡一教育)(二)

    记录自己看渡一教育免费java基础课的学习过程. int score = 90; 根据score成绩来进行区间的判定 不及格 及格 中等 良好 优秀 满分- score是一个变量空间(小容器)-一个学 ...

  4. java学习基础课之面向对象(渡一教育)【属性;方法;重载】(三)

    文章目录 一.面向对象之属性 二.面向对象之方法 三.形参实参 四.重载 记录自己看渡一教育免费java基础课的学习过程. 面向过程的编程思想 VS 面向对象的编程思想: 面向过程的编程思想 解决问题 ...

  5. java学习笔记14--多线程编程基础1

    本文地址:http://www.cnblogs.com/archimedes/p/java-study-note14.html,转载请注明源地址. 多线程编程基础 多进程 一个独立程序的每一次运行称为 ...

  6. java学习基础课之面向对象(渡一教育)【继承】(五)

    文章目录 一.继承 is-a 包含 has-a 依赖关系 use-a(need-a) 类和类之间的关系 A is-a B:泛化(继承 实现) A has-a B: 包含(组合 聚合 关联) A use ...

  7. java学习基础课之枚举(渡一教育)(八)

    数据类型 基本:8个 引用:数组[] :类class:抽象类abstract:接口interface: 枚举enum :注解@interface 一.枚举类 一个类中的对象,认为个数是有限且固定的,可 ...

  8. 0040 Java学习笔记-多线程-线程run()方法中的异常

    run()与异常 不管是Threade还是Runnable的run()方法都没有定义抛出异常,也就是说一条线程内部发生的checked异常,必须也只能在内部用try-catch处理掉,不能往外抛,因为 ...

  9. 【Java学习笔记】线程学习笔记

    一.资源 http://blog.csdn.net/axman/article/details/431796 这个博客里有Java多线程.线程池的一系列,从基础开始就很清楚,牛人 二.重点 今天读到了 ...

最新文章

  1. Learn Python 011: while loop
  2. 活动延期通知 | 7.31 阿里云 Serverless Developer Meetup 杭州站本周六见!
  3. 树莓派超声波模块测距
  4. MATLAB凸包Convex hull运算
  5. gsm模块网站服务器,gsm模块是什么_gsm模块工作原理_gsm模块的应用
  6. springMVC 前后台日期格式传值解决方式之二(共二) @InitBinder的使用
  7. 漫步微积分十——复合函数和链式法则
  8. 《Oracle数据库管理与维护实战》——1.2 Oracle各版本异同
  9. LibreELEC(kodi)基本设置
  10. 2014春节抢票经验记录
  11. 微信小程序账号注册初始化环境搭建
  12. LAMP系列文章之一:泛泛而谈LAMP
  13. 百度Echarts设置markPoint展示样式
  14. /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵
  15. [2016 版] 常见操作性能对比
  16. ML之FE:风控场景之金融评分卡模型之利用LoR模型权重变量系数正负符号结合p-value/P值大小实现变量筛选
  17. 【navicat 密码查看】小技巧navicat 如何查看密码
  18. python手机屏幕控制_用Python控制墨水屏
  19. 电子邮箱注册格式是什么?电子邮箱格式怎么写?
  20. Kaggle系列(1)——Titanic

热门文章

  1. myplay.pif、winsys16.dll、scrsys16.dll和AlxRes.exe的分析与解决(ZT)
  2. 如何测量服务器的带宽
  3. 程序员必看的数组详解!
  4. GIS二次开发实习——鹰眼功能模块的实现(鹰眼锁定不能动,红框与主地图联动)
  5. MySQL第七讲:MySQL分库分表详解
  6. 米家扫地机器人怎么加水_扫地拖地一次就好,米家扫拖机器人1C体验
  7. asp实现注册登录界面_(06)ASP登录页面的设计思路
  8. WAS集群:记一次Node Agent不活动问题解决过程
  9. SQL Server中的null(没有值,空值)
  10. What is null?