1、 线程的概念
现在的操作系统都是都任务的,在同一时刻允许运行多个程序。现在,人们都有单台拥
有多个CPU的计算机,俗称双核或者四核。但是并发执行的进程数目并不是由CPU数目决定的,操作系统将CPU的时间分配给每一个进程,给人一种并行出来的感觉。
多线程程序在较低层次上扩展了多任务的概念,一个程序执行多个任务。通常,每一个任务成为一个线程。能够同时运行一个以上线程的程序称为多线程程序。一个进程中至少有一个线程,在Java中这个线程运行代码存在与main方法中,该线程成为主线程。
多线程和多进程到底有哪些区别呢?本质的区别在于每个进程拥有自己的一整套变量,而线程则共享数据。共享变量使线程直间的通信比进程之间的通信更有效、更容易。此外,在有些操作系统中,与进程相比,线程更“轻量级”,创建、撤销一个线程比启动新进程的开销要小的多。
在实际应用中多线程非常有用。例如:一个浏览器可以同时下载多幅图片。
2、 定义线程
Java中已经定义好了一个Thread类。创建线程呢有两种方法,一种方法是将类声明为
Thread
的子类。该子类应重写Thread
类的run
方法。
定义线程的步骤:
1、 定义一个类并继承Thread类。
2、 复写Thread类中的run()方法。
3、 调用线程的start()方法;
示例如下:
class Thr extends Thread
{
public void run()
{
for(int i=0;i<100;i++)
{
System.out.println("Thr run");
}
}
}
class ThreadDemo
{
public static void main(String[] arge)
{
Thr a = new Thr();
a.start();
for(int i=0;i<100;i++)
{
System.out.println("Hello Java");
}
}
}
为什么要覆盖run方法呢?
Thread类用于描述一个线程,该类就定义一个功能,用于存储线程要运行的代码,该存储功能就是run方法。
线程被创建之后有下述四种基本状态执行状态、就绪状态、阻塞状态、消亡状态。运行状态是程序表示线程正在获得处理机而运行;就绪状态是指线程已具备各种执行条件,一旦获得CPU就可以执行的状态;阻塞状态是指线程在执行中因某事件而受阻,比如掉用了Sleep()、wait(),处于暂停执行状态。消亡状态是指线程结束不再被运行比如调用stop();
线程的名字:每个线程有自己默认的名字,但是我们也可以自定义线程名字。默认的名字为直接调用getName()方法,名称为Thread-编号,编号从0开始。自定义名字为在Thread类中有一个构造方法为Thread(String name)方法,我们可以在继承时调用这个父类方法如下:
class Thr extends Thread
{
public Thr(Stringname)
{
super(name);
}
}
创建线程的另一种方法是声明实现Runnable
接口的类。该类然后实现run
方法。然后可以分配该类的实例,在创建Thread
时作为一个参数来传递并启动。1、定义类并实现Runnable接口。2、覆盖Runnable接口中的run()方法。3、通过Thread类建立线程对象。4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数。5、调用Thread类的start()方法开启线程并调用Runnable接口的run()方法。
示例如下:
class Ticket implements Runnable//创建Ticket类并实现Runnable接口
{
public static int ticket = 100;
public void run()//实现Runnable接口的run()方法
{
while(true)
{
if(ticket>0)
System.out.println(Thread.currentThread().getName()+"~~~~~~"+ticket--);
}
}
}
class TicketDemo
{
public static void main(String[] arge)
{
Ticket t = new Ticket();//创建Ticket类对象
Thread a1 = new Thread(t);//将Ticket类对象作为参数传入Thread类对象
Thread a2 = new Thread(t);
a1.start();
a2.start();
}
}
为什么有了Thread类来创建线程还要有Runnable接口来创建线程呢?
在我们创建线程时是继承Thread类并复写run()方法,但是如果我们有一个需要进行多线程的运行的类,但是这个类已经继承过其他类,那么就不能再继承Thread类了,这个时候Runnable的优势就体现出来了,所以这个时候就要用到Runnable接口来实现。
3、多线程的安全问题
问题原因:当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行一部分,还没执行完,另一条线程参与进来执行,导致共享数据的错误。
解决办法:对多条操作共享数据的语句,只能让一个线程都执行完。在执行过程中,其他的线程不可以参与执行。
Java对于多线程的安全问题提供了专业的解决方式。同步代码块。
Synchronized(对象)
{
需要被同步的代码;
}
对象如同锁,持有锁的线程可以在同步中执行。没有持有锁的线程即使获取CPU的执行权,也不能执行,因为锁还未被持有的线程释放。那究竟什么时候需要同步代码呢?
1、 必须要有两个以上或者两个以上的线程。
2、 必须是多个线程使用同一个锁。
那么如何查找代码是否有安全问题呢?
1、 明确哪些代码是多线程运行代码。
2、 明确共享数据。
3、 明确多线程运行代码中哪些语句是操作共享数据的。
4、死锁
所谓死锁是指多个线程在运行过程中因争夺资源而造成的一种僵局,当线程在出于这种
僵局状态时,若无外力作用,它们将无法再向前推进。示例:
class Test implements Runnable
{
private boolean flag;
public Test(boolean flag)
{
this.flag = flag;
}
public void run()
{
if(flag)
{
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("else locka");
}
}
}
}
}
class MyLock
{
static Object locka = new Object();
static Object lockb = new Object();
}
class DeathLockTest
{
public static void main(String[] arge)
{
Thread t1 = new Thread(new Test(true));
Thread t2 = new Thread(new Test(false));
t1.start();
t2.start();
}
}
这个程序将很有可能发生死锁。
5、线程间的通信
线程间的通信就是多个线程在操作同一个资源但是操作的动作不同。下面提供一个生产者和消费者的示例里面涉及Lock.lock()、Lock.unlock()、conditions.await()、condition.signal()、的用法,示例如下:
import java.util.concurrent.locks.*;
class Resource//创建一个资源类
{
private String name;
private int count = 1;
private boolean flag = false;
private Lock lock = new ReentrantLock();
private Condition condition_pro = lock.newCondition();
private Condition condition_con = lock.newCondition();
public void set(String name)throws InterruptedException//输入资源方法
{
lock.lock();
try
{
while(flag)
condition_pro.await();
this.name = name+"--"+count++;
System.out.println(Thread.currentThread().getName()+"………生产者………"+this.name);
flag = true;
condition_con.signal();
}
finally
{
lock.unlock ();
}
}
public void out()throws InterruptedException//输出资源方法
{
lock.lock();
try
{
while(!flag)
condition_con.await();
System.out.println(Thread.currentThread().getName()+"………消费者………………………"+this.name);
flag = false;
condition_pro.signal();
}
finally
{
lock.unlock();
}
}
}
class Producer implements Runnable//建立一个生产者
{
private Resource res;
Producer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.set("商品");
}
catch(InterruptedException e)
{
}
}
}
}
class Consumer implements Runnable//建立一个消费者
{
private Resource res;
Consumer(Resource res)
{
this.res = res;
}
public void run()
{
while(true)
{
try
{
res.out();
}
catch(InterruptedException e)
{
}
}
}
}
class ProducerConsumerDemo2
{
public static void main(String[] arge)
{
Resource r = new Resource();
Producer pro = new Producer(r);
Consumer con = new Consumer(r);
Thread t1 = new Thread(pro);
Thread t3 = new Thread(pro);
Thread t2 = new Thread(con);
Thread t4 = new Thread(con);
t1.start();
t2.start();
t3.start();
t4.start();
}
}