多线程
一、 什么是线程
线程是程序的最小单位,相对独立可调配的单元,是CPU最小的基本单位
在同一个程序不同的线程完成功能,称为多线程,软件中最小的单位就是线程
二、线程、 进程 、程序之间的关系
程序 :程序就是一段静态代码,是应用程序的蓝本
进程:进程就是程序的一次正常运行,从代码的第一句执行到最后一句,整个过程就是进程的生命 周期
线程:进程中独立、 可调配的最小执行单元就是线程, 进程由多个线程组成
程序与进程的关系
一个程序一般只有一个进程, 单一个程序可以有多个进程
一个进程只能属于一个程序
进程与线程的关系
一个进程拥有多个线程
一个线程只能属于一个进程
java的应用程序主线程就是main方法,程序只有一个主线程,其他的都是子线程
CPU就是用来处理线程的
三、线程创建
在java中线程的创建方式有两种,分别如下:
1、创建一个类继承Thread类
Thread thread = new Thread();
thread.start();
2、创建一个类实现Runnable接口
Thread thread = new Thread( new Runnable);
thread.start();
使用Runnab
*O*四、线程的生命周期
1、创建:线程构造
2、启动:线程调用了start()方法,去CPU排队
3、运行:线程占用CPU,进入run()方法
4、中断:在run()方法遇到sleep()与wait()方法,线程会中断,让出CPU资源
5、死亡:线程运行完run()方法
*O* 五、线程状态
1、NEW(新建状态):线程对象已经创建好,但是没有被启动
2、RUNNABLE(可运行状态):线程对象调用start( ) 到进入run()都是可运行状态
3、BLOCKED(阻塞状态):线程对象遇到sleep()
4、WAITING(等待状态):线程对象遇到wait()
六、线程的休眠(sleep)与唤醒(interrupt)
当一个线程对象在sleep的时候,可以通过interrupt()方法进行强制唤醒,
run() 线程占用CPU时执行的方法
getState() 获取线程的状态
setName() 设置线程名称
getName( ) 获取线程名称
setPirority() 设置线程的优先级参数的数字越大表示优先级越高(0~10)
currentThread() 获取当前占用CPU的线程 ,静态方法
sleep() 线程 休眠
interrupt() 唤醒休眠的线程
*O* 八 、线程同步
1、什么是线程不安全
线程不安全必须满足必须满足三个条件:
a、必须是同一个对象
b、必须是多个线程操作
c、改变共同的全局属性值
在方法上增加synchronize关键字,增加同步锁之后,方法调用会变慢,编程重量级方法
线程安全 线程不安全
StringBuffer StringBuilder
Vector ArrayList
HashTable HashMap
一、 什么是线程
线程是程序的最小单位,相对独立可调配的单元,是CPU最小的基本单位
在同一个程序不同的线程完成功能,称为多线程,软件中最小的单位就是线程
二、线程、 进程 、程序之间的关系
程序 :程序就是一段静态代码,是应用程序的蓝本
进程:进程就是程序的一次正常运行,从代码的第一句执行到最后一句,整个过程就是进程的生命 周期
线程:进程中独立、 可调配的最小执行单元就是线程, 进程由多个线程组成
程序与进程的关系
一个程序一般只有一个进程, 单一个程序可以有多个进程
一个进程只能属于一个程序
进程与线程的关系
一个进程拥有多个线程
一个线程只能属于一个进程
java的应用程序主线程就是main方法,程序只有一个主线程,其他的都是子线程
CPU就是用来处理线程的
三、线程创建
在java中线程的创建方式有两种,分别如下:
1、创建一个类继承Thread类
Thread thread = new Thread();
thread.start();
2、创建一个类实现Runnable接口
Thread thread = new Thread( new Runnable);
thread.start();
使用Runnab
Thread类实现了Runnable接口
package com.tencent.thread;
/**
* 线程的创建
* @author Ricardo.M.Y 2018年5月14日
*/
public class ThreadCreate {
public static void main(String[] args) {
LeftThread left = new LeftThread(); //分配内存空间
left.start(); //启动线程
/*
Thread right =new Thread(new RightThread());
right.start();*/
RightThread right = new RightThread(); //分配内存空间
right.start();
}
}
/**
* 左线程
* @author Ricardo.M.Y 2018年5月14日
*/
class LeftThread extends Thread{
@Override
public void run() {
while("1".equals("1")){
System.out.println("我是光明左使杨逍");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 右线程
* @author Ricardo.M.Y 2018年5月14日
*/
class RightThread extends Thread{
@Override
public void run() {
while("1".equals("1")){
System.out.println("我是光明右使范遥");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 左线程
* @author Ricardo.M.Y 2018年5月14日
*/
/*class RightThread implements Runnable{
@Override
public void run() {
while(true){
System.out.println("我是光明右使范遥");
}
}
}*/
*O*四、线程的生命周期
1、创建:线程构造
2、启动:线程调用了start()方法,去CPU排队
3、运行:线程占用CPU,进入run()方法
4、中断:在run()方法遇到sleep()与wait()方法,线程会中断,让出CPU资源
5、死亡:线程运行完run()方法
*O* 五、线程状态
1、NEW(新建状态):线程对象已经创建好,但是没有被启动
2、RUNNABLE(可运行状态):线程对象调用start( ) 到进入run()都是可运行状态
3、BLOCKED(阻塞状态):线程对象遇到sleep()
4、WAITING(等待状态):线程对象遇到wait()
5、TERMINATED(死亡状态):线程对象运行完run() 方法
package com.tencent.thread;
/**
* 线程状态
* @author Ricardo.M.Y 2018年5月14日
*/
public class ThreadState {
public static void main(String[] args){
State s= new State();
System.out.println(s.getState());
s.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s.getState());
}
}
class State extends Thread{
@Override
public void run() {
System.out.println(this.getState());
}
}
六、线程的休眠(sleep)与唤醒(interrupt)
当一个线程对象在sleep的时候,可以通过interrupt()方法进行强制唤醒,
唤醒后会抛出InterruptedException异常
线程的挂起(wait)与恢复(notifyAll)
wait() 线程挂起
notifyAll() 唤醒所有挂起线程
notify() 唤醒单个线程
以上三个方法是Object类的方法 ()
wait()必须结合同步锁使用,否则会发生异常
package com.tencent.thread;
/**
* 测试线程唤醒
* 学生线程、 老师线程
* 上课的时候学生睡觉,老师说:上课!上课! 上课!
* 学生强行被唤醒,然后认真听课
* @author Ricardo.M.Y 2018年5月14日
*/
public class InterruptTest {
public static void main(String[] args) {
Thread t = new Thread(new School());
t.start();
}
}
class School implements Runnable{
private Thread student,teacher;
public School(){
student = new Thread(this);
student.setName("姜维");
student.setPriority(10);
student.start();
teacher = new Thread(this);
teacher.setName("诸葛亮");
teacher.setPriority(1);
teacher.start();
}
public void run() {
//Thread.currentThread() 获取当前正在占用cpu的线程
if(Thread.currentThread() == student){
System.out.println(student.getName() +"正在睡觉" );
try {
Thread.sleep(1000*60*60);
} catch (InterruptedException e) {
//e.printStackTrace();
System.out.println(student.getName()+"被"+teacher.getName()+"叫醒了");
System.out.println(student.getName()+"知道错了,下次再也不敢了!");
}
}
if(Thread.currentThread() == teacher){
for(int i=0;i<3;i++){
System.out.println(teacher.getName()+"说:上课!");
}
//唤醒学生
student.interrupt();
}
}
}
package com.tencent.thread;
/**
* 线程的挂起(wait)
* eg :员工与老板的案例
* 员工工作八小时后,跟老板说说一句:好累啊! 我去休息一下
* 老板开始数数,从1数到10,然后员工起来工作
* @author Ricardo.M.Y 2018年5月14日
*/
public class WaitTest {
public static void main(String[] args) {
new Company();
}
}
/**
* 公司
*/
class Company implements Runnable{
private Thread staff,boss;
private int hour=0; //工作时间
private int num=0; //老板数数
public Company(){
staff=new Thread(this,"乃亮");
staff.setPriority(10);
staff.start();
boss = new Thread(this,"pgOne");
boss.setPriority(1);
boss.start();
}
@Override
public void run() {
working();
}
//工作
private synchronized void working(){
if(Thread.currentThread() == staff){
while(true){
hour++;
System.out.println("老板,我是"+staff.getName()+",现在"+hour+"点了,我还在工作!");
if(hour ==8){
System.out.println("老板,我好累啊! 我去休息了");
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
hour=0;
this.notifyAll();
}
}
}
if(Thread.currentThread() == boss){
while(true){
num++;
System.out.println("我是"+boss.getName()+"老板,开始数数"+num);
if(num==10){
//唤醒员工
this.notifyAll();
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
num=0;
}
}
}
}
}
七、线程常用的方法
start() 启动线程run() 线程占用CPU时执行的方法
getState() 获取线程的状态
setName() 设置线程名称
getName( ) 获取线程名称
setPirority() 设置线程的优先级参数的数字越大表示优先级越高(0~10)
currentThread() 获取当前占用CPU的线程 ,静态方法
sleep() 线程 休眠
interrupt() 唤醒休眠的线程
*O* 八 、线程同步
1、什么是线程不安全
线程不安全必须满足必须满足三个条件:
a、必须是同一个对象
b、必须是多个线程操作
c、改变共同的全局属性值
在方法上增加synchronize关键字,增加同步锁之后,方法调用会变慢,编程重量级方法
线程安全 线程不安全
StringBuffer StringBuilder
Vector ArrayList
HashTable HashMap
package com.tencent.thread;
/**
* 线程同步
* eg、银行有出纳与会计两个线程
* 比如:有个账户有50w,每天出纳取钱两次,每次20万
* 会计每天存钱三次,每次30万
* 看一周后的账单
* @author Ricardo.M.Y 2018年5月14日
*/
public class BankTest {
public static void main(String[] args) {
Thread th = new Thread(new Bank());
th.start();
}
}
class Bank implements Runnable{
private Thread cashier,accountant; //两个线程
private int deposit =50; //存款
public Bank(){
cashier = new Thread(this,"出纳");
cashier.setPriority(10);
cashier.start();
accountant = new Thread(this,"会计");
accountant.setPriority(1);
accountant.start();
}
@Override
public void run() {
//业务逻辑
for(int i = 1;i<=5;i++){
if(Thread.currentThread() == cashier){
//取钱
access(i,20);
}
if(Thread.currentThread() ==accountant){
//存钱
access(i,30);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/**
* 存取钱
* @author Ricardo.M.Y 2018年5月14日
* @param day
* @param money
*/
private synchronized void access(int day,double money){
//出纳
if(Thread.currentThread() == cashier){
System.out.println("今天星期"+day+",我是"+cashier.getName());
for(int i=0;i<2;i++){
deposit-=money;
System.out.println("当前余额"+deposit+"万元");
}
}
//会计
if(Thread.currentThread() == accountant){
System.out.println("今天星期"+day+",我是"+accountant.getName());
for(int i=0;i<3;i++){
deposit+=money;
System.out.println("当前余额"+deposit+"万元");
}
}
}
}
经典案例:
1. 编写多线程应用程序,模拟多个人通过一个山洞的模拟。这个山洞每次只能通过一个人,每个人通过山洞的时间为5秒,随机生成10个人,同时准备过此山洞,显示一下每次通过山洞人的姓名。
2.编写线程同步模拟应用程序:
(1) 大气环境数据为:温度,湿度,风速。
(2) 一个大气环境传感器测量环境数据需要5秒时间。
(3) 一个计算机读取传感器的环境数据需要0.01秒时间。
模拟一个计算机读取大气环境传感器的读取的随机的温度,湿度和风速的100次。
package com.tencent.work;
/**
* 山洞类
* 2018年5月15日
*/
public class Cave {
/**
* 过山洞
* 2018年5月15日
*/
public synchronized void pass() {
String name = Thread.currentThread().getName();
System.out.println(name + "正在进山洞");
try {
Thread.sleep(5 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(name + "通过山洞");
}
public static void main(String[] args) {
Cave cave = new Cave();
for(int i=1; i<=10; i++) {
Person p = new Person(cave,"葫芦娃" + i + "号");
p.start();
}
}
}
class Person extends Thread {
private Cave cave;
public Person(Cave cave, String name) {
this.cave = cave;
this.setName(name);
}
@Override
public void run() {
cave.pass();
}
}
package com.tencent.work;
import java.text.DecimalFormat;
/**
* 编写线程同步模拟应用程序:
(1) 大气环境数据为:温度,湿度,风速。 大气环境 Atmospheric environment
(2) 一个大气环境传感器测量环境数据需要5秒时间。
(3) 一个计算机读取传感器的环境数据需要0.01秒时间。
模拟一个计算机读取大气环境传感器的读取的随机的温度,湿度和风速的100次。
* @author Ricardo.M.Y 2018年5月15日
*/
public class Environment {
public static void main(String[] args) {
System.out.println("请稍等,传感器正在测量环境数据(5秒)");
Environment en = new Environment();
for(int i =1;i<=100;i++){
Test t = new Test(en);
t.start();
}
}
/**
* 测量
* @author Ricardo.M.Y 2018年5月15日
*/
public synchronized void measure(){
//一个大气环境传感器测量环境数据需要5秒时间。
try {
Thread.sleep(1000*5);
} catch (InterruptedException e) {
e.printStackTrace();
}
//温度,湿度,风速
DecimalFormat df = new DecimalFormat("#.0"); //取小数点后几位
String temperature =df.format(Math.random()*100);
String humidity =df.format(Math.random()*100);
String speed = df.format(Math.random()*100);
//一个计算机读取传感器的环境数据需要0.01秒时间。
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//打印
System.out.println("温度:"+temperature+"\t 湿度:"+humidity+"\t 风速:"+speed);
}
}
class Test extends Thread {
private Environment en;
public Test(Environment en){
this.en = en;
}
@Override
public void run() {
en.measure();
}
}