1.理解程序、进程、线程的概念

程序可以理解为静态的代码
进程可以理解为执行中的程序。
线程可以理解为进程的进一步细分,程序的一条执行路径

每个Java程序都有一个隐含的主线程: main 方法

使用多线程的优点:

Thread的常用方法:

/** Thread的常用方法:* 1.start():启动线程并执行相应的run()方法* 2.run():子线程要执行的代码放入run()方法中* 3.currentThread():静态的,调取当前的线程* 4.getName():获取此线程的名字* 5.setName():设置此线程的名字* 6.yield():调用此方法的线程释放当前CPU的执行权* 7.join():在A线程中调用B线程的join()方法,表示:当执行到此方法,A线程停止执行,直至B线程执行完毕,* A线程再接着join()之后的代码执行* 8.isAlive():判断当前线程是否还存活* 9.sleep(long l):显式的让当前线程睡眠l毫秒* 10.线程通信:wait()   notify()  notifyAll()* * 设置线程的优先级* getPriority() :返回线程优先值 setPriority(int newPriority) :改变线程的优先级*/
class SubThread1 extends Thread {public void run() {for (int i = 1; i <= 100; i++) {// try {// Thread.currentThread().sleep(1000);// } catch (InterruptedException e) {// // TODO Auto-generated catch block// e.printStackTrace();// }System.out.println(Thread.currentThread().getName() + ":"+ Thread.currentThread().getPriority() + ":" + i);}}
}public class TestThread1 {public static void main(String[] args) {SubThread1 st1 = new SubThread1();st1.setName("子线程1");st1.setPriority(Thread.MAX_PRIORITY);st1.start();Thread.currentThread().setName("========主线程");for (int i = 1; i <= 100; i++) {System.out.println(Thread.currentThread().getName() + ":"+ Thread.currentThread().getPriority() + ":" + i);// if(i % 10 == 0){// Thread.currentThread().yield();// }// if(i == 20){// try {// st1.join();// } catch (InterruptedException e) {// // TODO Auto-generated catch block// e.printStackTrace();// }// }}System.out.println(st1.isAlive());}
}

线程的开启过程:

2.如何创建java程序的线程(重点)

方式一:继承于Thread类

class PrintNum extends Thread{public void run(){//子线程执行的代码for(int i = 1;i <= 100;i++){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ":" + i);}}}public PrintNum(String name){super(name);}
}public class TestThread {public static void main(String[] args) {PrintNum p1 = new PrintNum("线程1");PrintNum p2 = new PrintNum("线程2");p1.setPriority(Thread.MAX_PRIORITY);//10p2.setPriority(Thread.MIN_PRIORITY);//1p1.start();p2.start();}
}

方式二:实现Runnable接口

class SubThread implements Runnable{public void run(){//子线程执行的代码for(int i = 1;i <= 100;i++){if(i % 2 == 0){System.out.println(Thread.currentThread().getName() + ":" + i);}}           }
}
public class TestThread{public static void main(String[] args){SubThread s = new SubThread();Thread t1 = new Thread(s);Thread t2 = new Thread(s);t1.setName("线程1");t2.setName("线程2");t1.start();t2.start();}
}

两种方式的对比:
哪个比较好?实现的方式较好。class Thread implements Runnable
①解决了单继承的局限性。②如果多个线程有共享数据的话,建议使用实现方式,同时,共享数据所在的类可以作为Runnable接口的实现类。

联系:

继承的方式其实也是实现的Runnable接口。

线程里的常用方法:

start() run() currentThread() getName() setName(String name) yield() join() sleep() isAlive() getPriority() setPriority(int i); wait() notify() notifyAll()

补充:线程的分类
Java中的线程分为两类:一种是守护线程,一种是用户线程。

  • 它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。
  • 守护线程是用来服务用户线程的,通过在start()方法前调用thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
  • Java垃圾回收就是一个典型的守护线程。
  • 若JVM中都是守护线程,当前JVM将退出。

3.线程的生命周期

4.线程的同步机制(重点、难点)

前提:如果我们创建的多个线程,存在着共享数据,那么就有可能出现线程的安全问题:当其中一个线程操作共享数据时,还未操作完成,另外的线程就参与进来,导致对共享数据的操作出现问题。
解决方式:要求一个线程操作共享数据时,只有当其完成操作完成共享数据,其它线程才有机会执行共享数据。

方式一:
同步代码块:
synchronized(同步监视器){
//操作共享数据的代码
}

注:1.同步监视器:俗称锁,任何一个类的对象都可以才充当锁。要想保证线程的安全,必须要求所有的线程共用同一把锁!
2.使用实现Runnable接口的方式创建多线程的话,同步代码块中的锁,可以考虑是this。如果使用继承Thread类的方式,慎用this!
3.共享数据:多个线程需要共同操作的变量。 明确哪部分是操作共享数据的代码。

方式二:
同步方法:将操作共享数据的方法声明为synchronized。
比如:public synchronized void show(){ //操作共享数据的代码}

注:1.对于非静态的方法而言,使用同步的话,默认锁为:this。如果使用在继承的方式实现多线程的话,慎用!
2.对于静态的方法,如果使用同步,默认的锁为:当前类本身。以单例的懒汉式为例。 Class clazz = Singleton.class

总结: 释放锁:wait();
不释放锁: sleep() yield() suspend() (过时,可能导致死锁)

1.同步机制例题:售票

//使用实现Runnable接口的方式,售票
/** 此程序存在线程的安全问题:打印车票时,会出现重票、错票* 1.线程安全问题存在的原因?*   由于一个线程在操作共享数据过程中,未执行完毕的情况下,另外的线程参与进来,导致共享数据存在了安全问题。*   * 2.如何来解决线程的安全问题?*   必须让一个线程操作共享数据完毕以后,其它线程才有机会参与共享数据的操作。* * 3.java如何实现线程的安全:线程的同步机制*      *      方式一:同步代码块*      synchronized(同步监视器){*          //需要被同步的代码块(即为操作共享数据的代码)*      }*      1.共享数据:多个线程共同操作的同一个数据(变量)*      2.同步监视器:由一个类的对象来充当。哪个线程获取此监视器,谁就执行大括号里被同步的代码。俗称:锁*      要求:所有的线程必须共用同一把锁!*      注:在实现的方式中,考虑同步的话,可以使用this来充当锁。但是在继承的方式中,慎用this!* *      方式二:同步方法*      将操作共享数据的方法声明为synchronized。即此方法为同步方法,能够保证当其中一个线程执行*      此方法时,其它线程在外等待直至此线程执行完此方法。*      >同步方法的锁:this* * 4.线程的同步的弊端:由于同一个时间只能有一个线程访问共享数据,效率变低了。* */class Window4 implements Runnable {int ticket = 100;// 共享数据public void run() {while (true) {show();}}public synchronized void show() {if (ticket > 0) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "售票,票号为:"+ ticket--);}}
}public class TestWindow4 {public static void main(String[] args) {Window4 w = new Window4();Thread t1 = new Thread(w);Thread t2 = new Thread(w);Thread t3 = new Thread(w);t1.setName("窗口1");t2.setName("窗口2");t3.setName("窗口3");t1.start();t2.start();t3.start();}
}

2.懒汉式改进(加入同步机制)

//关于懒汉式的线程安全问题:使用同步机制
//对于一般的方法内,使用同步代码块,可以考虑使用this。
//对于静态方法而言,使用当前类本身充当锁。
class Singleton {private Singleton() {}private static Singleton instance = null;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}public class TestSingleton {public static void main(String[] args) {Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);// Class clazz = Singleton.class;}
}

死锁:
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
死锁是我们在使用同步时,需要避免的问题!

1.死锁的简单例子:

//死锁的问题:处理线程同步时容易出现。
//不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
//写代码时,要避免死锁!
public class TestDeadLock {static StringBuffer sb1 = new StringBuffer();static StringBuffer sb2 = new StringBuffer();public static void main(String[] args) {new Thread() {public void run() {synchronized (sb1) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}sb1.append("A");synchronized (sb2) {sb2.append("B");System.out.println(sb1);System.out.println(sb2);}}}}.start();new Thread() {public void run() {synchronized (sb2) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}sb1.append("C");synchronized (sb1) {sb2.append("D");System.out.println(sb1);System.out.println(sb2);}}}}.start();}}

5.线程的通信

wait() 与 notify() 和 notifyAll()

  • wait():令当前线程挂起并放弃CPU、同步资源,使别的线程可访问并修改共享资源,而当前线程排队等候再次对资源的访问
  • notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
  • notifyAll ():唤醒正在排队等待资源的所有线程结束等待.

Java.lang.Object提供的这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常

如下的三个方法必须使用在同步代码块或同步方法中!
wait():当在同步中,执行到此方法,则此线程“等待”,直至其他线程执行notify()的方法,将其唤醒,唤醒后继续其wait()后的代码
notify()/notifyAll():在同步中,执行到此方法,则唤醒其他的某一个或所有的被wait的线程。

例题:1.两个线程交替打印1-100自然数 2.生产者、消费者的例子

两个线程交替打印1-100自然数

//线程通信。如下的三个关键字使用的话,都得在同步代码块或同步方法中。
//wait():一旦一个线程执行到wait(),就释放当前的锁。
//notify()/notifyAll():唤醒wait的一个或所有的线程
//使用两个线程打印 1-100. 线程1, 线程2 交替打印class PrintNum implements Runnable {int num = 1;Object obj = new Object();public void run() {while (true) {synchronized (obj) {obj.notify();if (num <= 100) {try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + ":"+ num);num++;} else {break;}try {obj.wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}public class TestCommunication {public static void main(String[] args) {PrintNum p = new PrintNum();Thread t1 = new Thread(p);Thread t2 = new Thread(p);t1.setName("甲");t2.setName("乙");t1.start();t2.start();}
}

生产者、消费者问题:

/** 生产者/消费者问题* 生产者(Productor)将产品交给店员(Clerk),而消费者(Customer)从店员处取走产品,* 店员一次只能持有固定数量的产品(比如:20),如果生产者试图生产更多的产品,店员会叫生产者停一下,* 如果店中有空位放产品了再通知生产者继续生产;如果店中没有产品了,店员会告诉消费者等一下,* 如果店中有产品了再通知消费者来取走产品。分析:1.是否涉及到多线程的问题?是!生产者、消费者2.是否涉及到共享数据?有!考虑线程的安全3.此共享数据是谁?即为产品的数量4.是否涉及到线程的通信呢?存在这生产者与消费者的通信*/
class Clerk{//店员int product;public synchronized void addProduct(){//生产产品if(product >= 20){try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}else{product++;System.out.println(Thread.currentThread().getName() + ":生产了第" + product + "个产品");notifyAll();}}public synchronized void consumeProduct(){//消费产品if(product <= 0){try {wait();} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}else{System.out.println(Thread.currentThread().getName() + ":消费了第" + product + "个产品");product--;notifyAll();}}
}class Producer implements Runnable{//生产者Clerk clerk;public Producer(Clerk clerk){this.clerk = clerk;}public void run(){System.out.println("生产者开始生产产品");while(true){try {Thread.currentThread().sleep(100);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.addProduct();}}
}
class Consumer implements Runnable{//消费者Clerk clerk;public Consumer(Clerk clerk){this.clerk = clerk;}public void run(){System.out.println("消费者消费产品");while(true){try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}clerk.consumeProduct();}}
}public class TestProduceConsume {public static void main(String[] args) {Clerk clerk = new Clerk();Producer p1 = new Producer(clerk);Consumer c1 = new Consumer(clerk);Thread t1 = new Thread(p1);//一个生产者的线程Thread t3 = new Thread(p1);Thread t2 = new Thread(c1);//一个消费者的线程t1.setName("生产者1");t2.setName("消费者1");t3.setName("生产者2");t1.start();t2.start();t3.start();}
}

另加:菜鸟教程 Java多线程编程

Java多线程的使用:

有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。

通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。

请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU 花费在上下文的切换的时间将多于执行程序的时间!

线程池:

1、线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。(是什么)

2、那么,我们为什么需要用到线程池呢?每次用的时候手动创建不行吗?

在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或”切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。(为什么)

线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快;另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。(什么用)

3、线程池都是通过线程池工厂创建,再调用线程池中的方法获取线程,再通过线程去执行任务方法。

4、这里介绍两种使用线程池创建线程的方法

  • 1):使用Runnable接口创建线程池
  • 2)使用Callable接口创建线程池

第十一章:Java_多线程相关推荐

