JAVA多线程编程学习

一、创建方法

Extends Thread 和 implement Runnable
两者都需要重写run()方法(用@Override注解)
两者区别在于Runnable方法可以共享CPU资源,因为创建的同一个对象的不同线程

RunnableThread runThread = new RunnableThread();
Thread thread = new Thread(runThread,”RunThread”);
Thread.start();

二、线程的生命周期

在这里插入图片描述

创建——就绪

thread.start()

阻塞——终止

 @Override
public void run() {
    try {
        while (true) {
            // 执行任务...
        }
    } catch (InterruptedException ie) {  
        // 由于产生InterruptedException异常,退出while(true)循环,线程终止!
    }
}

说明:在while(true)中不断的执行任务,当线程处于阻塞状态时(Thread.sleep()和wait()函数时),调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意对InterruptedException的捕获务一般放在while(true)循环体的外面,这样,在产生异常时就退出了while(true)循环。否则,InterruptedException在while(true)循环体之内,无法退出while循环,线程会一直执行。因此需要额外的添加退出处理。添加break 或 return就能解决该问题。

运行——终止

  1. 通过“中断标记”终止线程
 @Override
public void run() {
    while (!isInterrupted()) {
        // 执行任务...
    }
}

说明:isInterrupted()是判断线程的中断标记是不是为true。当线程处于运行状态,并且我们需要终止它时;可以调用线程的interrupt()方法,使用线程的中断标记为true,即isInterrupted()会返回true。此时,就会退出while循环。
注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。
2、通过“额外添加标记”(推荐)

 private volatile boolean flag= true;
protected void stopTask() {
    flag = false;
}
@Override
public void run() {
    while (flag) {
        // 执行任务...
    }
}

说明:线程中有一个flag标记,它的默认值是true;并且我们提供stopTask()来设置flag标记。当我们需要终止该线程时,调用该线程的stopTask()方法就可以让线程退出while循环。
注意:将flag定义为volatile类型,是为了保证flag的可见性。即其它线程通过stopTask()修改了flag之后,本线程能看到修改后的flag的值。
3、综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:

@Override
public void run() {
    try {
        // 1. isInterrupted()保证,只要中断标记为true就终止线程。
        while (!isInterrupted()) {
            // 执行任务...
        }
    } catch (InterruptedException ie) {  
// 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
    }
}

参考链接:https://www.cnblogs.com/skywang12345/p/3479949.html

三、线程的同步和异步

对synchronized(this)的一些理解
1、当两个并发线程访问同一个对象object中的这个synchronized(this)同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。

 public class Thread1 implements Runnable {
	public void run() {
		synchronized (this) {
			for (int i = 0; i < 5; i++) {
				System.out.println(Thread.currentThread().getName()
						+ " synchronized loop " + i);
			}
		}
	}
	public static void main(String[] args) {
		Thread1 t1 = new Thread1();
		Thread ta = new Thread(t1, "A");
		Thread tb = new Thread(t1, "B");
		ta.start();
		tb.start();
	}
}
结果:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4

2、然而,当一个线程访问object的一个synchronized(this)同步代码块时,另一个线程仍然可以访问该object中的非synchronized(this)同步代码块。
3、尤其关键的是,当一个线程访问object的一个synchronized(this)同步代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
4、第三个例子同样适用其它同步代码块。也就是说,当一个线程访问object的一个synchronized(this)同步代码块时,它就获得了这个object的对象锁。结果,其它线程对该object对象所有同步代码部分的访问都被暂时阻塞。
5、以上规则对其它对象锁同样适用.
参考链接:https://www.cnblogs.com/firstdream/p/8334149.html

四、线程分类

分为用户线程和守护线程,守护线程在用户线程结束后伴随虚拟机终止。

在这里插入图片描述

五、线程可见性

1、可见性:一个线程对共享变量值的修改,能够及时被其他线程看到
2、共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量(共享数据的访问权限都必须定义为private)
3、JAVA内存模型(JMM):描述了JAVA程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取出变量这样的底层细节。
(1) 所有变量都存储在主内存中
(2) 每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的一份拷贝)
在这里插入图片描述

两条规定
(1) 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
(2) 不同线程之间无法直接访问其他线程工作内存的变量,线程间变量值的传递需要通过主内存进行传递
4、共享变量可见性实现的原理
(1) 把工作内存1中更新过的变量值刷新到主内存
(2) 将主内存中最新的共享变量值更新到工作内存2中
5、可见性实现方式:Synchronized,Volatile
6、Synchronized:实现原子性(同步,互斥锁),内存可见性
两条规定
(1)线程解锁前,必须把共享变量的最新值刷新到主内存
(2)线程加锁时,清空工作内存共享变量值,在使用共享变量时重新从主内存读取最新值
线程解锁前对共享变量值的修改在下次加锁时对其他线程可见
重排序:代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的排序优化。
as-if-serial:无论如何重排序,程序执行的结果应该与代码顺序执行的结果一致
多线程程序交错执行,重排序可能导致内存可见性问题
导致共享变量在线程间不可见的原因
(1) 线程的交叉执行——原子性
(2) 重排序结合线程交叉执行——as-if-servial原子性
(3) 共享变量更新后的值没有在工作内存和主内存间及时更新——可见性
7、Volatile:可见性,不能保证voiatile变量复合操作的原子性
通过加入内存屏障和禁止重排序优化实现内存可见性
(1) 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
(2) 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令
Voiltile变量每次在被线程访问时,都强迫从主内存重读该变量值;当变量发生变化时,又强迫线程将最新值刷新到主内存。
Volatile不能保证voiatile变量复合操作的原子性
解决方案
(1) 使用synchronized关键字:synchronized(this)
(2) 使用ReentrantLock(java.until.concurrent.locks包):

 Lock lock = new ReentrantLock();
lock.lock();
try{
   //
}finally{
   lock.unlock();
}

Volatile适应场合
(1) 对变量的写入操作不依赖其当前值:boolean值,温度变化
(2) 该变量没有包含在具有其他变量的不变式中(如:low<up)
8、synchronized和volatile比较
(1)Volatile不需要加锁,比synchronized更轻量级,不会阻塞线程
(2)从内存可见性角度,volatile读相当于加锁,volatile写相当于解锁
(3)synchronized既保证可见性,又保证原子性,volatile只保证可见性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值