一、进程的简述
1、进程:
是一个正在执行中的程序。每一个进程执行都有一个执行顺序,该顺序是一个执行路径,或者叫一个控制单元。Java中主线程要运行的代码在main中存储,自定义线程存在run中。
2、线程:
就是进程中的一个独立的控制单元,线程在控制着进程的执行。一个进程中至少有一个线程。
Java jvm启动时会有一个进程java.exe。该进程中至少有一个线程负责java程序的执行,而且这个线程运行的代码存在于main方法中该线程称之为主线程。
3、扩展:
其实更细节说明jvm,jvm启动不止一个线程,还有负责垃圾回事机制的线程。
4、多线程存在的意义:
使程序的部分产生同时运行效果,提高效率。
二、创建进程
1、如何在自定义的代码中,自定义一个线程呢?
继承Thread类,重写run()方法。创建步骤:
1、定义类继承Thread
2、复写Thread类中的run方法(目的是:将自定义代码存储在run方法,让线程运行)
3、调用线程的start方法,该方法有2个作用:启动线程、调用run方法。
eg:class Demo extends Thread // 定义一个线程类
{
public void run() // 重写run方法
{
for(int x=0;x<60;x++) // 循环的目的是让线程多运行一会。
System.out.println(“demo run--”+x);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
Demo d = new Demo(); // 建立好一个对象其实就是实例创建一个线程
d.start(); // 线程启动
for(int x=0;x<60;x++)
System.out.println(“Hello world!..”+x); // 主线程
}
}
2、实现Runnable接口,覆写run方法。详细步骤:
1、定义类实现Runable接口。
2、覆盖Runable接口中的run方法。将线程要运行的代码放在该run方法中。
3、通过Thread类建立线程对象。
4、将Runable接口的子类对象作为实际参数传递给Thread类的构造函数(为什么要将Runable 接口的子类对象传 递给Thread的构造函数)因为,自定义的run方法所属的对象是Runable接口的子类对象,所以要让线程去指 定指定对象的run方法。就必须明确该run方法所属对象。
5、调用Thread类的start方法,开启线程,并调用Runable接口子类的run方法。
eg:classTicket implements Runnable // (extendsThread) // 定义类实现Runnable接口用于创建线程
{
private void run() // 实现run方法
{
while(true)
{
if(ticket>0) // 票数大于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(t2);
t1.start(); // 启动线程
t2.start();
}
}
3、实现方式和继承方式的区别?
1、实现方式的好处,避免了单继承的局限性。在定义线程时,建立使用实现方式。
2、继承Thread:线程代码存放Thread子类run方法中,实现Runable,线程代码存在接口的子类的run方法。注:发现程序运行结果每一次都不同,以newi多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。明确一点,在某一个时刻,只能有一个程序在运行(多核除外)cpu做着快速的切换,以达到看上去是同时运行的效果。我们可以形象地把多线程的运行认为在互相抢夺cpu的执行权。这就是多线程的一个特性:随机性,谁抢到谁执行。至于执行多长时间,Cpu说的算。
三、线程的四种常见状态:
1、New(新创建)
2、Runnable(可运行)
3、Blocked(被阻塞)
4、Waiting(等待)
5、Timed waiting(计时等待)
6、Terminated(被终止)
四、线程的方法介绍
1、staticThread currentThread(); 返回当前正在执行的线程对象。静态方法
2、getName(); 获取线程名称。
3、设置线程名称:setName或者构造函数。调用父类构造super(name);
五、线程的同步
多线程当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行,导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式:同步代码块。
同步代码块的格式:Synchronized(对象){ 需要被同步的代码 }
说明:那些语句要操作共享数据,那些语句就需要同步。
eg:class Ticket implements Runnable
{
private int tick = 100;
Object obj = new Object();
public void run()
{
while(true)
{
Synchronized(obj)
{
if(tick>0)
{
try{Thread.sleep(10);} // 停止10秒看效果
catch(Exception e)
{ }
System.out.println(Thread.currentThread().getName()+”..sale:”+tick--);
}
}
}
}
}
同步代码块的对象参数相当于锁,持有锁的线程可以在同步中执行,没有持有锁的线程即便获取cpu的执行权,也进不去,因为没有获取锁。
同步的前提:
1、必须要有2个或2个以上的线程。
2、多个(必须)多个线程使用同一个锁(必须保证同步中只能有一个线程在运行)
同步代码块的好处:解决了多线程的安全问题。
同步代码块的弊端:多个线程需要判断锁,较为消耗资源。
如何找出同步问题:
1、明确哪些代码是多线程运行代码(run中代码及run中调用代码)
2、明确共享数据。
3、明确多线程运行代码中哪些语句是操作共享数据的。
同步有2种表现形式:同步代码块和同步函数。同步函数封装的定要是需要同步的语句(例如while循环不能放)
同步代码块封装代码,唯一的区别是他带着同步性,若让函数具备同步性,就可以。
六、同步锁
同步函数用的是哪一个锁?
函数需要被对象调用,那么函数都有一个所属对象引用就是this,所以同步函数的使用锁是this。证明实例如下:
class Ticket implements Runnable
{
private int tick = 1000;
Booleanflag = true;
public void run()
{
if(flag)
{
while(true)
{
Synchronized(this)
{
if(tick>0)
{
try{ Thread.sleep(10); }catch(Exception e){ }
}
else
while(true)
(this.)show();
}
System.out.println(Thread.currentThread().getName()+”…sale:”+tick--);
}
}
}
}
class ThisLockDemo
{
public static void main(String[] args)
{
Ticket t = new Ticket();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start(); // 一线程开始用同步代码块
t.flag = false; // 二线程开始用同步函数
t2.start();
}
}
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证发现不是this,因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码文件对象。就是类名.class。该对象的类型是class(可以通过getClass()方法获取)。静态的同步方法使用的锁是该方法所在类的字节码文件对象,类名.class。且此对象在内存中是唯一的。
七、死锁
死锁,同步嵌套同步。在编写程序过程中要尽量避免死索。
创建一个死锁程序的例子:
class Test implements Runnable
{
private Boolean flag;
Test(Boolean flag)
{ this.flag = flag; }
public void run()
{
if(flag)
{
while(true)
{
Synchronized(MyLock.locka)
{
System.out.println(“if locka”);
Synchronized(MyLock.Lockb)
{ System.out.println(“if lockb”); }
}
}
}
else
{
Synchronized(MyLock.lockb)
{
System.out.println(“else lockb”);
Synchronized(MyLock.locka)
{
System.out.println(“elselocka”);
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeadLockTest
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Test(true)); Thread t2 = new Thread(new Test(false));
t1.start(); t2.start();
}
}