多线程,线程同步和锁


​1:线程的创建​


1:线程的创建

1-1:继承Thred类

package com.lingaolu;

public class CreateThread extends Thread{

@Override
public void run() {
System.out.println("子线程执行");
}

public static void main(String[] args) {
System.out.println(1);
new CreateThread().start();
System.out.println(2);
}
}

多线程,线程同步和锁_synchronized

1-2:实现Runnable接口

package com.lingaolu;

public class CreateThread implements Runnable{

@Override
public void run() {
System.out.println("子线程执行");
}

public static void main(String[] args) {
System.out.println(1);
new Thread(new CreateThread()).start();
System.out.println(2);
}
}

        或者使用lambda表达式

package com.lingaolu;

public class CreateThread{

public static void main(String[] args) {
System.out.println(1);
new Thread(()->System.out.println("子线程执行")).start();
System.out.println(2);
}
}

1-3:实现Callable<>接口,有返回值的线程

package com.lingaolu;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CreateThread implements Callable<String> {

public static void main(String[] args) throws ExecutionException, InterruptedException {
System.out.println(1);
FutureTask<String> futureTask = new FutureTask<>(new CreateThread());
new Thread(futureTask).start();
// 这个get方法是阻塞的
System.out.println(futureTask.get());
System.out.println(2);
}

@Override
public String call(){
System.out.println("子线程执行");
return "OK";
}
}

多线程,线程同步和锁_线程池_02

2:线程的其他操作

        线程有5个状态,如下

多线程,线程同步和锁_锁_03

2-1:线程停止

        我们不推荐线程使用**interrupt(),stop(),destroy()**等方法停止线程,而是自己写一个标记位来作为线程的终止变量

package com.lingaolu;


public class CreateThread implements Runnable{

private boolean flag = true;

@Override
public void run() {
while (flag){
System.out.println("线程执行");
}
}

// 调用方法,改变标志位的值来停止线程
public void stop(){
this.flag = false;
}

}

2-2:线程礼让yield



  • 礼让线程,让当前正在执行的线程暂停,但不阻塞
  • 将线程从运行状态转为就绪状态
  • 让cpu重新调度,礼让不一定成功,看cpu心情


package com.lingaolu;


public class CreateThread implements Runnable{

public static void main(String[] args) {
System.out.println(1);
new Thread(new CreateThread(),"线程1").start();
new Thread(new CreateThread(),"线程2").start();
System.out.println(2);
}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程执行开始");
// 线程礼让
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程执结束");
}

}

多线程,线程同步和锁_synchronized_04

2-3:线程强制执行join

        join可以让线程强制执行

package com.lingaolu;


public class CreateThread implements Runnable{

public static void main(String[] args) throws InterruptedException {
System.out.println(1);
Thread thread1 = new Thread(new CreateThread());
thread1.start();
for (int i = 1; i <= 5; i++) {
if(i==3){
// 当i=3时,强制让thread1执行
thread1.join();
}
System.out.println("主线程"+i);
}
System.out.println(2);
}

@Override
public void run() {
System.out.println("子线程执行......");
}

}

多线程,线程同步和锁_多线程_05

2-4:查看线程状态

package com.lingaolu;

import lombok.SneakyThrows;

public class CreateThread implements Runnable{

public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(new CreateThread());
Thread.State state = thread1.getState();
System.out.println("线程创建状态"+state);
thread1.start();
state = thread1.getState();
System.out.println("线程启动状态"+state);
while(state != Thread.State.TERMINATED){
if(state == Thread.State.TIMED_WAITING){
System.out.println("线程等待状态"+state);
}
Thread.sleep(100);
state = thread1.getState();
}
state = thread1.getState();
System.out.println("线程结束状态"+state);
}

@SneakyThrows
@Override
public void run() {
Thread.sleep(100);
System.out.println("子线程执行状态......"+Thread.currentThread().getState());
System.out.println("子线程执行......");
}
}

        运行结果

多线程,线程同步和锁_线程池_06

