Java多线程初级学习总结

Java多线程初级学习总结

  • 一.Java创建线程的几种方法
  • 二.Thread类常用方法
  • 三.sleep方法与wait方法的区别
  • 四.start方法与run方法的区别
  • 五.Synchronized与Lock的区别
  • 补充.并发与并行
  • 六.线程的生命周期(码出高效)
  • 七.多线程的使用
  • 八.守护线程
  • 九.线程4种终止方法
  • 十.Volatile总结
  • 十一.AQS
  • 十二. 三大方法、七大参数、四种拒绝策略
  • 十三. CAS
  • 十四. 线程池
  • 十五. 解决线程安全的办法
  • 附录

一.Java创建线程的几种方法

  1. 继承Thread类
public class MyThread extends Thread {
	@Override
	public void run() {
		super.run();
		System.out.println("你好");
	}
}
------------------------------------------------
public class ThreadTest {
	public static void main(String[] args) {
		MyThread myThread = new MyThread();
		myThread.start();
	}
}
  1. 实现Runnable接口
public class MyThread1 implements Runnable {
	@Override
	public void run() {
		System.out.println("你好");
	}
}

--------------------------------------------------
public class MyThread1Test {
	public static void main(String[] args) {
		MyThread1 myThread1 = new MyThread1();
		Thread thread = new Thread(myThread1);
		thread.start();
	}
}
  1. 实现Callable接口
public class MyThread3 implements Callable<String> {

	@Override
	public String call() throws Exception {
		return "hello";
	}
}

--------------------------------------------------
public class Thread3Test {
	public static void main(String[] args) throws ExecutionException, InterruptedException {
		MyThread3 myThread3 = new MyThread3();
		FutureTask futureTask = new FutureTask(myThread3);
		new Thread(futureTask).start();
		System.out.println(futureTask.get());
	}
}

  1. 使用线程池
public class TestCallable implements Callable<String> {
	@Override
	public String call() throws Exception {
		return "通过线程池实现多线程";
	}
}
--------------------------------------------------
public class NewTest {
	public static void main(String[] args) throws ExecutionException, InterruptedException {
		TestCallable testCallable = new TestCallable();
		ExecutorService executorService = Executors.newFixedThreadPool(3);
		// 提交任务
		Future<String> future = executorService.submit(testCallable);
		String result = future.get();
		System.out.println(result);
	}
}

Java多线程初级学习总结_第1张图片

二.Thread类常用方法

  1. join方法
    A线程调用B线程的Join方法,将会使A等待B执行直到等待B线程终止,如果传入time参数将会使A等待B执行time时间,如果time时间到达,将会切换进A线程继续执行A线程。
  2. yield方法
    yield方法:当前线程放弃CPU的使用切换其他线程使用,执行此方法会向系统线程调度器(scheduler)发出一个暗示,告诉当前Java线程打算放弃对CPU的使用,但该暗示有可能被调度器忽略。
  3. interrupt方法
    该方法终端当前线程的执行,允许当前线程对自身进行终端,否则将会校验调用方法线程是否有该线程的权限。
  4. interrupted
    查看当前线程是否处于中断状态,该方法特殊之处在于如果调用成功,当前线程的interrupt status清除。所以如果连续两次调用该方法第二次返回false
  5. stop(已过时)
    由于stop方法可以让一个线程A终止掉另一个线程B,被终止的线程B会立即释放锁,这可能会使对象处于不一致状态。

三.sleep方法与wait方法的区别

  1. sleep方法属于Thread类,而wait方法属于Object类中的。
  2. sleep方法导致程序暂停给了暂停时间,让出CPU给其他线程,但他的监控状态依旧保持着,到了时间又会恢复运行状态。
  3. 在调用sleep方法的过程中线程不会释放掉锁对象。
  4. 当调用wait方法时,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后,该线程才进入对象锁定池,准备获取对象锁进入运行状态。

四.start方法与run方法的区别

  1. start方法用来启动线程真正实现了多线程运行。这时无需等待run方法执行完毕可以继续执行下面的代码。
  2. 通过调用Thread类的start方法来启动一个线程,此时线程是处于就绪状态并没有运行。
  3. 方法run()称为线程体,包含了要执行的这个线程的内容,线程进入了运行状态,开始运行run函数中的代码,run()方法运行结束,此时线程终止,然后CPU再调度其他线程。

五.Synchronized与Lock的区别

  1. Synchronized是Java内置的关键字,Lock是Java的一个类。
  2. Synchronized无法获取锁的状态,Lock可以判断是否获取到了锁。
  3. Synchronized会自动释放锁,Lock必须手动释放锁!如果不释放会造成死锁。
  4. Synchronized 线程1(获取锁,阻塞),线程2(等待,傻傻的等待),Lock锁就不一定会等下去。
  5. Synchronized可重入锁,不可中断,非公平的,Lock锁是可重入的锁,公平不公平可以设置。
  6. Synchronized适合少量的代码同步问题,Lock适合大量的同步代码。

补充.并发与并行

