------- android培训、java培训、期待与您交流! ----------

前言:通过观看毕向东老师的java基础视频,查漏补缺,将一些自己掌握的还不牢固的知识写出来,希望和大家交流分享。

一、进程与线程

进程:正在执行的程序。每一个进程执行都有一个执行顺序。该顺序是一个执行路径,或者叫一个控制单元。

线程:把进程中的功能分成各个部分,每个部分有一个线程控制。它是进程中的一个独立的控制单元。

多线程:在一个进程中有多个线程执行的方式,就叫做多线程。如在java虚拟机启动的时候会有一个java.exe的执行程序,也就是一个进程。该进程中至少有一个线程负责java程序的执行。而且这个线程运行的代码存在于main方法中。该线程称之为主线程。JVM启动除了执行一个主线程,还有负责垃圾回收机制的线程。

多线程的意义:多线程的出现能让程序产生同时运行效果。可以提高程序执行效率。

二、创建线程的方式

两种方法:继承Thread类和实现Runnable接口(建议使用,因为java是单继承,多实现,一旦继承Thread类后就不能继承其他类了)。

1)继承方式步骤:

1.定义类继承Thread。

2.复写Thread中的run方法。 目的:将自定义代码存储在run方法中,让线程运行。

3.创建定义类的实例对象。相当于创建一个线程。

4.用该对象调用线程的start方法。该方法的作用是:启动线程,调用run方法。

如果对象直接调用run方法,等同于只有一个线程在执行,自定义的线程并没有启动。

代码示例:

package thread;/*** 用继承的方式创建线程* @author songwenju**/
class Test extends Thread{public Test(String name) {super(name);}//覆写run方法@Overridepublic void run() {//打印线程内部信息,当执行输出20次后线程结束。for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+"---->"+i);}}
}public class ThreadByExtend{public static void main(String[] args) {//开启一个线程new Test("线程1").start();//开启第二个线程new Test("线程2").start();//打印主线程的信息for (int i = 0; i < 20; i++) {System.out.println("主线程---->"+i);}}
}

运行结果:

执行是随机、交替执行的,每一次运行的结果都会不同

2)实现方式步骤:

1.定义类实现Runnable的接口。

2.覆盖Runnable接口中的run方法。目的也是为了将线程要运行的代码存放在该run方法中。

3.通过Thread类创建线程对象。

4.将Runnable接口的子类对象作为实参传递给Thread类的构造方法。

5.调用Thread类中start方法启动线程。start方法会自动调用Runnable接口子类的run方法。

代码示例:

package thread;/*** 实现Runnable接口来创建线程类* @author songwenju**/
class Ticket implements Runnable{private int num = 20;//实现run方法@Overridepublic void run() {while (true) {if (num > 0) {System.out.println(Thread.currentThread().getName()+"卖出第————>"+num--+"张票");}else {break;}}}
}public class ThreadByImplement {public static void main(String[] args) {//定义一个线程对象。Ticket ticket = new Ticket();//开启三个线程:使用Thread(Runnable target, String name)这个构造方法分配新的 Thread 对象。new Thread(ticket,"窗口一").start();new Thread(ticket,"窗口二").start();new Thread(ticket,"窗口三").start();}
}

结果:

三、线程的状态
        被创建:等待启动,调用start启动。
        运行状态:具有执行资格和执行权。
        临时状态(阻塞):有执行资格,但是没有执行权。
        冻结状态:遇到sleep(time)方法和wait()方法时,失去执行资格和执行权,sleep方法时间到或者调用notify()方法时,获得执行资格,变为临时状态。
        消忙状态:stop()方法,或者run方法结束。
注:当已经从创建状态到了运行状态,再次调用start()方法时,就失去意义了,java运行时会提示线程状态异常。

图示:

四、线程的安全
      1.导致线程出现安全问题的原因:
         当多条语句在操作同一线程共享数据时,一个线程对多条语句只执行了一部分,还没用执行完,另一个线程参与进来执行。导致共享数据的错误。
         简单来说:a、多个线程访问出现延迟。b、线程随机性
      2.解决办法——线程同步:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他线程不可以参与执行。
        使用synchronized(同步)关键字
         同步有两种方式:同步代码块和同步函数
        a、同步代码块
            格式:
            synchronized(监听类对象){
                    需要被同步的代码
              }
            同步可以解决安全问题的根本原因就在那个对象上,其中对象如同锁。持有锁的线程可以在同步中执行。没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。
          代码示例:

package thread;/*** 为run方法加同步代码块* @author songwenju**/
class Ticket implements Runnable{private int num = 50;//实现run方法@Overridepublic void run() {while (true) {synchronized (Ticket.class) {if (num > 0) {try {Thread.sleep(10);} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖出第————>"+num--+"张票");}else {break;}}}}
}public class ThreadByImplement {public static void main(String[] args) {//定义一个线程对象。Ticket ticket = new Ticket();//开启三个线程:使用Thread(Runnable target, String name)这个构造方法分配新的 Thread 对象。new Thread(ticket,"窗口一").start();new Thread(ticket,"窗口二").start();new Thread(ticket,"窗口三").start();}
}

b、同步函数

格式: 在函数上加上synchronized修饰符即可。

同步函数用的是哪一个锁呢?

函数需要被对象调用。那么函数都有一个所属对象引用。就是this。所以同步函数使用的锁是this。

代码示例:

package thread;/*** 为run方法加同步代码块* @author songwenju**/
class Ticket2 implements Runnable{private int num = 50;//实现run方法@Overridepublic void run() {for (int i = 0; i < 1000; i++) {sale();}}public synchronized void  sale(){if (num > 0) {try {Thread.sleep(10);} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName()+"卖出第————>"+num--+"张票");}}
}public class ThreadDemo2 {public static void main(String[] args) {//定义一个线程对象。Ticket2 ticket = new Ticket2();//开启三个线程:使用Thread(Runnable target, String name)这个构造方法分配新的 Thread 对象。new Thread(ticket,"窗口一").start();new Thread(ticket,"窗口二").start();new Thread(ticket,"窗口三").start();}
}

3.注意事项:

a.同步的前提

1)必须要有两个或者两个以上的线程。

2)必须是多个线程使用同一个锁。

b.同步的利弊

好处:解决了多线程的安全问题。

弊端:多个线程需要判断锁,较为消耗资源。

c.如何寻找多线程中的安全问题

1)明确哪些代码是多线程运行代码。

2)明确共享数据。

3)明确多线程运行代码中哪些语句是操作共享数据的。

五、静态函数的同步方式

如果同步函数被静态修饰后,使用的锁是什么呢?

通过验证,发现不在是this。因为静态方法中不可以定义this。静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。

如:类名.class 该对象的类型是Class

这就是静态函数所使用的锁。而静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class

经典示例:

package thread;/*** 加同步的单例模式——懒汉式* @author songwenju**/
public class Single {private static Single s = null;private Single(){}public static Single getInstance() {if (s == null) {synchronized (Single.class) {if (s == null) {s = new Single();}}}return s;}
}

六、死锁

当同步中嵌套同步时,就有可能出现死锁现象。

代码示例:

package thread;/*** 一个死锁程序* @author songwenju**///定义一个类来实现Runnable,并覆写run方法。
class LockTest implements Runnable{private boolean flag;public LockTest(boolean flag) {this.flag = flag;}@Overridepublic void run() {if (flag) {while (true) {synchronized (LockClass.locka) {System.out.println(Thread.currentThread().getName()+"--->if_locka");  synchronized (LockClass.lockb) {System.out.println(Thread.currentThread().getName()+"--->if_lockb");  }}}}else {while (true) {synchronized (LockClass.lockb) {System.out.println(Thread.currentThread().getName()+"--->else_lockb");    synchronized (LockClass.locka) {System.out.println(Thread.currentThread().getName()+"--->else_locka");    }}}}}}//定义两个锁
class LockClass{static Object locka = new Object();static Object lockb = new Object();
}
public class DeadLock {public static void main(String[] args) {//创建2个进程,并启动  new Thread(new LockTest(true),"线程1").start();new Thread(new LockTest(false),"线程2").start();}
}

运行结果:程序卡住,不能继续执行

七、线程间通信

多个线程在操作同一个资源,但是操作的动作不同。

使用wait(),notify(),notifyAll()这几个方法来实现线程的通信。

代码示例:生产者与消费者问题

package thread;/*** 生产者与消费者问题* 假设有一个容器* 如果里面为空时,一个线程负责生产资源* 如果不为空,一个线程负责消费资源* @author songwenju**///资源
class Resource{private String name;private String weight;//为true时表示有值可取,为false时表示需要新值放入。private boolean flag = false;public synchronized void setInput(String name, String weight){if (flag) {try {wait();//如果有资源,等待被消费} catch (Exception e) {e.printStackTrace();}}this.name = name;this.weight = weight;System.out.println("生产者生产了"+name + ",重量为:"+weight);flag = true;//表示有资源notify();//唤醒等待}public synchronized void getOutput(){if (!flag) {try {wait();//如果没有资源,等待被生产} catch (Exception e) {e.printStackTrace();}}System.out.println("消费者消费了 "+name +",重量 为:"+weight);flag = false;//表示资源被消费notify();//唤醒等待}}
//控制生产者的线程
class Input implements Runnable{private Resource r;public Input(Resource r) {this.r = r;}@Overridepublic void run() {int x = 0;for (int i = 0; i < 100; i++) {if (x == 0) {r.setInput("牛奶", "10kg");}else {r.setInput("面板", "20kg");}x = (x+1)%2; //控制交替打印}}
}
//控制消费者的线程
class Output implements Runnable{private Resource r;public  Output(Resource r) {this.r = r;}@Overridepublic void run() {while (true) {r.getOutput();}}
}public class ResourceDemo {public static void main(String[] args) {Resource r = new Resource();       //表示操作的是同一个资源new Thread(new Input(r)).start();  //开启生产者线程new Thread(new Output(r)).start(); //开启消费者线程}
}

执行结果:

几个问题:

1)wait(),notify(),notifyAll(),用来操作线程为什么定义在了Object类中?

a,这些方法存在与同步中。

b,使用这些方法时必须要标识所属的同步的锁。同一个锁上wait的线程,只可以被同一个锁上的notify唤醒。

c,锁可以是任意对象,所以任意对象调用的方法一定定义Object类中。

2)wait(),sleep()有什么区别?

wait():释放cpu执行权,释放锁。

sleep():释放cpu执行权,不释放锁。

3)为甚么要定义notifyAll?

因为在需要唤醒对方线程时。如果只用notify,容易出现只唤醒本方线程的情况。导致程序中的所以线程都等待。

八、join方法

当A线程执行到了b线程的.join()方法时,A线程就会等待,等B线程都执行完,A线程才会执行。(此时B和其他线程交替运行。)join可以用来临时加入线程执行。

九、setPriority()方法用来设置优先级

MAX_PRIORITY 最高优先级10

MIN_PRIORITY   最低优先级1

NORM_PRIORITY 分配给线程的默认优先级

十、yield()方法可以暂停当前线程,让其他线程执行。

------- android培训、java培训、期待与您交流! ----------

黑马程序员——java的多线程相关推荐

  1. 黑马程序员——java基础---多线程(二)

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! -------  线程间的通信:简单来说,就是多个线程在操作同一资源,但操作的动作不同. 试想一下,对于同一个资 ...

  2. 黑马程序员——Java基础---多线程

    ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- 多线程 一.概述 说起多线程,我们就需要首先来谈谈什么叫做进程.所谓进程,就是在计算机上正在进行 ...

  3. 黑马程序员JAVA基础-多线程

