1 线程的基本概念

1.1 进程

进程的概念 : 应用程序运行的时候进入到内存,程序在内存中占用的内存空间(进程)。

1.2 线程(Thread)

在内存和CPU之间,建立一条连接通路,CPU可以到内存中取出数据进行计算,这个连接的通路,就是线程。
一个内存资源 : 一个独立的进程,进程中可以开启多个线程 (多条通路)。
并发: 同一个时刻多个线程同时操作了同一个数据
并行: 同一个时刻多个线程同时执行不同的程序
多线程与多进程的区别:
本质的区别在于每个进程都拥有自己的一套变量,而线程则共享数据。

2 java实现线程程序的方法

2.1 java.lang.Thread类

一切都是对象,线程也是对象,Thread类是线程对象的描述类。
实现线程程序的步骤 :
(1)定义类继承Thread
(2)子类重写方法run
(3)创建子类对象
(4)调用子类对象的方法start()启动线程

//- 定义类继承Thread
//- 子类重写方法run
public class SubThread extends Thread {
    public void run(){
        for(int x = 0 ; x < 50 ;x++)
            System.out.println("run..."+x);
    }
}
public static void main(String[] args) {
    //创建线程程序
    SubThread subThread = new SubThread();
    //调用子类对象的方法start()启动线程
    //启动线程,JVM调用方法run
    subThread.start();
    for(int x = 0 ; x < 50 ;x++)
        System.out.println("main..."+x);
}

2.1.1 Thread类方法

(1)getName():返回线程的名字,返回值是String类型
(2)currentThread():静态调用,作用是返回当前的线程对象
(3)Thread类的方法 join():解释,执行join()方法的线程,它不结束,其它线程运行不了
(4)Thread类的方法 static yield():线程让步,线程把执行权让出

2.2 java.lang.Runnable接口

实现线程程序的步骤 :
(1)定义类实现接口
(2)重写接口的抽象方法run()
(3)创建Thread类对象
Thread类构造方法中,传递Runnable接口的实现类对象
(4) 调用Thread对象方法start()启动线程

//- 定义类实现接口
// - 重写接口的抽象方法run()
public class SubRunnable implements Runnable{
    @Override
    public void run() {
        for(int x = 0 ; x < 50 ;x++){
            System.out.println(Thread.currentThread().getName()+"x.."+x);
        }
    }
}
    public static void main(String[] args) {
        //创建接口实现类对象
        Runnable r = new SubRunnable();
        //创建Thread对象,构造方法传递接口实现类
        Thread t0 = new Thread(r);
        t0.start();
        for(int x = 0 ; x < 50 ;x++){
            System.out.println(Thread.currentThread().getName()+"x.."+x);
        }
    }

实现接口的好处:
接口实现好处是设计上的分离效果 : 线程要执行的任务和线程对象本身是分离的。
继承Thread重写方法run() : Thread是线程对象,run()是线程要执行的任务。
实现Runnable接口 : 方法run在实现类,和线程无关,创建Thread类传递接口的实现类对象,线程的任务和Thread没有联系, 解开耦合性。

3 线程安全

出现线程安全的问题出现的原因 : 多个线程同时操作同一个资源。
例:售票问题

/**
 * 票源对象,需要多个线程同时操作
 */
public class Ticket implements Runnable {

    //定义票源
    private int tickets = 100;

    @Override
    public void run() {
        while (true) {
            if (tickets > 0) {
                try {
                    Thread.sleep(10);//线程休眠,暂停执行
                }catch (Exception ex){}
                System.out.println(Thread.currentThread().getName()+" 出售第" + tickets + "张");
                tickets--;
            }else
                break;;
        }
    }
}
public static void main(String[] args) {
    Ticket ticket = new Ticket();
    //创建3个窗口,3个线程
    Thread t0 = new Thread(ticket);
    Thread t1 = new Thread(ticket);
    Thread t2 = new Thread(ticket);

    t0.start();
    t1.start();
    t2.start();
}

出现的问题:多个线程同时操作一个资源。

java多线程结束指定的线程 java多线程用法_后端

3.1 同步代码块

同步代码块可以解决线程安全问题 : synchronized(任意对象)
任意对象 : 同步中这个对象称为对象锁,简称锁, 也叫对象监视器。

synchronized(任意对象){
    //线程操作的共享资源
}

如下:对重写的run方法中操作资源的代码块加上synchronized关键字。

