Java多线程

2020-10-10  本文已影响0人  小王_min

Java 多线程编程

Java给多线程编程提供了内置的支持。一个多线程程序包含两个或多个能并发运行的部分。程序的每一部分都称作一个线程,并且每个线程定义了一个独立的执行路径。

多线程是多任务的一种特别的形式。多线程比多任务需要更小的开销。

这里定义和线程相关的另一个术语:进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守候线程都结束运行后才能结束。

多线程能满足程序员编写非常有效率的程序来达到充分利用CPU的目的,因为CPU的空闲时间能够保持在最低限度。


一个线程的生命周期

线程的生命周期包含出生状态、就绪状态、运行状态、等待状态、休眠状态、阻塞状态和死亡状态7种状态。当用户创建线程时线程处于出生状态;在用户调用start()方法后线程处于就绪状态;当线程得到资源后进入运行状态;当在运行态调用wait()方法时线程处于等待状态,此时必须调用notify()方法才能被唤醒,notifyAll()可以唤醒所有处于等待状态下的线程;当线程调用sleep()方法时会进入休眠状态;如果一个线程在运行状态下发出输入/输出请求,该线程将进入阻塞状态,在其等待输入/输出结束时线程进入就绪状态;当线程的run()方法执行完毕时线程进入死亡状态。(具体内容可参考操作系统)

下图为线程生命周期状态图,可对照上述文字加以理解。

image.png image.png

线程的优先级

每一个Java线程都有一个优先级,这样有助于操作系统确定线程的调度顺序。

Java线程优先级:(1-10)

关于优先级的方法:

getPriority():返回线程优先级的值

setPriority():改变线程的优先级

说明:

测试优先级的实例

package com.briup.bigdata.zookeeper.code.thread;

import java.util.concurrent.CountDownLatch;

public class TestThreadPriority {

    public static void main(String[] args) throws InterruptedException {

        CountDownLatch cdl = new CountDownLatch(2);

        Thread thread1 = new Thread(() -> runFun(cdl));

        Thread thread2 = new Thread(() -> runFun(cdl));

        thread1.setPriority(Thread.MAX_PRIORITY);

        thread1.start();

        cdl.countDown();

        thread2.setPriority(Thread.MIN_PRIORITY);

        thread2.start();

        cdl.countDown();

    }

    private static void runFun(CountDownLatch cdl){

        try {

            cdl.await();

            for (int i = 0; i < 10; i++) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

            }

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

运行结果:

image.png

创建一个线程

Java提供了三种创建线程方法:


通过实现Runnable接口来创建线程

创建一个线程,最简单的方法是创建一个实现Runnable接口的类。

为了实现Runnable,一个类只需要执行一个方法调用run(),声明如下:

public void run()

你可以重写该方法,重要的是理解的run()可以调用其他方法,使用其他类,并声明变量,就像主线程一样。

在创建一个实现Runnable接口的类之后,你可以在类中实例化一个线程对象。

Thread定义了几个构造方法,下面的这个是我们经常使用的:

Thread(Runnable threadOb,String threadName);

这里,threadOb 是一个实现Runnable 接口的类的实例,并且 threadName指定新线程的名字。

新线程创建之后,你调用它的start()方法它才会运行。

void start();

实例

下面是一个创建线程并开始让它执行的实例:

package com.briup.bigdata.zookeeper.code.thread;

public class MyThread01 implements Runnable {

    public static void main(String[] args) throws InterruptedException {

        new Thread(new MyThread01()).start();

        try {

            for(int i = 10; i > 0; i--) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

                Thread.sleep(100);

            }

        } catch (InterruptedException e) {

            System.out.println(Thread.currentThread().getName() + " was interrupted.");

        }

        System.out.println("Exiting " + Thread.currentThread().getName() + " thread.");

    }

    @Override

    public void run() {

        try {

            for (int i = 0; i < 10; i++) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

                Thread.sleep(1000);

            }

        } catch (InterruptedException e) {

                System.out.println(Thread.currentThread().getName() + " was interrupted.");

        }

        System.out.println("Exiting " + Thread.currentThread().getName() + " thread.");

    }

}

运行结果:

image.png

通过继承Thread来创建线程

创建一个线程的第二种方法是创建一个新的类,该类继承Thread类,然后创建一个该类的实例。