2-5:线程优先级priority

        Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行


  • 但是优先级高的只是意味着获得调度的高概率,并不是每次都会比优先级低的先调度,这个得看CPU的调度


package com.lingaolu;

import lombok.SneakyThrows;

public class CreateThread implements Runnable{

public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new CreateThread(),"t1");
Thread t2 = new Thread(new CreateThread(),"t2");
Thread t3 = new Thread(new CreateThread(),"t3");
Thread t4 = new Thread(new CreateThread(),"t4");
Thread t5 = new Thread(new CreateThread(),"t5");
t1.setPriority(1);
t1.start();
t2.setPriority(5);
t2.start();
t3.setPriority(Thread.MAX_PRIORITY);
t3.start();
t4.setPriority(2);
t4.start();
t5.setPriority(6);
t5.start();
System.out.println("主线程优先级为"+Thread.currentThread().getPriority());

}

@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"子线程执行......优先级为"+Thread.currentThread().getPriority());
}

}

多线程,线程同步和锁_锁_07

2-6:守护线程deamon



  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线层执行完毕,比如:监控内存,垃圾回收等等


package com.lingaolu;

public class CreateThread{

public static void main(String[] args) throws InterruptedException {
Thread girlFriendThread = new Thread(new GirlFriend());
// 守护主线程,主线程完毕,girlFriendThread也就完毕
girlFriendThread.setDaemon(true);
girlFriendThread.setPriority(10);
girlFriendThread.start();
}

}

class I implements Runnable{

@Override
public void run() {
while (true){
System.out.println("我一直守护女朋友");
}
}
}

class GirlFriend implements Runnable{
@Override
public void run() {
Thread thread = new Thread(new I());
// 默认false,false为正常线程,true为守护线程,守护上级线程
thread.setDaemon(true);
thread.start();
for (int i = 1; i <= 30; i++) {
System.out.println("女朋友线程执行"+i);
}
}
}

多线程,线程同步和锁_并发_08

4:并发与锁

4-1:初识并发问题

        3个线程同时抢20张票,更详细的用法这里有介绍​

package com.lingaolu;

import lombok.SneakyThrows;

public class CreateThread implements Runnable{

private Boolean flag = true;
private static int num = 20;

public static void main(String[] args) {
CreateThread createThread = new CreateThread();
new Thread(createThread,"111线程").start();
new Thread(createThread,"222线程").start();
new Thread(createThread,"333线程").start();
}

@SneakyThrows
@Override
public void run() {
while (flag) {
if (num > 0) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"抢了第"+num--+"张票");
} else {
flag = false;
}
}


}
}

        从结果可以看出,不仅有线程抢到了相同的票,还有负的票,这就是线程安全问题

多线程,线程同步和锁_锁_09

4-2:synchronized

        synchronized方法控制对“对象”的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。



  • 缺陷:若将一个大的方法申明为synchronized将会影响效率
  • 方法里面需要修改内容才需要锁,锁太多,浪费资源


package com.lingaolu;

import lombok.SneakyThrows;

public class CreateThread implements Runnable{

private Boolean flag = true;
private static int num = 20;
private Object obj = new Object();

public static void main(String[] args) {
CreateThread createThread = new CreateThread();
new Thread(createThread,"111线程").start();
new Thread(createThread,"222线程").start();
new Thread(createThread,"333线程").start();
}

@SneakyThrows
@Override
public void run() {
while (flag) {
synchronized (obj) {
if (num > 0) {
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "抢了第" + num-- + "张票");
} else {
flag = false;
}
}
}


}
}

4-3:死锁

        多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或者多个线程都在等待对方释放资源,都停止执行的情形。某一个同步块同时拥有“两个以上对象的锁”时,就可能会发生“死锁”问题。

package com.lingaolu;

import lombok.Data;
import lombok.SneakyThrows;

