一、在软件运行时,可以真实看到的是进程,每个进程在执行时都有顺序,该顺序是一个执行路径,即控制单元。线程是进程中一个独立的控制单元,线程在控制进程的执行,一个进程中至少有一个线程。在java中,运行代码存在于main函数里的线程叫主线程。
二、线程的定义方式由两种,继承和实现接口。继承:1、定义类并继承Thread类,2、复写Thread类中的run方法;3、创建该类对象,即创建线程;4、调用线程的start方法,开启线程。说明:线程要执行的代码存放在Thread子类的run方法中。实现:1、定义类实现Runnable接口;2、复写Runnable中的run方法;3、创建Thread类对象,即创建线程;4、将Runnable接口的子类对象以实际参数的形式传递给Thread类的构造函数。说明:线程要执行的代码存放在Runnable子类的run方法中,所以要将Runable的子类对象传递给Thread类的构造函数。
线程的运行状态:创建、运行、消亡、冻结、临时状态(阻塞状态)。线程运行时,该线程既有执行资格,也有执行权,冻结时既没有执行资格,也没有执行权,临时状态有执行资格,但是没有执行权。从运行到冻结由两种方式,sleep()和wait(),sleep在指定的时间之后,线程自动苏醒,状态改为临时状态,wait需要手动去唤醒(notify())状态才能更改为临时状态。
三、多线程的安全问题:由于多条语句操作同一线程共享数据,一个线程还没有将多条语句全部执行结束,另一线程参与进来,导致共享数据发生错误。java中提供了专门解决多线安全问题的方法:同步。通分为:同步代码块和同步方法。同步是通过控制锁的状态来实现同步功能的,只有持有锁你的线程才能进来执行,执行结束释放锁,没有持有锁的线程就算具有cpu的执行权,它也只有将cpu的执行权拱手相让。同步代码块中,锁是对象,在非静态的同步方法中,锁是this,在静态方法中,锁是该方法所在类的字节码文件。
用第一种方式创建线程,代码如下:
/*
需求:创建两个线程,与主线程交替执行。主线程无需我们收到创建。
*/
//定义一个类并继承Thread
class Test extends Thread
{
//通过构造函数的方式修改线程名称
Test(String name)
{
super(name);
}
//复写Thread中的run方法,方法体内的代码是线程要执行的代码。
public void run()
{
for(int i=0;i<60;i++)
System.out.println(getName()+i);
}
}
class ThreadDemo
{
public static void main(String[] args)
{
//创建两个线程
Test t = new Test("Test");
Test t1 = new Test("Test--2-");
//开启并执行线程
t.start();
t1.start();
for(int i=0;i<60;i++)
System.out.println("main run-----"+i);
}
}
多个窗口卖票问题:
/*
卖票问题:多个线程操作共享资源——车票,所以不能用继承Thread的方法,
应该将线程和线程要执行的代码分开。
第二种创建线程的方法:1、定义类实现Runable接口;2、复写run方法;
3、用Thread类创建线程;4、将Runnable接口的子类
对象以实际参数的形式传递给Thread类的构造函数。
由于多条语句操作同一线程共享资源,有可能会出现一个线程还未将全部语句
执行完,另外的线程参与进来,所以应该用同步,一个线程在执行的时候,
另外的线程虽然持有cpu的执行权,也不能执行线程代码。
*/
//定义类并实现接口Runnable
class Ticket implements Runnable
{
//定义共享资源:车票
private int ticket = 200;
Object obj = new Object();
//复写run方法
public void run()
{
while(true)
{
synchronized(obj)
{
if(ticket > 0)
{
System.out.println(Thread.currentThread().getName()+"卖票"+ticket);
ticket--;
}
}
}
}
}
class Thread2Test
{
public static void main(String[] args)
{
//创建Ticket类对象
Ticket tc = new Ticket();
//将Runnable接口的子类对象传递给Thread类的构造函数,指定线程要执行的代码
Thread t1 = new Thread(tc,"线程一:");
Thread t2 = new Thread(tc,"线程二::");
Thread t3 = new Thread(tc,"线程三:::");
Thread t4 = new Thread(tc,"线程四::::");
//开启线程
t1.start();
t2.start();
t3.start();
t4.start();
}
}
死锁:
/*
死锁:同步乔套同步,而且用的锁不是同一个;
步骤:
1、定义一个类,用来放锁;
2、定义一个类实现Runnable接口,并复写run方法;
3、用Thread类创建线程;
关键步骤:需要定义一个boolean型变量,来判断执行哪块代码,
在创建线程的时候,需要传递该变量的值。出现死锁
情况,必须有两个线程,并且这两个线程都有用不同
锁控制的同步嵌套,用锁的顺序正好相反。
*/
//定义一个类,存放两把锁,方便使用,定义成静态的
class DeadLock
{
static Object lockA = new Object();
static Object lockB = new Object();
}
//定义一个类实现Runnable接口,并复写run方法
class DeadLockTest implements Runnable
{
//定义一个标志变量
private boolean flag;
DeadLockTest(boolean flag)
{
this.flag = flag;
}
//复写run方法
public void run()
{
//根据不同的标志位值选择不同的代码块执行
//第一个代码块用锁的顺序:lockA、lockB;
//第二个代码块用锁的顺序:lockB、lockA
if(flag)
{
synchronized(DeadLock.lockA)
{
System.out.println("if lockA");
synchronized(DeadLock.lockB)
{
System.out.println("if lockB");
}
}
}
else
{
synchronized(DeadLock.lockB)
{
System.out.println("else lockB");
synchronized(DeadLock.lockA)
{
System.out.println("else lockA");
}
}
}
}
}
class DeadLock2Test
{
public static void main(String[] args)
{
//创建两个线程
Thread t1 = new Thread(new DeadLockTest(true));
Thread t2 = new Thread(new DeadLockTest(false));
//开启线程
t1.start();
t2.start();
}
}