六.线程的生命周期(码出高效)

  1. NEW 初始状态/新建状态,是线程被创建且未启动的状态。创建线程的方式有三种:第一种是继承自Thread类,第二种是实现Runnable接口,第三种是实现Callable接口。相比第一种,推荐第二种,因为继承自Thread类往往不符合里氏代换原则,而实现Runnable接口可以使编程更加灵活,对外暴露的细节比较少,让使用者专注于实现线程的run()方法上。第三种Callable接口的call()声明如下:
    Java多线程初级学习总结_第2张图片
    由此可知,Callable与Runnable有两点不同:第一,可以通过call()方法获得返回值。前两种方式都有一个共同的缺陷,即在任务执行完成后,无法直接获取执行结果,需要截取共享变量获取,而Callable和Future则很好地解决了这个问题;第二,call()可以抛出异常。而Runnable只有通过setDefaultUncaughtExceptionHandler()的方式才能在主线程中捕获到子线程异常。

  2. RUNNABLE,即就绪状态,是调用start()之后运行之前的状态。线程的start()不能多次调用,否则会抛出IllegalStateException异常。

  3. RUNNING即运行状态,是run()正在执行时的线程的状态。现成可能会由于某些因素而退出RUNNING,如时间、异常、锁、调度等。

  4. BLOCKED即阻塞状态,进入此状态有以下几种情况。
    a.同步阻塞:锁被其他线程占用。
    b.主动阻塞:调用Thread的某些方法,主动让出CPU执行权,比如sleep()、join()等。
    c.等待阻塞:执行了wait()

  5. DEAD,即终止状态,是run()执行结束,或因异常退出后的状态,此状态不可逆转。

再用医生坐诊的例子说明 医生并发地处理多个病人的询问、开化验单、查看化验结果、开药等工作,任何一个环节一旦出现数据混淆,都可能引发严重的医疗事故。延伸到计算机的结程处理过程中,因为各个线程轮流占用 CP 的计算资源,可能会出现某个线程尚未执行完就不得不中断的情况,容易导致线程不安全。例如,在服务端某个高并发业务共享某用户数据,首先 线程执行用户数据的查询任务 但数据尚未返回就退出 CPU 时间片,然后 线程抢占了 PU 资源执行并覆盖了该用户数据最后线程返回到执行现场,直接将 线程处理过后的用户数据返回给前端,导致页面显示数据错误。为保证线程安全,在多个线程并发地竞争共享资源时,通常采用同步机制协调各个线程的执行,以确保得到正确的结果。

七.多线程的使用

package com.ruoyi.framework.config;

import com.ruoyi.common.utils.Threads;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 线程池配置
 *
 * @author ruoyi
 **/
@Configuration
public class ThreadPoolConfig {
	// 核心线程池大小
	private int corePoolSize = 50;

	// 最大可创建的线程数
	private int maxPoolSize = 200;

	// 队列最大长度
	private int queueCapacity = 1000;

	// 线程池维护线程所允许的空闲时间
	private int keepAliveSeconds = 300;

	@Bean(name = "threadPoolTaskExecutor")
	public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setMaxPoolSize(maxPoolSize);
		executor.setCorePoolSize(corePoolSize);
		executor.setQueueCapacity(queueCapacity);
		executor.setKeepAliveSeconds(keepAliveSeconds);
		// 线程池对拒绝任务(无线程可用)的处理策略
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		return executor;
	}

	/**
	 * 执行周期性或定时任务
	 */
	@Bean(name = "scheduledExecutorService")
	protected ScheduledExecutorService scheduledExecutorService() {
		return new ScheduledThreadPoolExecutor(corePoolSize,
				new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build(),
				new ThreadPoolExecutor.CallerRunsPolicy()) {
			@Override
			protected void afterExecute(Runnable r, Throwable t) {
				super.afterExecute(r, t);
				Threads.printException(r, t);
			}
		};
	}
}

或者加@Async注解

八.守护线程

守护线程也称为服务线程,他是后台线程,它有一个特性即为用户提供公共服务,在没有用户线程可服务的时候会自动离开。
优先级:守护线程的优先级比较低。
设置:通过setDaemon(true)来设置成为守护线程。
example:垃圾回收就是一个守护线程。

九.线程4种终止方法

  1. 正常运行结束
  2. 使用退出标志
    一般run()方法执行完毕,线程就会正常结束,然而常常有些线程是伺服线程。他们需要长时间运行,只有在外部某些条件满足的情况下,才能关闭这些线程。例如最直接的方法就是设一个boolean类型的标志通过这个标志为true或者false来控制while循环。
public volatile boolean exit = false;
	
	public void run(){
		while(!exit){
			
		}
	}
  1. 使用Interrupt()方法结束
    使用Interrupt()方法不会真正使这个线程停下来,仅仅是给线程发一个信号告诉它,他应该结束了。
  2. stop()方法但是不安全。

十.Volatile总结

Volatile是JVM提供最轻量级的一个关键字,说到Volatile首先说到我们计算的模型,CPU和内存之间的线程效率是差了好几个数量级但是为了保证他们之间的计算,不影响CPU的计算,然后中间有好多LLV那种缓存,我们线程在这个缓存中去工作,首先取数据会从主内存取到工作内存中,在工作内存中计算完之后再传回去,这个就有一个问题多线程之间的可见性,如何保证。在计算机层面有很多协议,在JVM它为了解决这些比较复杂的东西,它提供了像JMM这种模型,像被Volatile修饰的一个变量他就可以保证这个变量在所有线程间的可用性,在这个线程修改这个变量后,他可以立刻刷回到主内存中,它在使用时会立刻从主内存中取出刷新那个值,Volatile它是不保证原子性,像自增自减操作它是不能保证的。

十一.AQS

十二. 三大方法、七大参数、四种拒绝策略

十三. CAS

十四. 线程池


十五. 解决线程安全的办法

Java多线程初级学习总结_第3张图片
Java多线程初级学习总结_第4张图片

附录

1.并发编程
2. 多线程的应用
3. 多线程实战
4. 多线程补充

你可能感兴趣的:(Java多线程,java,学习,jvm)