@Data
public class CreateThread implements Runnable{
private static Object obj1 = new Object();
private static Object obj2 = new Object();

private int num;

public CreateThread(int num) {
this.num = num;
}

public static void main(String[] args) {
new Thread(new CreateThread(1),"111线程").start();
new Thread(new CreateThread(2),"222线程").start();
}

@SneakyThrows
@Override
public void run() {
if(num==1){
synchronized (obj1) {
System.out.println(Thread.currentThread().getName()+"=====对象1");
Thread.sleep(1000);
synchronized (obj2) {
System.out.println(Thread.currentThread().getName()+"=====对象2");
Thread.sleep(1000);
}
}
}else {
synchronized (obj2) {
System.out.println(Thread.currentThread().getName()+"=====对象2");
Thread.sleep(1000);
synchronized (obj1) {
System.out.println(Thread.currentThread().getName()+"=====对象1");
Thread.sleep(1000);
}
}
}
}
}

        发生死锁,if和else的代码块里面都互相等待对方的锁

多线程,线程同步和锁_synchronized_10

        死锁解决,代码块不同时拥有2个对象锁以上,要分开

package com.lingaolu;

import lombok.Data;
import lombok.SneakyThrows;

@Data
public class CreateThread implements Runnable{
private static Object obj1 = new Object();
private static Object obj2 = new Object();

private int num;

public CreateThread(int num) {
this.num = num;
}

public static void main(String[] args) {
new Thread(new CreateThread(1),"111线程").start();
new Thread(new CreateThread(2),"222线程").start();
}

@SneakyThrows
@Override
public void run() {
if(num==1){
synchronized (obj1) {
System.out.println(Thread.currentThread().getName()+"=====对象1");
Thread.sleep(1000);
}
// 同步代码块锁分开
synchronized (obj2) {
System.out.println(Thread.currentThread().getName()+"=====对象2");
Thread.sleep(1000);
}
}else {
synchronized (obj2) {
System.out.println(Thread.currentThread().getName()+"=====对象2");
Thread.sleep(1000);
}
// 同步代码块锁分开
synchronized (obj1) {
System.out.println(Thread.currentThread().getName()+"=====对象1");
Thread.sleep(1000);
}
}
}
}

        成功解决

多线程,线程同步和锁_synchronized_11

5:线程通信wait和notify()

5-1:管程法

        并发协作模型“生产者/消费者模式”



  • 生产者:负责生产数据的模块(可能是方法,对象,线程,进程)
  • 消费者:负责处理数据的模块(可能是方法,对象,线程,进程)
  • 缓冲区:消费者不能直接使用生产者的数据,他们之间有个“缓冲区”
  • 生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据


package com.lingaolu;

import lombok.Data;

@Data
public class CreateThread {
public static void main(String[] args) {
DemandPool demandPool = new DemandPool();
new Productor(demandPool).start();
new programmer(demandPool).start();
}
}

// 生产者,产品
class Productor extends Thread{
DemandPool demandPool;
public Productor(DemandPool demandPool){
this.demandPool = demandPool;
}
// 添加需求
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
demandPool.push(new Demand(i));
}
}
}

// 消费者,程序员
class programmer extends Thread{
DemandPool demandPool;
public programmer(DemandPool demandPool){
this.demandPool = demandPool;
}

// 做需求
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
demandPool.pop();
}
}
}

// 产品,需求
class Demand{
// 需求编号
int id;

public Demand(int id) {
this.id = id;
}
}

