【Java并发编程】二.Java并发基础

1 Java线程简介

进程是程序的基本执行实体,进程可以容纳若干线程,是线程的容器。线程就是轻量级进程,线程的运行成本远远小于进程,所以我们用多线程去设计并发程序,而不是多进程。

线程的生命周期

jdk1.5以后,在java.lang.Thread的内部枚举类State中定义了线程的几种状态:

public enum State {
        /**
         * 新建一个线程,但还没有启动。
         */
        NEW,
        /**
         * 线程调用了start方法开始执行
         */
        RUNNABLE,
        /**
         *线程阻塞于锁(同步代码块或同步方法)
         */
        BLOCKED,
        /**
         * 线程处于等待状态,调用了如下方法:
         *  Object#wait(),Thread#join(),LockSupport.park()
         */
        WAITING,
        /**
         * 线程处于一个明确时间的等待状态,调用了如下方法:
         *  Thread.sleep,Object#wait(long), Thread#join(long)  
         *  LockSupport#parkNanos,  LockSupport#parkUntil 
         */
        TIMED_WAITING,

        /**
         * 中止状态:表示线程执行完毕
         */
        TERMINATED;
}

2 线程基本操作

2.1 新建线程

方法1:继承自Thread类,并重载其run方法,写成内部类就是:

Thread thread = new Thread() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
};

方法2:实现Runnable接口,使用Thread构造方法传入

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello");
    }
});

在jdk8中推荐使用lambda表达式构建Runnable:

//单行
Thread thread1 = new Thread(() -> System.out.println("Hello"));
//多行
Thread thread2 = new Thread(() -> {
    System.out.println("Hello");
    System.out.println("World");
});
//也可以单独构建runnable
Runnable task = () -> System.out.println("Hello");

启动线程,需要调用Thread#start() 方法(注意不是run方法):

 Runnable task = () -> System.out.println("Hello");
 new Thread(task).start();

2.2 终止线程Thread#stop()

使用Thread#stop()方法可以立即终止一个线程,不过这个方法会把执行到一半的线程强行终止,jdk中已经不再推荐使用。

Thread thread = new Thread(() -> {
        while (true) {        
                System.out.print("Hello ");
                try {
                    Thread.sleep(3000);//模拟耗时操作
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("World");
        }

});
thread.start();
Thread.sleep(1000);
thread.stop();

新建一个线程,要求线程每次完整的打印Hello World,当用了stop方法,你会发现只打印了Hello,造成了数据不一致。解决方法就是增加一个终止标志位,来让线程决定什么时候自己停止。

public class StopThread {
    volatile static boolean isStop = false;//标志位


    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.print("Hello ");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("World");
                if (isStop) {
                    break;
                }
            }
            System.out.println("已终止");
        });
        thread.start();
        Thread.sleep(1000);
        isStop = true;//终止

    }
}

2.3 线程中断 (interrupt)

上节我们使用了终止标志位来正确地终止线程,而在JDK中对线程提供了更好的中断支持,Thread类提供了三个方法,来支持中断操作:

public void interrupt()   //把线程设置为中断状态(注意:并不会真正中断线程,仅改变状态)
public boolean isInterrupted()  //获取线程是否为中断状态
public static boolean isInterrupted()  获取线程是否为中断状态,并清除中断状态

使用方法如下:

public class InterruptedTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName()+"线程被中断");
                    break;
                }
                System.out.println("hello world");
            }
        },"t1");
        thread.start();

        Thread.sleep(2000);

        System.out.println(thread.getName() + "当前中断状态:" + thread.isInterrupted());
        thread.interrupt();
    }
}

中断对Thread.sleep()的影响

Thread.sleep会让当前线程休眠,当线程休眠中调用中断方法时,会抛出一个InterruptedException方法。

 public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(10000);
            } catch (InterruptedException e) {
                System.out.println("线程被中断:" + e.getMessage());
            }
        });
        thread.start();

        Thread.sleep(1000);
        thread.interrupt();
}

2.4 线程的等待(wait)与通知(notify)

Object类提供了两个方法 wait、notify来实现线程的等待与通知

public class WaitNotify {
    static final Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        Runnable task1 = () -> {
            System.out.println("task1 start");
            synchronized (object) {
                System.out.println("task1 enter lock");
                try {
                    Thread.sleep(1000);
                    System.out.println("task1 wait");
                    object.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task1 is notified by task2");
            }

        };
        Runnable task2 = () -> {
            System.out.println("task2 start");
            synchronized (object) {
                System.out.println("task2 enter lock");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("task2 start");
                object.notify();
            }

        };

        new Thread(task1).start();
        new Thread(task2).start();
    }
}

2.5 线程的挂起

Thread的suspend()方法会导致线程挂起,但不会释放锁
Thread的resume()方法会继续执行该线程。

由于挂起时并不会释放锁资源,这两个方法已经过时

public class SuspendTest {

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("hello:");
                try {
                    Thread.sleep(300);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        Thread.sleep(1000);
        thread.suspend();//此时thread线程被挂起
        System.out.println("suspend...3s");//挂起thread 3秒
        Thread.sleep(3000);
        thread.resume();//thread线程恢复
    }
}

2.6 线程等待join

很多时候,线程之间需要互相协作来进行工作。比如下面的例子:
使用thread线程进行计算1到100的相加结果,main输出结果

public class JoinTest {
    volatile static int i = 0;

    @Test
    public void test() {
        Thread thread = new Thread(() -> {
            for (int j = 1; j <= 100; j++) {
                i = i + j;
            }
        });
        thread.start();

        System.out.println(i);//使用thread线程进行计算1到100万的相加结果,main输出结果
    }
}

结果却输出0,原因是main线程没有等到thread线程执行完。
使用join方法改写上面的例子:

public class JoinTest {
    volatile static int i = 0;

    @Test
    public void test() throws InterruptedException {
        Thread thread = new Thread(() -> {
            for (int j = 1; j <= 100; j++) {
                i = i + j;
            }
        });
        thread.start();

        thread.join();//这里main线程会停下来等待thread线程执行完
        System.out.println(i);//输出5050
    }
}

2.7 线程谦让yield

public static native void yield();

执行这个方法后会让出CPU资源,当前线程会和其他线程一起进行CPU资源的争夺。

3 volatile

volatile关键字保证了不同线程对其他线程修改数据的后可见性。

4 线程组

线程组可以把一些线程放到同一组中,对这组线程进行统一管理:

public class ThreadGroupTest {
    @Test
    public void test1() {
        ThreadGroup group = new ThreadGroup("group 1");

        Thread t1 = new Thread(group, () -> System.out.println("i am t1"));
        Thread t2 = new Thread(group, () -> System.out.println("i am t2"));

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

        //对线程组各种操作
        group.stop();
        group.interrupt();
        //...等等
    }
}

5 守护线程

守护线程是一种特殊的线程,当非守护线程全都退出时,守护线程也会退出。

public class DaemonTest {
    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(() -> {
            while (true) {
                System.out.println("i am Daemon thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.setDaemon(true);//如果注释掉这行,thread将一直会循环打印下去
        thread.start();
        Thread.sleep(3000);
    }
}

6 线程优先级

线程优先级使用 Thread#setPriority(int)指定。

Thread thread = new Thread();
thread.setPriority(Thread.MAX_PRIORITY);//Thread.MAX_PRIORITY = 10
thread.setPriority(Thread.NORM_PRIORITY);//=5
thread.setPriority(Thread.MIN_PRIORITY);//=1

值得注意的是:设置优先级并不能完全保证优先级高的线程竞争资源更有优势。

7.synchronized

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值