一、多线程引入
通常来说cpu运行程序的速度是很快的,如果在某一时刻cpu只执行一个程序,这对于cpu资源来说是一种极大地浪费。而且随着多任务操作系统主导地位的确立,多线程的编程以不可或缺。
二、多线程概述
多线程是指在系统中同时有多条程序在并发的运行,虽然对于单核cpu来说,再某一时刻只有一条程序再运行,根本不可能实现“多条程序的同时运行”,但由于cpu的运行速度很快,在不断的快速切换中,用户感觉好像有多个程序在同时运行一样,这就是多线程的由来。
三、创建多线程的方法
1)直接继承Thread类
步骤如下:
1,定义一个类继承Thread类。
2,覆盖Thread类中的run方法。
3,直接创建Thread的子类对象创建线程。
4,调用start方法开启线程并调用线程的任务run方法执行。
2)实现Runnable接口。
步骤如下:
1,定义类实现Runnable接口。
2,覆盖接口中的run方法,将线程的任务代码封装到run方法中。
3,通过Thread类创建线程对象,并将Runnable接口的子类对象作为Thread类的构造函数的参数进行传递。
4,调用线程对象的start方法开启线程。
四、两种创建线程方法的比较
实现Runnable接口的好处:
1,将线程的任务从线程的子类中分离出来,进行了单独的封装。
按照面向对象的思想将任务封装成对象。
2,避免了java单继承的局限性(如果用Thread的子类创建对象,它将不能再继承其它的类!)。
3.便于共享资源的操作。
五、run和start的特点
启动一个线程该调用start()方法而不能直接调用run()方法,这样不会创建一个新的线程,只是简单的在当前线程中执行了run()方法,当调用start()方法时,才会创建一个新的线程,这个新的线程将执行run()方法上的代码。
六、多线程的安全问题
线程安全问题产生的原因:
1,多个线程在操作共享的数据。
2,操作共享数据的线程代码有多条。
当一个线程在执行操作共享数据的多条代码过程中,其他线程参与了运算。
就会导致线程安全问题的产生。
解决思路;
就是将多条操作共享数据的线程代码封装起来,当有线程在执行这些代码的时候,
其他线程时不可以参与运算的。
必须要当前线程把这些代码都执行完毕后,其他线程才可以参与运算。
在java中,用同步代码块或者同步函数就可以解决这个问题。
同步代码块的格式:
synchronized(对象)
{
需要被同步的代码;
}
同步的好处:解决了线程的安全问题。
同步的弊端:相对降低了效率,因为同步外的线程的都会判断同步锁。
同步的前提:同步中必须有多个线程并使用同一个锁。
同步函数的格式:
public synchronized void synchronized method()
{
……
}
同时要注意:同步非静态方法的监视器是this,而同步静态方法的监视器是当前所在的类的Class对象。只有线程用的是同一把锁时,才可以实现线程的同步。
一个多线程同步的示例程序如下:
class Ticket implements Runnable//extends Thread
{
private int num = 100;
Object obj = new Object();
public void run()
{
while(true)
{
synchronized(obj)
{
if(num>0)
{
try{Thread.sleep(10);}catch (InterruptedException e){}
System.out.println(Thread.currentThread().getName()+".....sale...."+num--);
}
}
}
}
}
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();
}
}
七、多线程的死锁问题
在编写多线程同步的时候一定要注意避免死锁的发生。
Object o1=new Object();
Object o2=new Object();
while(true) {
synchronized(o1) {
...
synchronized(o2){
..
}
...
}
}
while(true) {
synchronized(o2) {
...
synchronized(o1){
...
}
...
}
}
八、线程间的通信问题
Object类里面有几个方法是用于线程通信的:wait(),notify(),notifyAll()。由于任何对象都可以作为监视器对象,所以任何对象都可以调用这几个方法。
但是这些方法只能在被synchronized修饰的方法或者代码块中调用。
九、停止线程
当要停止一个线程时一共有两个方法
停止线程:
1,stop方法。
2,run方法结束。
怎么控制线程的任务结束呢?
任务中都会有循环结构,只要控制住循环就可以结束任务。
控制循环通常就用定义标记来完成。
但是如果线程处于了冻结状态,无法读取标记。如何结束呢?
可以使用interrupt()方法将线程从冻结状态强制恢复到运行状态中来,让线程具备cpu的执行资格。
示例代码如下:
class StopThread implements Runnable
{
private boolean flag = true;
public synchronized void run()
{
while(flag)
{
try
{
wait();//t0 t1
}
catch (InterruptedException e)
{
System.out.println(Thread.currentThread().getName()+"....."+e);
flag = false;
}
System.out.println(Thread.currentThread().getName()+"......++++");
}
}
public void setFlag()
{
flag = false;
}
}
class StopThreadDemo
{
public static void main(String[] args)
{
StopThread st = new StopThread();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.setDaemon(true);
t2.start();
int num = 1;
for(;;)
{
if(++num==50)
{
// st.setFlag();
t1.interrupt();
// t2.interrupt();
break;
}
System.out.println("main...."+num);
}
System.out.println("over");
}
}