继承类必须重写run()方法,该方法是新线程的入口点。它也必须调用start()方法才能执行。该方法尽管被列为一种多线程实现方式,但是本质上也是实现了 Runnable 接口的一个实例。

实例

package com.briup.bigdata.zookeeper.code.thread;

public class MyThread02 extends Thread {

    public static void main(String[] args) {

        new MyThread02().start();

        try {

            for (int i = 0; i < 5; i++) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

                Thread.sleep(1000);

            }

        } catch (InterruptedException e) {

            System.out.println(Thread.currentThread().getName() + " was interrupted.");

        }

        System.out.println("Exiting " + Thread.currentThread().getName() + " thread.");

    }

    @Override

    public void run() {

        try {

            for (int i = 0; i < 5; i++) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

                Thread.sleep(1000);

            }

        } catch (InterruptedException e) {

            System.out.println(Thread.currentThread().getName() + " was interrupted.");

        }

        System.out.println("Exiting " + Thread.currentThread().getName() + " thread.");

    }

}

运行结果:

image.png

Thread 方法

下表列出了Thread类的一些重要方法:

序号 方法 描述
1 public void start() 使该线程开始执行;java虚拟机调用该线程的 run 方法。
2 public void run() 如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则该方法不执行任何操作返回。
3 public final void setName(String name) 改变线程名称,使之与参数 name 相同。
4 public final void setPriority(int priority) 更改线程的优先级。
5 public final void setDaemon(boolean on) 将该线程标记为守护线程,主线程结束,守护线程也跟着结束。
6 public final void join(long millisec) 等待该线程终止的时间最长为 millis 毫秒。
7 public void interrupt() 中断线程。
8 public final boolean isAlive() 测试线程是否处于活动状态。
9 public final void stop() 停止线程,已过时

测试线程是否处于活动状态。 上述方法是被Thread对象调用的。下面的方法是Thread类的静态方法。

序号 方法 描述
1 public static void yield() 暂停当前正在执行的线程对象,并执行其他线程。
2 public static void sleep(long millisec) 在指定的毫秒数内让当前正在执行的线程休眠(暂停执行),此操作受到系统计时器和调度程序精度和准确性的影响。
3 public static boolean holdsLock(Object x) 仅当当前线程在指定的对象上保持监视器锁时,才返回 true。
4 public static Thread currentThread() 返回对当前正在执行的线程对象的引用。
5 public static void dumpStack() 将当前线程的堆栈跟踪打印至标准错误流。

实例

如下的ThreadClassDemo 程序演示了Thread类的一些方法:

package com.briup.bigdata.zookeeper.code.thread;

public class ThreadClassDemo {

    public static void main(String[] args) throws InterruptedException {

        Thread thread1 = new Thread(() -> {

            try {

                for (int i = 0; i < 5; i++) {

                    // yield

                    if (i == 3) Thread.yield();

                    System.out.println(Thread.currentThread().getName() + " : " + i);

                    Thread.sleep(1000);

                }

            } catch (InterruptedException e){

                System.out.println(Thread.currentThread().getName() + " was interrupted.");

            }

            System.out.println(Thread.currentThread().getName() + " was exit.");

        }, "thread1");

        thread1.start();

        for (int i = 0; i < 5; i++) {

            System.out.println(Thread.currentThread().getName() + " : " + i);

        }

        Thread joinThread = new Thread(() -> {

            for (int i = 0; i < 5; i++) {

                System.out.println(Thread.currentThread().getName() + ":" + i);

            }

        }, "joinThread");

        joinThread.start();

        Thread.sleep(3000);

        for (int i = 5; i < 10; i++) {

            System.out.println(Thread.currentThread().getName() + " : " + i);

            Thread.yield();

        }

    }

}

运行结果:

image.png

通过 Callable 和 Future 创建线程

实例:

package com.briup.bigdata.zookeeper.code.thread;

import java.util.concurrent.Callable;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.FutureTask;

/**

* 实现Callable接口的类中的call方法是有返回值的

*/

public class MyThread03 implements Callable<Integer> {

