本文章首发微信公众号:IT笔记分享java
扫码关注我 😊 git
进程是操做系统运行程序的基本单位,是一次程序的执行。简单来讲一个进程就是一个运行中的程序。github
线程能够认为是在进程中独立运行的子任务。一个进程会有多个线程。安全
进程和线程最大区别就是,各个进程是独立的,而线程却不必定,同一进程中的线程多是相互影响的。进程属于操做心痛范围的,同一时间会运行多个程序,每一个进程上的又会有多个线程在执行同个或不一样的任务。bash
多线程就是多个线程同时运行或交替运行。单核CPU的话是顺序执行,也就是交替运行。多核CPU的话,由于每一个CPU有本身的运算器,因此在多个CPU中能够同时运行。微信
在Java的JDK开发包中,实现多线程主要有两种,一种是继承Thread类,另外一种是实现Runnable接口。其实还有不少事项多线程方法,例如:使用ExecutorService、Callable、Future实现由返回结果的多线程,这是经过线程池建立的任务,能够参考《一篇搞懂线程池》。如今咱们只来具体来说一下前两种方法的使用。多线程
在建立多线程前,咱们先来看一下Thread的类结构图:ide
能够看出Thread实现了Runnable接口,其实Runnable和Thread的区别就在于继承Thread建立多线程不支持多继承,为了多继承彻底可使用Runnable替换,二者建立的线程本质上没有区别。@FunctionalInterface是Java8的函数式接口,方便lambda表达式使用。函数
咱们建立一个Mythread.java继承Thread,重写run方法:this
package main.java.com.xiaosen.Mythread;
/** * @author xiaosen * @date 2019/2/24 10:36 * @description */
public class MyThread extends Thread{
@Override
public void run() {
super.run();
System.out.println("this is MyThread");
}
}
复制代码
运行类的代码以下:
import main.java.com.xiaosen.Mythread.MyThread;
public class Main {
public static void main(String[] args) {
// 继承Thread
MyThread myThread = new MyThread();
myThread.start();
// lambda表达式的使用
new Thread(() -> System.out.println("this is lambda thread")).start();
System.out.println("Hello World!");
}
}
复制代码
运行结果以下:
this is MyThread
Hello World!
this is lambda thread
复制代码
这里一低昂要注意myThread调用的是start()方法,若是调用mythread.run()那么就是单纯的方法调用,而不是建立线程去执行。关于lambda表达式只给出使用方法,暂不作介绍。
从运行结果能够看见"Hello World!"的输出在两个线程中间,说明使用多线程和代码的顺序无关。CPU会以随机的时间来调用线程中的run方法。
建立MyRunnable实现Runnable接口:
package main.java.com.xiaosen.myrunnable;
/** * @author xiaosen * @date 2019/2/24 11:20 * @description */
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("run MyRunnable");
}
}
复制代码
main方法使用:
// 实现Runnable
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable);
thread.start();
System.out.println("Hello World!");
复制代码
输出内容:
Hello World!
run MyRunnable
复制代码
使用Runnable方法须要用到Thread的构造方法。
自定义的实例变量有共享和不共享之分,在多线程交互的时候很重要。
建立一个类继承Thread:
public class NotShareThread extends Thread{
private int count = 5;
public NotShareThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
super.run();
while (count>0){
count--;
System.out.println("线程" + this.currentThread().getName() + ":count=" + count);
}
}
}
public static void main(String[] args){
NotShareThread notShareThread1 = new NotShareThread("A");
NotShareThread notShareThread2 = new NotShareThread("B");
NotShareThread notShareThread3 = new NotShareThread("C");
notShareThread1.start();
notShareThread2.start();
notShareThread3.start();
}
复制代码
执行结果:
线程A:count=4
线程B:count=4
线程C:count=4
线程B:count=3
线程A:count=3
线程A:count=2
线程A:count=1
线程B:count=2
线程C:count=3
线程B:count=1
线程A:count=0
线程B:count=0
线程C:count=2
线程C:count=1
线程C:count=0
复制代码
从输出结果能够看到每个线程都有各自的count变量,彼此变量不共享。
共享数据就是多个线程同时访问同一个变量,好比买火车票,多个线程同时操做剩余票数。
public class ShareThread extends Thread {
private int count = 5;
@Override
public void run() {
super.run();
count--;
System.out.println("线程" + this.currentThread().getName() + ":count=" + count);
}
public static void main(String[] args){
ShareThread shareThread = new ShareThread();
Thread a = new Thread(shareThread, "A");
Thread b = new Thread(shareThread, "B");
Thread c = new Thread(shareThread, "C");
Thread d = new Thread(shareThread, "D");
Thread e = new Thread(shareThread, "E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
复制代码
输出:
线程B:count=2
线程E:count=0
线程D:count=1
线程C:count=2
线程A:count=2
复制代码
能够发现A,B,C三个线程的值都是2,产生非线程安全状况,咱们应该避免这种状况发生。这是由于在执行i--操做分如下几步:
在这三步中,若是有多线程同时访问,就会出现线程不安全状况。
咱们来看看线程安全的代码:
@Override
public void run() {
super.run();
synchronized (ShareThread.class){
count--;
System.out.println("线程" + this.currentThread().getName() + ":count=" + count);
}
}
复制代码
咱们加上synchronized代码块加锁,那么只有拿到所锁的线程才能够执行代码块的内容,没拿到则不断尝试获取锁,知道拿到为止。
代码Github