首页 > 代码库 > 再读多线程

再读多线程

一,概念

当代操作系统中,可以独立并发执行的基本单元。

轻量:占用系统资源少

独立:操作系统中可以陆地调度和分派的基本单位

共享:共享进程中的资源


二,实现线程

更推荐集成Runnable接口方式?


三,线程生命周期

新建:线程刚刚创建完毕

可运行:启动线程后

运行:操作系统调度中

阻塞/等待:等待资源或者时间片

消亡:退出run方法


四,常用API

静态 针对当前进程

currentThread

yield

sleep


实例方法 针对指定线程

start

setPriority/getPriority(1-10 不设置是5 不一定高优先级就能先执行)

setName/getName

setDaemon/isDaemon(守护线程是其他线程结束之后自动消亡的线程)


五,线程同步

/*
 * DESCRIPTION : 
 * USER : zhouhui
 * DATE : 2017/8/1 15:50
 */
public class SynchronizedTest {


    /**
     * method1:对方法进行锁
     */
    public synchronized void method1(){

    }

    /**
     * method2:对当前对象锁
     */
    public void method2(){
        synchronized (this){

        }
    }

    /**
     *method3:对当前对象锁
     */
    public void method3(){
        synchronized (SynchronizedTest.this){

        }
    }


    /**
     *method4:对静态方法锁
     * 静态方法属于类,所以是对类上锁
     */
    public synchronized static void method4(){

    }

    /**
     *method5:对class锁
     */
    public static void method5(){
        synchronized (SynchronizedTest.class){

        }
    }

    /**
     * method6:对成员变量锁
     */
    private Integer i = 0;
    public void method6(){
        synchronized (i){

        }
    }

}

Java语法规定,任何线程执行同步方法、同步代码块 之前,必须先获取对应的监视器。

其中method1,method2,method3都是对当前对象上锁。

method4,method5对当前类上锁

method6是对属性上锁。

对方法上锁和对代码块上锁的区别是,如果方法太大,对方法上锁的开销要大的多。


method2和method6的区别:

public class Test {

    public static void main(String[] args) {
        Data data = new Data();
        new Thread(new MyRunnable(data),"A").start();
        new Thread(new MyRunnable(data),"B").start();
    }

}

class Data{

    private Integer i = 0;

    public void show(){
        while (true){
            synchronized (i){
                if(i == 30){
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " Before :" + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                i ++;
                System.out.println(Thread.currentThread().getName() + " After :" + i);

            }
        }
    }
}

class MyRunnable implements Runnable{

    private Data data;

    public MyRunnable(Data data){
        this.data = data;
    }

    public void run() {
        data.show();
    }
}

synchronized (i){

synchronized (this){


会产生不同的结果。

对this上锁能够正常的在30时停止,对i上锁会打印如下:

A Before :0
A After :1
B Before :1
A Before :1
A After :2
A Before :2
B After :3
B Before :3
B After :4
A After :5
B Before :5
B After :6
B Before :6
A Before :6
A After :7
A Before :7
B After :8
B Before :8
B After :9
A After :9
B Before :9
B After :10
B Before :10
A Before :10
A After :11
A Before :12
B After :12
A After :13
B Before :13
A Before :13
A After :15
B After :15
A Before :15
A After :16
B Before :16
A Before :16
A After :17
A Before :17
B After :18
B Before :18
A After :19
A Before :19
B After :20
B Before :20
A After :21
A Before :21
B After :22
B Before :22
A After :23
A Before :23
B After :24
B Before :24
A After :25
A Before :25
B After :26
B Before :26
A After :27
A Before :27
B After :28
B Before :28
A After :29
A Before :29
B After :30
A After :31
A Before :31
A After :32
A Before :32
A After :33
A Before :33
A After :34
A Before :34
A After :35
A Before :35

但是有时候是可以成功的。

如果对i上锁,能够保证同一时间只能一个线程对i进行操作。如果一个线程能够在另外一个线程i++之前获得i那么,他是能够正常i==30停止的,但是如果在另外一个线程i++之后获得i那么,他就会直接跳过30,而继续运行。

对this上锁,意味着对整个对象上锁,只能一个线程操作代码块,保证数据的完整性。


六,线程间通信

Object对象提供了wait() notify() notifyAll()方法,被所有子类继承。

wait()获得当前对象锁的线程,释放掉当前的锁,无限期的等待,必须被其他的线程唤醒。

notify() 唤醒任意一个waiting的线程到ready状态,是否变成running状态,还需要线程调度器的调度。

notifyAll()唤醒所有的waiting的线程到ready状态。

线程间的通信其实就是停止和唤醒的过程。


以下代码实现两个线程对同一个对象的+1和-1操作,相互停止和唤醒。

/*
 * DESCRIPTION : 
 * USER : zhouhui
 * DATE : 2017/8/1 17:28
 */
public class NumberHolder {
    private int number;

    public synchronized void increase() {
        if (0 != number) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 能执行到这里说明已经被唤醒
        // 并且number为0
        number++;
        System.out.println(number);

        // 通知在等待的线程
        notify();
    }

    public synchronized void decrease() {
        if (0 == number) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }

        // 能执行到这里说明已经被唤醒
        // 并且number不为0
        number--;
        System.out.println(number);
        notify();
    }

}



 class IncreaseThread extends Thread {
    private NumberHolder numberHolder;

    public IncreaseThread(NumberHolder numberHolder) {
        this.numberHolder = numberHolder;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; ++i) {
            // 进行一定的延时
            try {
                Thread.sleep((long) Math.random() * 1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 进行增加操作
            numberHolder.increase();
        }
    }

}



 class DecreaseThread extends Thread {
    private NumberHolder numberHolder;

    public DecreaseThread(NumberHolder numberHolder) {
        this.numberHolder = numberHolder;
    }

    @Override
    public void run() {
        for (int i = 0; i < 20; ++i) {
            // 进行一定的延时
            try {
                Thread.sleep((long) Math.random() * 1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 进行减少操作
            numberHolder.decrease();
        }
    }
}



 class NumberTest {
    public static void main(String[] args) {
        NumberHolder numberHolder = new NumberHolder();

        Thread t1 = new IncreaseThread(numberHolder);
        Thread t2 = new DecreaseThread(numberHolder);

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

}

打印结果:

1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0


再读多线程