    public static void main(String[] args) {

        MyThread03 myThread = new MyThread03();

        FutureTask<Integer> ft = new FutureTask<>(myThread);

        for(int i = 0;i < 100;i++) {

            System.out.println(Thread.currentThread().getName()+":"+i);

            if(i==20){

                Thread.yield();

                new Thread(ft,"有返回值的线程").start();

            }

        }

        try {

            System.out.println("子线程的返回值:"+ft.get());

        } catch (InterruptedException e) {

            e.printStackTrace();

        } catch (ExecutionException e) {

            e.printStackTrace();

        }

    }

    @Override

    public Integer call() throws Exception {

        int i = 0;

        for (; i < 100; i++) {

            System.out.println(Thread.currentThread().getName() + ":" + i);

        }

        return i;

    }

}

运行结果:


创建线程的三种方式的对比


线程的几个主要概念

在多线程编程时,你需要了解以下几个概念:


多线程的使用

有效利用多线程的关键是理解程序是并发执行而不是串行执行的。例如:程序中有两个子系统需要并发执行,这时候就需要利用多线程编程。

通过对多线程的使用,可以编写出非常高效的程序。不过请注意,如果你创建太多的线程,程序执行的效率实际上是降低了,而不是提升了。

请记住,上下文的切换开销也很重要,如果你创建了太多的线程,CPU花费在上下文的切换的时间将多于执行程序的时间!


线程同步

多线程中,可能会发生两个线程抢占资源的问题,例如两个人同时过一个独木桥。所以Java提供线程同步机制来防止这些资源访问的冲突。

问题实例:火车售票系统

package com.briup.bigdata.zookeeper.code.thread.example;

public class TicketSaleSystem implements Runnable {

    private int ticket = 10;

    @Override