// 需求池
class DemandPool{
// 需要一个容器大小
Demand[] demands = new Demand[5];
// 容器计数器
int count = 0;

// 产品加需求
public synchronized void push(Demand demand){
// 如果需求池满了,就需要等待程序员做需求
if(count == demands.length){
// 通知程序员做需求,产品等待加需求
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 如果没有满,产品就加需求
System.out.println("产品添加了第"+demand.id+"个需求");
demands[count] = demand;
count++;
// 可以通知程序员做需求
this.notifyAll();

}

// 程序员做需求
public synchronized Demand pop(){
// 判断是否有需求
if(count==0){
// 等待产品的需求
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 可以做需求
count--;
Demand demand = demands[count];
System.out.println("程序员做了第"+demand.id+"个需求");
// 通知产品加需求
this.notifyAll();
return demand;
}
}

        结果

多线程,线程同步和锁_线程池_12

5-2:信号灯法

        信号灯法就是使用一个标记位,判断标记位来等待启动,我们举个例子,产品 出什么需要,程序员就做什么需求

package com.lingaolu;

import lombok.Data;

@Data
public class CreateThread {
public static void main(String[] args) {
Demand demand = new Demand();
new Productor(demand).start();
new programmer(demand).start();
}
}

// 生产者,产品
class Productor extends Thread{
Demand demand;
public Productor(Demand demand){
this.demand = demand;
}
// 添加需求
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
this.demand.push(i);
}
}
}

// 消费者,程序员
class programmer extends Thread{
Demand demand;
public programmer(Demand demand){
this.demand = demand;
}

// 做需求
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
this.demand.pop();
}
}
}

// 需求
class Demand{
private int id;
private Boolean flag = true;

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public Demand() {
}

// 产品加需求
public synchronized void push(int id){
if(!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setId(id);
System.out.println("产品添加了第"+id+"个需求");
// 通知程序员做需求
this.notifyAll();
this.flag = !this.flag;
}

// 程序员做需求
public synchronized void pop(){
if(flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("程序员做了第"+id+"个需求");
// 通知产品加需求
this.notifyAll();
this.flag = !this.flag;
}
}

        结果

多线程,线程同步和锁_锁_13

6:线程池

6-1:线程池例子

        JDK5.0起提供了线程池相关API:ExecutorService和Executors


ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor


  • void execute(Runnable command):执行任务命令。没有返回值,一般用来执行Runnable
  • Future submit(Callable task):执行任务命令。有返回值,一般用来执行Callable
  • voidshutdown():关闭连接池



Executors:工具类,线程池的工厂,用于创建并返回不同类型的线程池



比较重要的几个类:


  • ExecutorService: 真正的线程池接口。
  • ScheduledExecutorService: 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
  • ThreadPoolExecutor: ExecutorService的默认实现。
  • ScheduledThreadPoolExecutor: 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现


package com.lingaolu;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CreateThread implements Runnable {
public static void main(String[] args) {
// 创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
// 执行线程
executorService.execute(new CreateThread());
executorService.execute(()->System.out.println("线程1执行"));
executorService.execute(()->System.out.println("线程2执行"));
executorService.execute(()->System.out.println("线程3执行"));
// 关闭连接
executorService.shutdown();
}

@Override
public void run() {
System.out.println("线程执行");
}
}

        结果

多线程,线程同步和锁_并发_14

6-2:自定义线程池(手动创建线程池,效果会更好)

        线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的

多线程,线程同步和锁_synchronized_15

package com.lingaolu;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class CreateThread{
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
4, // 核心线程数
8, // 最大线程数
5, // 存活时间
TimeUnit.SECONDS, // 秒
new LinkedBlockingDeque<>(300), // 队列大小300
new MyThreadFactory(new ThreadGroup("myThreadGroup"),"my-thread"), // 线程工厂
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略(默认),所谓拒绝策略,是指将任务添加到线程池中时,线程池拒绝该任务所采取的相应策略
);
while (true){
Thread.sleep(1000);
threadPoolExecutor.execute(()->{
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
});
}
}

static class MyThreadFactory implements ThreadFactory{
private AtomicInteger number = new AtomicInteger(0);
private ThreadGroup group;
private String namePrefix;
public MyThreadFactory(ThreadGroup group, String namePrefix) {
this.group = group;
this.namePrefix = namePrefix;
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group,r,namePrefix+"-"+number.getAndIncrement());
if (t.isDaemon()){
t.setDaemon(false);
}
if (t.getPriority() != Thread.NORM_PRIORITY){
t.setPriority(Thread.NORM_PRIORITY);
}
return t;
}
}

}

        定的核心线程数是4个

多线程,线程同步和锁_多线程_16