    ------- android培训.java培训.期待与您交流! ---------- 线程: 线程是进程中的一个独立的控制单元 线程在控制着进程的执行 一个进程中至少有一个线程 线程的创建 第一种方 ...

  4. 黑马程序员---java基础------------------多线程

    线程:就是进程中的一个独立的控制单元.线程在控制着进程的执行. 线程的创建 一:继承Thread类.步骤: 1,定义类继承Thread. 2,复写Thread类中的run方法.      目的:将自定 ...

  5. 黑马程序员Java零基础视频教程_下部(P135-P200)

    黑马程序员Java零基础视频教程_下部(P135-P200) 1 多线程 1.1 什么是多线程? 1.2 多线程的并发与并行 1.3 多线程的实现方式 1.3.1 继承Thread类的方式进行实现 1 ...

  6. 黑马 程序员——Java基础---流程控制

    黑马程序员--Java基础---流程控制 ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------ 一.概述 Java提供了两种基本的流程控制结构:分支结构 ...

  7. 黑马 程序员——Java基础---IO(下)

    黑马程序员--Java基础---IO(下) ------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------ 一.概述 Java除了基本的字节流.字符流之外,还提供 ...

  8. 2023年黑马程序员Java学习路线图

    2023年Java学科免费学习资源放送40+套课程,超过600小时的内容! 在过去的一年里,为了帮助更多人线上充电,学会更多技能,黑马程序员举办了 150+ 场免费直播公开课,新增精品视频教程 80+ ...

  9. 黑马程序员Java教程学习笔记(五)

    学习视频:https://www.bilibili.com/video/BV1Cv411372m 如侵权,请私信联系本人删除 文章目录 黑马程序员Java教程学习笔记(五) 日期时间:Date.Sim ...

最新文章

  1. asp.net 安装element ui_Vue组件库系列三:打造属于自己的 UI 库文档(新版本的方案)...
  2. C#调用非托管C++DLL:通过托管C++DLL间接调用
  3. linux设备驱动之USB主机控制器驱动分析
  4. python使用需要钱吗-万能的Python竟然算不明白“钱”?损失惨重!
  5. 本地java判断zk节点是否存在
  6. Beam概念学习系列之Pipeline Runners
  7. 在浏览器控制台输出内容 console.log(string);
  8. Intellij IDEA社区版集成Maven插件
  9. Mysql学习总结(21)——MySQL数据库常见面试题
  10. 2种方式(线程间通信/互斥锁)实现两个线程,一个线程打印1-52,另一个线程打印字母A-Z,打印顺序为12A34B56C......5152Z...
  11. DiskCatalogMaker for Mac(磁盘管理工具)
  12. 苹果计算机磁盘格式,苹果电脑上怎么进行格式化磁盘?
  13. 有什么软件测试固态硬盘,SSD差距有多大?两款主流NVMe固态硬盘测试,一看就包懂...
  14. kears编写CNN网络,实现对mnist的识别
  15. 支持多种登录模式的token方案设计(微信、支付宝登录等)和数据库设计含手机号绑定方法
  16. 深度:融360还是一家有价值的公司吗?
  17. 软件测试mysql数据库相关操作
  18. matlab rti dds,[译]*RTI_DDS测试
  19. 未来10年计算机专业会不会淘汰,未来10年不会“被淘汰”的4个专业,发展潜力较大,就业前景可观...
  20. 条形码录入测试软件,条码管理:商品条码录入

热门文章

  1. 疑难杂症:申请点内存为何这么耗时
  2. 全面加速ADSL宽带速度
  3. 帆软报表相关问题汇总
  4. 程序员们的爱情表白书
  5. 计算机毕业设计ssm基于Javaweb的宠物救助网站s6a19系统+程序+源码+lw+远程部署
  6. 201912-3化学方程式
  7. 《IT 专业应该怎么学》
  8. A. Stones---水题
  9. python Lock.acquire(0)的作用
  10. CDN加速的工作原理是什么