public void run() {
        while (true){
            synchronized (this){
                if (tickets>0){
                    try {
                        Thread.sleep(10);
                    }catch (Exception e){}

                    System.out.println(Thread.currentThread().getName()+"出售第"+tickets+"张");
                    tickets--;
                }
            }

        }
    }

通过同步代码块可避免多个线程同时使用一个资源的现象,保证当前的资源只有一个线程能够使用,使线程更加的安全。

同步代码块,如何保证线程的安全性?
同步代码块的执行原理 : 关键点就是对象锁,线程执行到同步,判断锁是否存在,如果锁存在,获取到锁,进入到同步中执行。执行完毕,线程出去同步代码块,将锁对象归还线程执行到同步,判断锁所否存在,
如果锁不存在,线程只能在同步代码块这里等待锁的到来。

3.2 同步方法

当一个方法中,所有代码都是线程操作的共享内容,可以在方法的定义上添加同步的关键字 synchronized , 同步的方法,或者称为同步的函数。同步方法中的对象锁为this对象。

@Override
    public void run() {
        while (true)
          sale();
    }

private static synchronized void sale(){
    if (tickets > 0) {
    try {
        Thread.sleep(20);//线程休眠,暂停执行
        } catch (Exception ex) {
    }
    System.out.println(Thread.currentThread().getName() + " 出售第" + tickets + "张");
    tickets--;
    }
}

4 死锁

死锁程序 : 多个线程同时争夺同一个锁资源,出现程序的假死现象。

同步代码块 : 线程判断锁,获取锁,释放锁,不出代码,锁不释放。

死锁的案例 : 同步代码块的嵌套

情景描述:如有两个人要分别进入两个房间,但是只有两把锁,第一个人拿了A锁,第二个人拿了B锁,结果导致第一个人进入房间后无法拿到锁B即无法进入小屋,而第二个人没有锁A也无法进入小屋,由于在同步代码块锁不释放,所以双方互相等待对方释放资源,发生死锁。

java多线程结束指定的线程 java多线程用法_开发语言_02

/**
 * 实现死锁程序
 */
public class ThreadDeadLock implements Runnable{

    private boolean flag ;

    public ThreadDeadLock(boolean flag){
        this.flag = flag;
    }

    @Override
    public void run() {
        while (true){
            //同步代码块的嵌套
            if (flag){
                //先进入A锁同步
                synchronized (LockA.lockA){
                    System.out.println("线程获取A锁");
                    //在进入另一个同步B锁
                    synchronized (LockB.lockB){
                        System.out.println("线程获取B锁");
                    }
                }
            }else {
                //先进入B锁同步
                synchronized (LockB.lockB){
                    System.out.println("线程获取B锁");
                    //再进入另一个同步锁A锁
                    synchronized (LockA.lockA){
                        System.out.println("线程获取A锁");
                    }
                }
            }
        }
    }
}
public class LockA {
    public static LockA lockA = new LockA();
}
public class LockB {
    public static LockB lockB = new LockB();
}
    public static void main(String[] args) {
        ThreadDeadLock threadDeadLock = new ThreadDeadLock(true);
        ThreadDeadLock threadDeadLock2 = new ThreadDeadLock(false);

        new Thread(threadDeadLock).start();
        new Thread(threadDeadLock2).start();
    }

打印结果:

java多线程结束指定的线程 java多线程用法_java_03


线程死锁,都无法获取对法需要的锁,锁死不动。

5 JDK5新特性Lock锁

  • JDK5新的特性 : java.util.concurrent.locks包. 定义了接口Lock, Lock接口替代了synchronized,可以更加灵活。
  • Lock接口的方法
    void lock() :获取锁
    void unlock():释放锁
  • Lock接口的实现类ReentrantLock

例如,售票问题,使用Lock锁代替synchronized,同样可以达到线程安全的效果。

public class Ticket implements Runnable{
    //定义票源
    private int tickets = 100;
    //获取Lock接口的实现类对象
    private Lock lock = new ReentrantLock();

    @Override
    public void run() {
        while (true){
            sale();
        }
    }
    private void sale(){  //同步方法
        //获取锁
        lock.lock();
            if (tickets>0){
                try {
                    Thread.sleep(10);
                }catch (Exception e){}
                System.out.println(Thread.currentThread().getName()+"出售第"+tickets+"张");
                tickets--;
            }
            //释放锁
        lock.unlock();
    }
}