博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Java并发编程】二.Java并发基础
阅读量:4291 次
发布时间:2019-05-27

本文共 8421 字,大约阅读时间需要 28 分钟。

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");});//也可以单独构建runnableRunnable 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 = 10thread.setPriority(Thread.NORM_PRIORITY);//=5thread.setPriority(Thread.MIN_PRIORITY);//=1

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

7.synchronized

synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:

1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
2. 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
3. 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;

转载地址:http://bphgi.baihongyu.com/

你可能感兴趣的文章
信号量与互斥锁
查看>>
linux 查看CPU个数,核数
查看>>
string 序列化
查看>>
va_start(),va_end()函数应用
查看>>
crontab命令
查看>>
State Threads——异步回调的线性实现
查看>>
va_start va_end
查看>>
共享内存,共享缓冲区 一对多
查看>>
无锁队列的实现
查看>>
CAS原子操作实现无锁及性能分析
查看>>
Linux 互斥锁、原子操作实现原理
查看>>
搭建简单hls直播测试服务
查看>>
共享内存的数据同步
查看>>
Cache和Buffer的区别
查看>>
50个sql语句
查看>>
MYSQL sql 语句性能分析
查看>>
C++操作Redis数据库
查看>>
python yield用法
查看>>
python pipe模块用法
查看>>
安装完 MySQL 后必须调整的 10 项配置
查看>>