    public void run() {

        for (int i = 0; i < 10; i++) {

            if (this.ticket > 0) {

                if ("票贩子A".equals(Thread.currentThread().getName())) {

                    try {

                        Thread.sleep(10);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                System.out.println(Thread.currentThread().getName() + "卖出 1 张票," + " 余量:" + --this.ticket);

            }

        }

    }

    public static void main(String[] args) {

        TicketSaleSystem tss = new TicketSaleSystem();

        Thread t1 = new Thread(tss, "票贩子A");

        Thread t2 = new Thread(tss, "票贩子B");

        Thread t3 = new Thread(tss, "票贩子C");

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

image.png

线程同步的几种方法:

同步代码块实例:

1.实现Runnable接口

package com.briup.bigdata.zookeeper.code.thread.example;

public class SynchronizedTicketSaleSystem01 implements Runnable {

    private int ticket = 10;

    @Override

    public void run() {

        synchronized (SynchronizedTicketSaleSystem01.class) {

            for (int i = 0; i < 10; i++) {

                if (this.ticket > 0) {

                    if ("票贩子A".equals(Thread.currentThread().getName())) {

                        try {

                            Thread.sleep(10);

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                        }

                    }

                    System.out.println(Thread.currentThread().getName() + "卖出 1 张票," + " 余量:" + --this.ticket);

                }

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedTicketSaleSystem01 tss = new SynchronizedTicketSaleSystem01();

        Thread t1 = new Thread(tss, "票贩子A");

        Thread t2 = new Thread(tss, "票贩子B");

        Thread t3 = new Thread(tss, "票贩子C");

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

image.png

2.继承Thread类

package com.briup.bigdata.zookeeper.code.thread.example;

public class SynchronizedTicketSaleSystem02 extends Thread {

    private static int ticket = 10;

    @Override

    public void run() {

        synchronized (SynchronizedTicketSaleSystem02.class) {

            for (int i = 0; i < 10; i++) {

                if (this.ticket > 0) {

                    if ("票贩子A".equals(Thread.currentThread().getName())) {

                        try {

                            Thread.sleep(10);

                        } catch (InterruptedException e) {

                            e.printStackTrace();

                        }

                    }

                    System.out.println(this.getName() + "卖出 1 张票," + " 余量:" + --ticket);

                }

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedTicketSaleSystem02 t1 = new SynchronizedTicketSaleSystem02();

        t1.setName("票贩子A");

        SynchronizedTicketSaleSystem02 t2 = new SynchronizedTicketSaleSystem02();

        t2.setName("票贩子B");

        SynchronizedTicketSaleSystem02 t3 = new SynchronizedTicketSaleSystem02();

        t3.setName("票贩子C");

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

image.png

说明:

同步代码块实例:

1.实现Runnable接口

package com.briup.bigdata.zookeeper.code.thread.example;

public class SynchronizedTicketSaleSystem03 implements Runnable {

    private int ticket = 10;

    @Override

    public void run() {

        fun();

    }

    // 同步方法

    private synchronized void fun(){

        for (int i = 0; i < 10; i++) {

            if (this.ticket > 0) {

                if ("票贩子A".equals(Thread.currentThread().getName())) {

                    try {

                        Thread.sleep(10);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                System.out.println(Thread.currentThread().getName() + "卖出 1 张票," + " 余量:" + --this.ticket);

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedTicketSaleSystem03 tss = new SynchronizedTicketSaleSystem03();

        Thread t1 = new Thread(tss, "票贩子A");

        Thread t2 = new Thread(tss, "票贩子B");

        Thread t3 = new Thread(tss, "票贩子C");

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

image.png

2.继承Thread类

package com.briup.bigdata.zookeeper.code.thread.example;

public class SynchronizedTicketSaleSystem04 extends Thread {

    private static int ticket = 10;

    @Override

    public void run() {

        fun();

    }

    public static synchronized void fun(){

        for (int i = 0; i < 10; i++) {

            if (ticket > 0) {

                if ("票贩子A".equals(Thread.currentThread().getName())) {

                    try {

                        Thread.sleep(10);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

                System.out.println(Thread.currentThread().getName() + "卖出 1 张票," + " 余量:" + --ticket);

            }

        }

    }

    public static void main(String[] args) {

        SynchronizedTicketSaleSystem04 t1 = new SynchronizedTicketSaleSystem04();

        t1.setName("票贩子A");

        SynchronizedTicketSaleSystem04 t2 = new SynchronizedTicketSaleSystem04();

        t2.setName("票贩子B");

        SynchronizedTicketSaleSystem04 t3 = new SynchronizedTicketSaleSystem04();

        t3.setName("票贩子C");

        t1.start();

        t2.start();

        t3.start();

    }

}

运行结果:

image.png

说明:

锁实例:Lock完全用Java写成,在java这个层面是无关JVM实现的(java1.5)

package com.briup.bigdata.zookeeper.code.thread.example;

import java.util.concurrent.locks.ReentrantLock;

public class ThreadLock {

    private static int ticket = 10;

    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {

        Runnable r1 = () -> {

            while (true){

                try {

                    lock.lock();

                    if (ticket > 0){

                        try {

                            Thread.sleep(10);

                        }catch (InterruptedException e) {

                            e.printStackTrace();

                        }

                        System.out.println(Thread.currentThread().getName() + ":售票,票余量:" + --ticket);

                    }else break;

                }finally {

                    lock.unlock();

                }

            }

        };

        new Thread(r1).start();

        new Thread(r1).start();

    }

}

synchronized和lock锁的区别:


线程死锁

不同线程分别占用对方所需资源不主动释放,都在等待对方释放自己所需的同步资源,就会形成线程死锁。

实例:

package com.briup.bigdata.zookeeper.code.thread.example;

import java.util.Date;

public class ThreadDeadLock {

    private static Object o1 = new Object();

    private static Object o2 = new Object();

    public static void main(String[] args) {

        Runnable lockA = () -> {

            try {

                System.out.println(new Date().toString() + " LockA 开始执行");

                while (true){

                    synchronized (ThreadDeadLock.o1){

                        System.out.println(new Date().toString() + " LockA 锁住 o1");

                        Thread.sleep(3000);

                        synchronized (ThreadDeadLock.o2){

                            System.out.println(new Date().toString() + " LockA 锁住 o2");

                        }

                    }

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        };

        Runnable lockB = () -> {

            try {

                System.out.println(new Date().toString() + " LockB 开始执行");

                while (true){

                    synchronized (ThreadDeadLock.o2){

                        System.out.println(new Date().toString() + " LockB 锁住 o2");

                        Thread.sleep(3000);

                        synchronized (ThreadDeadLock.o1){

                            System.out.println(new Date().toString() + " LockB 锁住 o1");

                        }

                    }

                }

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        };

        new Thread(lockA).start();

        new Thread(lockB).start();

    }

}

运行结果:

image.png

解决方法:

上一篇 下一篇

猜你喜欢

热点阅读