——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-
一、概述
在一个程序里可以在运行时创建多个线程,以达到同时处理数据,提高整体的运行效率的目的。
二、创建线程
在 Java 语言中,创建线程有两种方式,第一种是创建一个类,让其继承 Thread 类,并重写 run 方法,在其中写入要执行的语句:
// 创建一个类,让其继承 Thread 类
class Demo extends Thread {
public void run () {
while(true){
System.out.println("线程");
}
}
}
// 在主函数中创建该类的对象并启动线程
class ThreadDemo {
public static void main (String[] args) {
Demo d = new Demo();
d.start();
}
}
因为 Demo 类继承了 Thread 类,因此可以使用 start 方法启动线程并执行 run 方法中的语句。
第二种创建线程的方法是创建一个类,让其实现 Runable 接口,并在类中重写 run 方法,在其中写入要执行的语句:
// 创建一个类,让其实现 Runable 接口
class Demo implements Runable {
public void run () {
while (true) {
System.out.println("线程");
}
}
}
// 在主函数中创建该类的对象,并使用该对象创建线程
class ThreadDemo {
public static void main (String[] args) {
Demo d = new Demo();
Thread t = new Thread(d);
t.start();
}
}
使用第二种方法创建线程可以将一个对象传递给多个线程,从而实现类中数据的共享。
三、线程的状态
线程的状态可以分为被创建、运行、冻结、和消亡状态,线程通过创建 Thread 类对象被创建,通过 start 方法开始运行,通过 sleep 或 wait 方法进入冻结状态,在进过 sleep 方法设置的时间后,或是被 notify 方法唤醒后恢复运行运行状态,run 方法中的语句执行完毕后或通过 stop 方法(此方法在较新的 Java 版本中已过期)结束运行。
四、多线程的安全性
当 run 方法中存在多条语句,则可能存在某个线程在执行到某一条语句之后失去 CPU 的执行权,转到另一个线程执行 run 方法中的语句的情况,最终导致执行的结果出现问题。
// 定义一个类继承 Thread 类,在 run 方法定义循环,要求在成员变量 a 大于 0 时打印 a 的值
class Demo implements Runable {
int a = 100;
public void run () {
while (true) {
if (a > 0) {
System.out.println(a);
a--;
}
}
}
}
// 在主函数中创建并启动两个线程执行 run 方法中语句
class ThreadDemo {
public static void main (String[] args) {
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
}
}
在上面的代码中,有可能出现在 a 变量变为 1 时,t1 线程在执行了 if (a>0) 判断语句之后失去了 CPU 的执行权,然后 t2 线程获得了 CPU 的执行权,此时 a 值为 1,t2 线程线程执行打印语句,a 的值减为 0,此时 t1 线程重新或的执行权,由于已经执行的判断语句,t1 线程会直接执行打印语句,并打印出 0 值,这不符合最初的设计要求。
为了解决线程的安全性问题,需要在执行语句中加入 Synchronized 代码块或用 Synchronized 修饰方法,上面的代码在引入同步之后可以改写为:
class Demo implements Runable {
int a = 100;
public void run () {
while (true) {
// 使用同步代码块
Synchronized (this)
{
if (a > 0) {
System.out.println(a);
a--;
}
}
}
}
}
使用同步代码块需要有一个对象,这里使用调用函数的对象 this。
class Demo implements Runable {
int a = 100;
public void run () {
while (true) {
out();
}
}
// 定义同步方法
public Synchronized void out() {
if (a > 0) {
System.out.println(a);
a--;
}
}
}
在使用了同步代码块或同步方法后,在一个线程开始执行代码块中或方法中的语句后,该线程执行完毕前,其他进程既不能执行该段代码或该方法,从而解决了线程的安全问题。
五、使用 Lock 接口实现同步
在 JDK 5.0 中引入新的 Lock 接口,使用 Lock 接口同样可以实现同步,解决多线程的安全性问题。上面的代码可以改写为:
// 在类中创建 Lock 接口的子类对象,并使用 Lock 中的方法实现同步
class Demo implements Runable {
int a = 100;
private Lock lock = new ReentrantLock();
public void run () {
while (true) {
lock.lock();
if (a > 0) {
System.out.println(a);
a--;
}
lock.unlock();
}
}
}
Lock 接口使用两个不同的方法获取和释放锁,此外,还支持多个 Condition 对象,可以实现更为灵活的线程控制。