  1. note-PythonCookbook-第十一章 网络与WEB编程

    第十一章 网络与WEB编程 11.1 作为客户端与HTTP服务交互 requests模块 11.2 创建 TCP 服务器 简单的应答服务器 from socketserver import BaseR ...

  2. 速学堂(java)第十一章编程题答案(自写)

    速学堂(java)第十一章编程题答案(自写) 1.设计一个多线程的程序如下:设计一个火车售票模拟程序.假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况 ...

  3. [swift 进阶]读书笔记-第十一章:互用性 C11P1 实践:封装 CommonMark

    第十一章:互用性 Interoperability 前言: swift 的最大优点就是与C 或者 OC 混编的时候稳的一匹 本章主要讲了swift和C之间的一些知识点. 11.1 实践:封装 Comm ...

  4. 【译】 WebSocket 协议第十一章——IANA 注意事项(IANA Considerations)

    概述 本文为 WebSocket 协议的第十一章,本文翻译的主要内容为 WebSocket 的 IANA 相关注意事项. IANA 注意事项(协议正文) 11.1 注册新 URI 协议 11.1.1 ...

  5. java语言仅支持单重继承_java语言程序设计基础篇习题_复习题_第十一章

    java语言程序设计基础篇习题_复习题_第十一章 11.1 下面说法是真是假?一个子类是父类的子集. 11.2 使用什么关键字来定义一个子类 11.3 什么是单一继承?什么是多重继承?java支持多重 ...

