java进程、线程、线程生命周期,创建线程的五种方法、以及多线程并发同步问题。
1.程序、进程、线程、并行、并发
程序:一段静态的代码。
进程:程序的一次动态执行过程。进程基于操作系统,运行在内存中。如windows就是一个多任务系统,可以同时运行多个应用程序。
线程:在一个程序中同时运行的多个独立的流程,每一个独立的流程就是线程。
进程和线程有时也被称为任务。
线程并发:多个线程并发执行。
并行:并行是指两个或者多个事件在同一时刻发生,同时运行。只有具备多个cpu才能实现并行。
并发:并发是指两个或多个事件在同一时间间隔发生。是伪并行,即看起来是同时运行。单个cpu+多道技术就可以实现并发。
而无论是并行还是并发,在用户看来都是“同时”运行的。
主线程:当JVM启动后,加载类文件,发现main方法,那么就会为main方法创建一个线程,这个为main方法创建的线程称为主线程。
一个进程中可以有多个任务一起执行,每一个任务都被称为一个线程。线程依赖于进程,在进程中运行·,不能独立存在。每个进程中至少有一个线程,被称为主线程。只有当程序中所有的线程都结束之后,程序才会结束。
线程随机性:正常情况下,线程是随机的,谁先执行是不确定的,如果我们想控制顺序,需要自己编写代码。
2.创建线程的五种写法以及java中多线程代码的编写
方法1:直接Thread t=new Thread(); t.start(); 这种方法程序可以运行,但没有什么意义,因为原始线程run()方法里什么也没有。start开始即结束。
方法2:继承Thread,重写run方法;
方法3:实现Runnable ,new Thread();
方法4:Thread匿名内部类
方法5:Runnable匿名内部类
下面给出用五种方法来创建线程的代码示例:
定义EatThread类,继承Thread,重写run()方法
package test; //继承Thread,重写run()方法 public class EatThread extends Thread { @Override public void run() { for(int i=1;i<3;i++) { //每隔0.1秒吃一口 System.out.println(this.getName()+"吃一口!"); try { //等待0.1秒 sleep(100); //也可以写成this.sleep(100)或者Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(this.getName()+"线程结束"); } }
定义PhoneThread类,实现Runnable接口
package test; //实现Runnable接口 public class PhoneThread implements Runnable { @Override public void run() { //获取当前正在执行这段代码的线程 Thread now =Thread.currentThread(); for(int i=1;i<3;i++) { //每隔0.1秒玩一次手机 System.out.println(now.getName()+"玩手机");//注意:此处不能写成this.getName() try { //等待0.1秒 now.sleep(100); //或者写成Thread.sleep(100) //注意:此处不能写成sleep(100)或者this.sleep(100),会报错 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(now.getName()+"线程结束"); } }
主函数,五种创建方法
package test; public class Test { public static void main(String[] args) { /** * 第一种:直接new thread(),这种方法没有什么意义 */ Thread t1=new Thread(); t1.setName("1"); t1.start(); /** * 第二种,继承Thread,重写run()方法 */ EatThread t2=new EatThread(); t2.setName("2"); t2.start(); /** * 第三种,实现Runnable接口 */ PhoneThread pt=new PhoneThread(); Thread t3=new Thread(pt); Thread t4=new Thread(new PhoneThread());//此处也可以写成Thread t4=new Thread(pt); t3.setName("3");//也可以在创建线程的时候直接命名Thread t3=new Thread(pt,"3"); t4.setName("4"); t3.start(); t4.start(); /** * 第四种,Thread匿名内部类,仅适合局部使用 */ new Thread("5") { @Override public void run() { for(int i=1;i<3;i++) { //每隔0.1秒站一下 System.out.println(this.getName()+"站起来!"); try { //等待0.1秒 sleep(100); //也可以写成this.sleep(100)或者Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(this.getName()+"线程结束"); } }.start(); /** * 第五种:Runnable匿名内部类 */ Thread t6=new Thread(new Runnable() { @Override public void run() { Thread now=Thread.currentThread(); for(int i=1;i<3;i++) { //每隔0.1秒坐一下 System.out.println(now.getName()+"坐下去!"); try { //等待0.1秒 Thread.sleep(100); //或者now.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(now.getName()+"线程结束"); } },"6"); t6.start(); } }
运行结果:
2吃一口!
3玩手机
4玩手机
5站起来!
6坐下去!
3玩手机
2吃一口!
4玩手机
6坐下去!
5站起来!
3线程结束
2线程结束
5线程结束
6线程结束
4线程结束
3.线程的生命周期
新建、就绪、运行、阻塞、死亡总共5种状态
(1)新建:创建一个线程;
(2)就绪:执行start()之后就处于就绪状态,时刻准备着被cpu执行。
(3)运行:线程得到了cpu资源进行运行,但只有一小段时间片;时间到了之后,cpu就切换到其他线程,当前线程又变为就绪状态。
(4)堵塞:睡眠sleep(毫秒)、等待wait()、等待队列join()、
(5)死亡:run执行结束或者中途被其他线程杀死
自然死亡:正常运行run()方法结束后终止
异常终止:调用stop()等方法杀死
4.多线程同步问题
以存钱取钱为例,说明线程同步问题。
先看如下代码,张三存钱,存10次,每次存100元。李四取钱,取10次,每次取100元。
package test; //账户 public class Account { static private double n; //定义账户余额 public double getN() { return n; } public void setN(double n) { this.n = n; } }
package test; public class Tongbu { public static void main(String[] args) { Account ac =new Account(); ac.setN(1000); //账户当前余额1000元 System.out.println("最开始余额为:"+ac.getN()); new Thread("张三") { @Override public void run() { try { for(int i=1;i<=10;i++) { //获取当前账户余额 double n=ac.getN(); //向账户中存出100 n=n+100; sleep(1); ac.setN(n); //输出当前账户余额 System.out.println(this.getName()+"存100后余额为:"+n); } }catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("李四") { @Override public void run() { try { for(int i=1;i<=10;i++) { //获取当前账户余额 double n=ac.getN(); //从账户中取出100 n=n-100; sleep(1); ac.setN(n); //输出当前账户余额 System.out.println(this.getName()+"取100后余额为:"+n); } }catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }
运行结果为:
最开始余额为:1000.0
张三存100余额后为:1100.0
李四取100后余额为:900.0
张三存100余额后为:1200.0
李四取100后余额为:800.0
张三存100余额后为:1300.0
李四取100后余额为:700.0
张三存100余额后为:1400.0
李四取100后余额为:600.0
张三存100余额后为:1500.0
李四取100后余额为:500.0
张三存100余额后为:1600.0
李四取100后余额为:400.0
张三存100余额后为:1700.0
李四取100后余额为:300.0
张三存100余额后为:1800.0
李四取100后余额为:200.0
张三存100余额后为:1900.0
李四取100后余额为:100.0
张三存100余额后为:2000.0
李四取100后余额为:0.0
从上面的结果看出,这并不是我们想要的结果,最终余额应该仍为1000。
解决方案:加锁,synchronized,
synchronized里面的代码是一个整体,必须执行完,即使里面有sleep,其他的线程也要等待这一段执行完。
synchronized:线程同步,让线程排队。
锁:使用不同的锁就有不同的队伍
package test; public class Tongbu { public static void main(String[] args) { Account ac =new Account(); ac.setN(1000); //账户当前余额1000元 System.out.println("最开始余额为:"+ac.getN()); new Thread("张三") { @Override public void run() { try { for(int i=1;i<=10;i++) { //加锁,ac为大家都认识的对象,不能加一个只有你自己认识的锁 synchronized(ac) { //获取当前账户余额 double n=ac.getN(); //向账户中存出100 n=n+100; sleep(1); ac.setN(n); //输出当前账户余额 System.out.println(this.getName()+"存100后余额为:"+n); } } }catch (InterruptedException e) { e.printStackTrace(); } } }.start(); new Thread("李四") { @Override public void run() { try { for(int i=1;i<=10;i++) { //加锁,ac为大家都认识的对象 synchronized(ac) { //获取当前账户余额 double n=ac.getN(); //从账户中取出100 n=n-100; sleep(1); ac.setN(n); //输出当前账户余额 System.out.println(this.getName()+"取100后余额为:"+n); } } }catch (InterruptedException e) { e.printStackTrace(); } } }.start(); } }
运行结果:
最开始余额为:1000.0
张三存100后余额为:1100.0
张三存100后余额为:1200.0
张三存100后余额为:1300.0
张三存100后余额为:1400.0
张三存100后余额为:1500.0
李四取100后余额为:1400.0
李四取100后余额为:1300.0
李四取100后余额为:1200.0
李四取100后余额为:1100.0
李四取100后余额为:1000.0
李四取100后余额为:900.0
李四取100后余额为:800.0
李四取100后余额为:700.0
李四取100后余额为:600.0
李四取100后余额为:500.0
张三存100后余额为:600.0
张三存100后余额为:700.0
张三存100后余额为:800.0
张三存100后余额为:900.0
张三存100后余额为:1000.0
- 【搞懂Java多线程之一】多线程相关概念,线程生命周期以及线程创建方法
- JAVA多线程并发同步,以及线程终止
- Java多线程下生产者消费者问题的五种同步方法实现
- 死磕Java并发(一)什么是线程以及创建多线程的三种方式
- Java笔记3 多线程<1>线程概述、多线程的创建、多线程的安全问题、静态同步函数的锁、死锁
- JAVA基础初探(十四)多线程(线程与进程概述、线程的实现、状态、常用方法、优先级、生命周期)
- 黑马毕向东Java课程笔记(day11):多线程(第一部分)——进程与线程+线程创建+线程安全与同步代码块+同步锁/死锁
- Java总结(九)——(线程模块 一(线程的创建(方法一)与启动,线程状态与生命周期,进程与线程))
- java进程、线程、多线程以及线程安全问题
- Java 中的多线程-两种创建方式,定时器的应用,线程的安全问题可以用银行转账来说明
- 关于java基础类型与引用类型内存存储问题,以及string.intern()方法(String两种创建方式的区别)
- java并发编程---如何创建线程以及Thread类的使用
- java多线程Thread的实现方法、中断机制、生命周期、守护进程等
- java多线程之线程并发库同步集合类的应用
- 黑马程序员------多线程(No.1)(概述、线程的创建、安全问题、同步锁、同步函数)
- java线程一之创建线程、线程池以及多线程运行时间统计
- 理解iOS多线程应用的开发以及线程的创建方法
- java多线程实现火车售票系统 以及java中的同步的实现 同步块 和同步方法同时 同步
- Java并发线程--多线程的创建
- Java Notes: Java的多线程创建的两种方法以及Race Condition的解释