进程:一个正在执行中的程序,每一个进程执行都有一个执行顺序
该顺序就是一个执行路径/控制单元
线程:进程中的一个独立控制单元,控制着进程的执行
一个进程中至少有一个线程
eg.Java VM启动时会有一个进程java.exe
该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于
main方法中,该线程成为主线程
扩展:jvm启动不止一个线程,还有负责垃圾回收机制的线程
如何定义一个线程:Java-API-Thread类-继承Thread类
/*
1.定义类继承Thread
2.复写Thread中的run方法
3.调用线程的start方法:启动线程,调用run方法
**CPU并不是同时进行线程,而是在不同线程之间切换进行
*/
class Demo extends Thread{
public void run(){
System.out.println("Demo_Run");
}
}
class ThreadDemo{
public static void main(String[] args){
Demo d=new Demo();
d.start();
}
}
多线程运行即互相抢夺CPU的执行权,即多线程的运行具有随机性
为什么要覆盖run方法?
Thread用于描述线程,该类就定义了一个功能,用于存储线程要运行的代码,该储存
功能就是run方法,即run用于储存线程要运行的代码
覆写run()的目的在于储存自定义代码,让线程运行,
**start()用于开启线程并执行run(),run()仅调用方法,创建线程,并未运行
线程的运行状态
1.被创建
2.运行 start()
3.冻结:放弃执行资格 sleep(time) wait () notify()
4.消亡 stop()
**阻塞状态:等待cpu执行权,具备运行资格
获取线程对象及名称
static Thread currentThread() 获取当前线程对象
getName() 获取线程名称
setName()/构造函数 设置线程名称
class Demo extends Thread{
Demo(String name){
super(name);
}
public void run(){
for(int x=0;x<60;x++){
System.out.println(Thread.currentThread()+this.getName()+"Demo_Run"+x);
}
}
}
class ThreadDemo{
public static void main(String[] args){
Demo d=new Demo("Mark_II");
d.start();
for(int x=0;x<60;x++){
System.out.println("Demo"+x);
}
}
}
练习:卖票窗口
Ver 1.0
/*
需求:简单的卖票程序
多个窗口同时卖票
*/
class Ticket extends Thread{
private static int tick=100; //用static 来共享总票数
public void run(){
while(true){
if(tick>0){
System.out.println(currentThread().getName()+"__"+"sale:"+tick--);
}
}
}
}
class TicketDemo{
public static void main(String[] args){
Ticket t1=new Ticket();
Ticket t2=new Ticket();
Ticket t3=new Ticket();
Ticket t4=new Ticket();
t1.start();
t2.start();
t3.start();
t4.start();
}
}
V 2.0 解决共享票数的问题
/*
需求:简单的卖票程序
多个窗口同时卖票
*/
class Ticket implements Runnable{//extends Thread{
private int tick=100;
public void run(){
while(true){
if(tick>0){
System.out.println(Thread.currentThread().getName()+"__"+ "sale:"+tick--);
}
}
}
}
class TicketDemo{
public static void main(String[] args){
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
/* 创建线程的第二种方式:实现Runnable接口
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法
-将代码存放在该run方法中
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象作为实际参数传递给Thread类的构造函数
-自定义的run方法所属对象是Runnable接口的子类对象
所以要让线程去指定对象的run方法,就必须明确该run方法的所属对象
5.调用Thread类的start方法开启线程并调用Runnable接口子类的run方法 */
实现方式和继承方式的区别
实现方式:避免了单继承的局限性,建议使用。
线程代码存放在接口子类的run()
继承方式:线程代码存放在Thread的子类run()中
线程的安全问题
/*
通过分析,发现打印出0,-1,-2等错票
多线程的运行出现了安全问题
*/
class Ticket implements Runnable{//extends Thread{
private int tick=100;
public void run(){
while(true){
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"__"+ "sale:"+tick--);
}
}
}
}
class TicketDemo{
public static void main(String[] args){
Ticket t=new Ticket();
Thread t1=new Thread(t);
Thread t2=new Thread(t);
Thread t3=new Thread(t);
Thread t4=new Thread(t);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分
还没有执行完,另一个线程参与进来执行,导致了共享数据的错误
解决办法:
对多条操作共享数据的语句,只能让一个线程执行完,其他线程不可以参与执行
Java对于多线程的安全问题提供了专业的解决方式——同步代码块
synchronized(对象){
需要同步的代码
}
class Ticket implements Runnable{//extends Thread{
private int tick=400;
Object obj=new Object();
public void run(){
while(true){
synchronized(obj){
if(tick>0){
try{Thread.sleep(10);}catch(Exception e){}
System.out.println(Thread.currentThread().getName()+"__"+ "sale:"+tick--);
}
}
}
}
}