  6. 《javascript设计模式》笔记之第十章 和 第十一章:门面模式和适配器模式

    第十章:门面模式 一:门面模式的作用 简化已有的api,使其更加容易使用 解决浏览器的兼容问题 二:门面模式的本质 门面模式的本质就是包装已有的api来简化操作   三:门面模式的两个简单例子 下面这 ...

  7. 计算机网络离不开光缆,九年级物理全册 第二十一章 第四节 越来越宽的信息之路习题课件 新人教版.ppt...

    九年级物理全册 第二十一章 第四节 越来越宽的信息之路习题课件 新人教版.ppt 第二十一章信息的传递,第四节越来越宽的信息之路,1微波的性质更接近光波,大致沿_______传播,需要每隔_____k ...

  8. 开发日记-20190915 关键词 汇编语言王爽版 第十一章

    第十一章 标志寄存器 CPU内部的寄存器中,有一种特殊的寄存器(对于不同的处理器,个数和结构都可能不同)具有以下三种作用. (1)用来存储相关的指令的某些执行结果 (2)用来为CPU执行相关的指令提供 ...

  9. 羊皮卷的实践-第二十一章

    第二十一章 高山滑雪是人与环境以及时间的竞赛.每当我看到输赢之间只差极短的时间时,我就不禁摇头同情那些输家. 第一名的时间是一分三十七秒二二. 第二名的时间是一分二十七秒二五. 也就是说,冠军与平庸之 ...

  10. [转]Windows Shell 编程 第十一章 【来源:http://blog.csdn.net/wangqiulin123456/article/details/7987992】...

    第十一章 探索Shell 我们现在将注意力从API转向Windows Shell本身.从这一章往后,我们的目标主要集中在清晰和全面的揭示探测器的工作原理和Shell 命名空间的构成对象上,最后给出客户 ...

最新文章

  1. android 设置activity启动退出动画 | 解决设置activity 动画不生效问题
  2. Java黑皮书课后题第1章:1.5(计算表达式)编写程序,显示以下式子的结果
  3. linux多级菜单脚本教程,linux shell 编写菜单脚本事例
  4. 如何安装MiniGUI 3.0在Linux PC
  5. vb与三菱plc以太网通讯_实战演练|三菱触摸屏GOT2000与三菱Q系列以太网通讯
  6. fastreport 横向分栏_FastReport开发指南
  7. 全国计算机二级c语言答案,全国计算机二级C语言试题及答案
  8. 由于芯片短缺 通用将生产部分无自动启停功能新车
  9. mysql5.6.38占用内存过大问题解决
  10. 苹果Mac图片清晰度增强软件:Topaz Sharpen AI
  11. python人脸照片分类_Python系列之三——人脸检测、人脸识别
  12. 去除暴风影音2009广告的方法!
  13. 将excel或是csv文件中的矩阵相乘,输出点乘后的excel文件
  14. VBA checkbook动态添加后,添加事件方式
  15. java特殊字符大全_java 字符串特殊符号
  16. 2019年春季学期《C语言程序设计II》助教注意事项
  17. SSM | Spring
  18. linux常用命令课堂总结
  19. 互联网P2P优化管理标准体系研讨会召开
  20. jzoj 2867. 【集训队互测 2012】Contra

热门文章

  1. CORS 实战 专题
  2. 你真的在正确地使用WLAN控制器吗?
  3. YII中session和cookie
  4. CGImageRef 图片压缩 裁减
  5. 如何设置SSH访问的时间限制
  6. The configured Task Off-Heap Memory 0 bytes is less than the least required Python worker Memory 79
  7. 無法在 module_path 中找出佈景主題引擎:‘clearlooks’,
  8. 第1章-什么是深度学习(笔记)
  9. 为什么“极大似然估计表达式的极值”可以用来估计参数
  10. ubuntu搜狗输入法突然无法使用(新解)