文章目录

  • 概念
  • 线程创建
  • 继承Thread类
  • 实现Runnable接口
  • 实现Callable接口
  • 补充知识点
  • 静态代理模式
  • Lamda表达式
  • 线程状态
  • 线程停止(stop)
  • 线程休眠(sleep)
  • 线程礼让(yield)
  • 线程强制执行(join)
  • 线程优先级
  • 守护进程
  • 线程同步
  • 线程不安全举例
  • 不安全买票
  • 不安全银行
  • 不安全集合
  • 同步方法
  • 同步方法,同步块(synchronized)
  • 安全买票代码
  • 安全取钱代码
  • 安全list代码
  • JUC
  • 死锁
  • Lock锁
  • 不加锁的情况
  • 加锁的代码
  • 线程协作
  • 生产者消费者问题
  • 管程法
  • 信号灯法
  • 线程池
  • 特点
  • 学习链接


概念

注意:很多多线程是模拟出来的,真正的多线程是指有多个cpu,即多核,如服务 器。如果是模拟出来的多线程,即在一个cpu的情况下,在同一个时间点,cpu只能 执行一个代码,因为切换的很快,所以就有同时执行的错觉。

  • 线程就是独立的执行路径;
  • 在程序运行时,即使没有自己创建线程,后台也会有多个线程,如主线程,gc线程;
  • main() 称之为主线程,为系统的入口,用于执行整个程序;
  • 在一个进程中,如果开辟了多个线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。
  • 对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制;
  • 线程会带来额外的开销,如cpu调度时间,并发控制开销。
  • 每个线程在自己的工作内存交互,内存控制不当会造成数据不一致

线程创建

继承Thread类

1.继承Thread类
2.重写run方法
3.创建对象调用star();
测试

package collectionstest; 
import java.lang.*; 
public class ThreadTest {
	public static void main(String[] args) {
		MyThread mt1 = new MyThread("1");
		MyThread mt2 = new MyThread("2222222222222222222222222222222222222222222222");
		MyThread mt3 = new MyThread("33333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333333");
		mt1.start();
		mt2.start();
		mt3.start();
		for(int i = 0;i<100;i++) {
			System.out.println("666666666666666666666");
		} 
	}
}

 

class MyThread extends Thread{
	private String k;
	MyThread(String n){
		this.k = n;
	}
	@Override
	public void run() { 
		for(int i = 0;i<100;i++) {
			System.out.println(k);
		}
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_优先级

实现Runnable接口

1.实现Runnable接口
2.实现run方法
3.创建对象,调用start方法
测试:

package collectionstest;

public class RunnableTest {
	public static void main(String[] args) {
		
		MyRunnable mr =new MyRunnable("0");
		Thread myThread = new Thread(mr);
		myThread.start();
		
		MyRunnable mr1 =new MyRunnable("11111111");
		Thread myThread1 = new Thread(mr1);
		myThread1.start();
		
	}
}
class MyRunnable implements Runnable{
	private String k;
	MyRunnable(String n){
		this.k = n;
	}
	@Override
	public void run() { 
		for(int i = 0;i<100;i++) {
			System.out.println(k);
		}
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_java_02

实现Callable接口

测试代码

package collectionstest;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

class myCallable implements Callable<Boolean>{
	private String msg;
	myCallable(String s){
		msg = s;
	}
	public Boolean call() {
		System.out.println(msg);
		return true;
	}
}
public class CallableTest {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		//创建目标对象
		myCallable mc0 = new myCallable("1");
		myCallable mc1 = new myCallable("22");
		myCallable mc2 = new myCallable("333");
		//创建执行服务
		ExecutorService es = Executors.newFixedThreadPool(3);
		//提交执行
		Future<Boolean> f0 = es.submit(mc0);
		Future<Boolean> f1 = es.submit(mc1);
		Future<Boolean> f2 = es.submit(mc2);
		//获取结果
		boolean r0 = f0.get();
		boolean r1 = f1.get();
		boolean r2 = f2.get(); 
		System.out.println(""+r0 + " " + r1 + " "+ r2);
		//关闭服务
		es.shutdownNow();
	} 
}

结果

Java 多线程GC JAVA 多线程 采集plc_System_03

补充知识点

静态代理模式

也就是让一个类帮助另一个类做事情(可以做一些额外的准备)。
代码

interface Marry{
	void happyMarry(); 
}
class Human implements Marry{
	String name;
	Human(String s){
		name = s;
	}
	public void happyMarry() {
		System.out.println(name + "结婚了");
	}
}
class WeddingCompany implements Marry {
	private Human human;
	WeddingCompany(Human a){
		human = a;
	}
	private void before() {
		System.out.println("结婚前的准备");
	}
	private void after() {
		System.out.println("结婚后的整理");
	}
	public  void happyMarry() {
		before();
		human.happyMarry();
		after();
	}
}
public class staticPorxy {
	public static void main(String[] args) {
		WeddingCompany weddingCompany = new WeddingCompany(new Human("一个人"));  
		weddingCompany.happyMarry();
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_System_04

Lamda表达式

只用一次的类: 内部类 -> 匿名内部类 -> 局部内部类 -> 匿名内部类 -> lamda表达式。
特点
避免匿名内部类定义过多
代码很简洁,只留下核心的逻辑。
函数式接口
只有唯一一个抽象方法。可以通过lamda表达式来创建该接口的对象。
代码

package ProcessThreadTest;
interface MyInterface{
	public void function();
}
interface MyInterface1{
	public void function(String a);
}
class ClassOne implements MyInterface{
	public void function() {
		System.out.println("普通类实现接口");
	} 
}
public class lamdaTest {
	//静态内部类
	static class ClassTwo  implements MyInterface{
		public void function() {
			System.out.println("静态内部类实现接口");
		}
	}
	public static void main(String[] args) {
		//普通方法调用不使用lamda表达式
		MyInterface myclass = new ClassOne();
		myclass.function();
		//静态方法调用
		myclass = new ClassTwo();
		myclass .function();
		//局部内部类调用
		class ClassThree implements MyInterface{
			public void function() {
				System.out.println("局部内部类实现接口");
			}
		}
		myclass = new ClassThree();
		myclass.function();
		//匿名内部类
		myclass = new MyInterface() {
			public void function() {
				System.out.println("匿名内部类实现接口");
			}
		};
		myclass.function();
		//lamda表达式实现
		myclass = ()->{
			System.out.println("lamda表达式");
		};
		myclass.function();
		//带参数
		MyInterface1 myclass1 = (String a)->{
			System.out.println("参数是:"+a);
		};
		myclass1.function("带参数");
		//省略参数类型
		myclass1= myclass1 = (a)->{
			System.out.println("参数是:"+a);
		};
		myclass1.function("省略类型");
		//省略括号 
		myclass1= myclass1 = a->{
			System.out.println("参数是:"+a);
		};
		myclass1.function("省略括号");
		//省略花括号(只有一行)
		myclass1= myclass1 = a -> System.out.println("参数是:"+a); 
		myclass1.function("省略花括号");
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_System_05

线程状态

线程的几种状态:

Java 多线程GC JAVA 多线程 采集plc_java_06


Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_07

Thread类的一些重要的方法:

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_08

线程停止(stop)

不建议使用stop方法和destory方法。

从Thread类的源码中可以看到,这个stop方法已经废弃。

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_09


建议线程通过循环次数自动停止下来(上文已实现)或者设置标志位。

标志位代码

package ProcessThreadTest;
class MyTheadStop implements Runnable{//此处不能继承Thread类,因为stop方法在Thread类中是
	boolean flag = true;
	public void run() {
		while(flag ==true) {
			System.out.println("hi!");
		}
	}
	public void stop() {
		this.flag = false;
	}
	
}
public class ThreadStopTest {
	public static void main(String[] args) {
		MyTheadStop  mts = new MyTheadStop();
		Thread myThread = new Thread(mts);
		myThread.start();
		for(int i = 0;i<300;i++) {
			if(i == 150) {
				mts.stop();
				System.out.println("线程停止了");
			}
			System.out.println(i);
		}
	}
}

Java 多线程GC JAVA 多线程 采集plc_java_10


. . . . . . . . .

Java 多线程GC JAVA 多线程 采集plc_java_11

线程休眠(sleep)

sleep (时间)指定当前线程阻塞的毫秒数;
sleep存在异常InterruptedException;
sleep时间达到后线程进入就绪状态;
sleep可以模拟网络延时,倒计时等。
每一个对象都有一个锁,sleep不会释放锁;
代码

package ProcessThreadTest;

import java.sql.Time;
import java.util.Date;

public class SleepTest implements Runnable {
	private int ticketNums = 1;
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			Date date = new Date() ;
			if(ticketNums > 10) {
				break;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName() + date + ":拿到了第"+ ticketNums +"张票");
			ticketNums++;
		}
	}
	
	public static void main(String[] args) {
		SleepTest sleepTest = new SleepTest();
		new Thread(sleepTest,"小明").start();
		new Thread(sleepTest,"小红").start();
		new Thread(sleepTest,"小亮").start();
	}
}

结果(读数据之前没有加锁,暂不解决)

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_12

线程礼让(yield)

礼让线程,让当前正在执行的线程暂停,但不阻塞。
将线程从运行状态转为就绪状态,让cpu重新调度,礼让不一定成功!
测试代码

package ProcessThreadTest;
class myYield implements Runnable{
	private String name;
	myYield(String a){
		name = a;
	}
	public void run() {
		System.out.println(name + "礼让前");
		Thread.yield();
		System.out.println(name + "礼让后");
	}
}
public class YieldTest {
	public static void main(String[] args) {
		new Thread( new myYield("小明") ).start();
		new Thread( new myYield("小红") ).start(); 
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_优先级_13

线程强制执行(join)

Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞 ,可以想象成插队
测试代码

package ProcessThreadTest;

public class JoinTest implements Runnable {
	public static void main(String[] args) throws InterruptedException {
		JoinTest jt = new JoinTest();
		Thread thread = new Thread(jt);
		thread.start();
		for(int i = 0;i<100;i++) {
			System.out.println("main:"+i);
			if(i==50) {
				thread.join();
			}
		}
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i<100;i++) {
			System.out.println("   join:"+i);
		}
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_System_14

线程优先级

Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度 器按照优先级决定应该调度哪个线程来执行。线程的优先级用数字表示,范围从1~10。

Thread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;

getPriority()获取优先级,setPriority(int xxx)设置优先级。

守护进程

特点

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕
  • 如,后台记录操作日志,监控内存,垃圾回收等待
    代码
package ProcessThreadTest; 
class DeamonClass implements Runnable{ 
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true)
			System.out.println("守护进程--------------");
	} 
}

class NormalClass implements Runnable{ 
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i<100;i++) {
			System.out.println("普通进程");
		}
		System.out.println("普通进程结束~~~~~");
	} 
}
public class DeamonTest {
	public static void main(String[] args) {  
		Thread thread = new Thread(new DeamonClass()); 
		thread.setDaemon(true);
		thread.start();
		new Thread(new NormalClass()).start(); 
	} 
}

运行结果:

Java 多线程GC JAVA 多线程 采集plc_java_15

线程同步

处理多线程问题时 , 多个线程访问同一个对象 , 并且某些线程还想修改这个对象 。这时候我们就需要线程同步 。线程同步其实就是一种等待机制 , 多个需要同时访问此对象的线程进入这个对象的等待池形成队列, 等待前面线程使用完毕 , 下一个线程再使用。

由于同一进程的多个线程共享同一块存储空间 , 在带来方便的同时,也带来了访问 冲突问题 , 为了保证数据在方法中被访问时的正确性 , 在访问时加入 锁机制synchronized , 当一个线程获得对象的排它锁 , 独占资源 , 其他线程必须等待 , 使用后释放锁即可 . 存在以下问题 :
一个线程持有锁会导致其他所有需要此锁的线程挂起 ;
在多线程竞争下 , 加锁 , 释放锁会导致比较多的上下文切换 和 调度延时,引起性能问题 ;
如果一个优先级高的线程等待一个优先级低的线程释放锁 会导致优先级倒置 , 引起性能问题 。

线程不安全举例

不安全买票

代码

package ProcessThreadTest;

class BuyTicket implements Runnable{
	private int ticketNums = 10;
	boolean flag = true;
	public void buy() throws InterruptedException {
		if(ticketNums<=0) {
			flag = false;
			return;
		}
		Thread.sleep(100); //模拟网络延迟
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}
	public void run() {
		while(flag) {
			try {
				buy();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
public class UnsafeBuyTIcket {
	public static void main(String[] args) {
		BuyTicket bt = new BuyTicket();
		new Thread(bt,"A").start();
		new Thread(bt,"B").start();
		new Thread(bt,"C").start();
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_System_16

不安全银行

代码

package ProcessThreadTest;

public class UnsafeBank {
	public static void main(String[] args) {
		Account account = new Account(100,"零花钱");
		Drawing A = new Drawing(account,50,"A");
		Drawing B = new Drawing(account,100,"B");
		A.start();
		B.start();
	}
}
class Account{
	int money;
	String name;
	Account(int a,String b){
		money = a; 
		name = b;
	}
}


class Drawing extends Thread{
	Account account;
	int drawingMoney;
	String name;
	public Drawing(Account account,int drawingMoney,String name) {
		this.account = account;
		this.drawingMoney = drawingMoney;
		this.name = name;
	}
	
	public void run() {
		if(account.money < drawingMoney) {
			System.out.println(Thread.currentThread().getName() + "钱不够");
			return;
		} 
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		account.money -= drawingMoney; 
		System.out.println(name + "取了:"+ drawingMoney);
		System.out.println(account.name + "余额为" + account.money);
	}
}

结果

Java 多线程GC JAVA 多线程 采集plc_System_17

不安全集合

代码

package ProcessThreadTest;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		for(int i = 0;i<10000;i++) {
			new Thread( ()->{
				list.add(Thread.currentThread().getName());
			}).start();
		}
		System.out.println(list.size());
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_优先级_18


可以看到list中的元素并不是一万个,因为有的是同时新增的,其中的某些就被覆盖掉了。

同步方法

由于我们可以通过 private 关键字来保证数据对象只能被方法访问 , 所以我们只需 要针对方法提出一套机制 , 这套机制就是 synchronized 关键字 , 它包括两种用法 : synchronized 方法 和synchronized 块。
同步方法 : public synchronized void method(int args) {}

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

同步方法,同步块(synchronized)

同步块 : synchronized (Obj ) { }

Obj 称之为同步监视器
Obj 可以是任何对象 , 但是推荐使用共享资源作为同步监视器
同步方法中无需指定同步监视器 , 因为同步方法的同步监视器就是this , 就是 这个对象本身 , 或者是 class
同步监视器的执行过程

  1. 第一个线程访问 , 锁定同步监视器 , 执行其中代码 。
  2. 第二个线程访问 , 发现同步监视器被锁定 , 无法访问。
  3. 第一个线程访问完毕 , 解锁同步监视器 。
  4. 第二个线程访问, 发现同步监视器没有锁 , 然后锁定并访问。
安全买票代码

只要把之前不安全的买票的buy方法加上synchronized修饰就可以保证同步了。

1.方法一
	public synchronized void buy() throws InterruptedException {
		if(ticketNums<=0) {
			flag = false;
			return;
		}
		Thread.sleep(100);
		System.out.println(Thread.currentThread().getName()+"拿到"+ticketNums--);
	}

Java 多线程GC JAVA 多线程 采集plc_优先级_19

安全取钱代码

synchronized 默认锁的是this方法,但这应该锁Account类而不是Drawing类。
可以把几个函数封装在synchronized之中。

public void run() {
		//account不是本对象,不能用synchronized方法。
		synchronized(account) {
			if(account.money < drawingMoney) {
				System.out.println(account.name  + " 钱不够");
				return;
			} 
			try {
				Thread.sleep(200);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			account.money -= drawingMoney; 
			System.out.println(name + "取了:"+ drawingMoney);
			System.out.println(account.name + "余额为" + account.money);
		}
		
	}

运行结果

Java 多线程GC JAVA 多线程 采集plc_优先级_20

安全list代码
package ProcessThreadTest;

import java.util.ArrayList;
import java.util.List;

public class UnsafeList {
	public static void main(String[] args) throws InterruptedException {
		List<String> list = new ArrayList<String>();
		for(int i = 0;i<10000;i++) {
			new Thread( ()->{ 
				synchronized(list) {
					list.add(Thread.currentThread().getName());
				} 
			}).start();
		}
		Thread.sleep(1000);
		System.out.println(list.size());
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_21

JUC

安全类型的集合。
代码

package ProcessThreadTest;

import java.util.concurrent.CopyOnWriteArrayList;

public class CopyOnWriteArrayListTest {
	public static void main(String[] args) throws InterruptedException {
		CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
		for(int i = 0;i<10000;i++) {
			new Thread( ()->{
				list.add(Thread.currentThread().getName());
			}).start();
		}
		Thread.sleep(1000);
		System.out.println(list.size());
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_22

死锁

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

1. 互斥条件:一个资源每次只能被一个进程使用。 
2. 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。 
3. 不剥夺条件 : 进程已获得的资源,在末使用完之前,不能强行剥夺。 
4. 循环等待条件 : 若干进程之间形成一种头尾相接的循环等待资源关系。

实验代码

package ProcessThreadTest;

public class DeadLockTest {
	public static void main(String[] args) {
		Makeup q1 = new Makeup(0,"A");
		Makeup q2 = new Makeup(1,"B");
		q1.start();
		q2.start();
	}
}
class Lipstick{
	
}
class Mirror{
	
}

class Makeup extends Thread{
	static Lipstick lipstick = new Lipstick();
	static Mirror mirror = new Mirror();
	int choice;
	String girlName;
	Makeup(int Choice,String girlName){
		this.choice = Choice;
		this.girlName = girlName;
	}
	public void run() {
		try {
			makeup();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	private void makeup() throws InterruptedException {
		if(choice == 0) {
			synchronized(lipstick) {
				System.out.println(this.girlName + "获得口红锁");
				Thread.sleep(1000);
				synchronized(mirror) {
					System.out.println(this.girlName + "获得镜子锁");
				}
			}
			
		}else {
			synchronized(mirror) {
				System.out.println(this.girlName + "获得镜子锁");
				Thread.sleep(1000);
				synchronized(lipstick) {
					System.out.println(this.girlName + "获得口红锁");
				}
			}
			
		}
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_java_23

Lock锁

Java 多线程GC JAVA 多线程 采集plc_java_24

从JDK 5.0开始,Java提供了更强大的线程同步机制–通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象 ReentrantLock类实现了Lock ,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的ReentrantL ock,可以显式加锁、释放锁。
synchronized 与 Lock 的对比

  • Lock是显式锁(手动开启和关闭锁,别忘记关闭锁)
  • synchronized是隐式锁,出了作用域自动释放
  • Lock只有代码块锁,synchronized有代码块锁和方法锁
  • 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
  • 优先使用顺序: Lock > 同步代码块(已经进入了方法体,分配了相应资源)> 同步方法(在方法体之外)

不加锁的情况

代码

package ProcessThreadTest;
class MyLock implements Runnable{
	int n = 10;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(true) {
			if(n > 0) {
				try {
					Thread.sleep(1000);
				}catch(InterruptedException e) {
					e.printStackTrace();
				}
				System.out.println(n--);
			}else {
				break;
			}
		}
	}
	
}
public class LockTest {
	public static void main(String[] args) {
		MyLock myLock = new MyLock();
		new Thread(myLock).start();
		new Thread(myLock).start();
		new Thread(myLock).start();
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_Java 多线程GC_25

加锁的代码

代码

class MyLock implements Runnable{
	int n = 10;
	private final ReentrantLock lock = new ReentrantLock(); 
	@Override
	public void run() { 
		while(true) {
			try{
				lock.lock();
				if(n > 0) {
					try {
						Thread.sleep(1000);
					}catch(InterruptedException e) {
						e.printStackTrace();
					}
					System.out.println(n--);
				}else {
					break;
				}
			}finally {
				lock.unlock();
			}
			
		}
	}
	
}

结果

Java 多线程GC JAVA 多线程 采集plc_java_26

线程协作

生产者消费者问题

假设仓库中只能存放一件产品 , 生产者将生产出来的产品放入仓库 , 消费者将仓库中产品取走消费 。
如果仓库中没有产品 , 则生产者将产品放入仓库 , 否则停止生产并等待 , 直到 仓库中的产品被消费者取走为止 。
如果仓库中放有产品 , 则消费者可以将产品取走消费 , 否则停止消费并等待 , 直到仓库中再次放入产品为止 。

这是一个线程同步问题 , 生产者和消费者共享同一个资源 , 并且生产者和消费者之间相互依赖 , 互为条件。对于生产者 , 没有生产产品之前 , 要通知消费者等待 . 而生产了产品之后 , 又 需要马上通知消费者消费。对于消费者 , 在消费之后 , 要通知生产者已经结束消费 , 需要生产新的产品以供消费. 在生产者消费者问题中 , 仅有synchronized是不够的。synchronized 可阻止并发更新同一个共享资源 , 实现了同步 u synchronized 不能用来实现不同线程之间的消息传递 (通信)。

解决线程通信的方法

Java 多线程GC JAVA 多线程 采集plc_java_27


注意 : 均是Object类的方法 , 都只能在同步方法或者同步代码块中使用,否则会抛出异常IllegalMonitorStateException

管程法

Java 多线程GC JAVA 多线程 采集plc_System_28


代码实现

package ProcessThreadTest;

import java.util.Random;

public class ProductorConsumerTest {
	public static void main(String[] args) {
		SynContainer sc = new SynContainer();
		new Productor(sc).start();
		new Consumer(sc).start();
	}
}
class toy{
	private int id;
	toy(int i){
		setId(i);
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
}
class Productor extends Thread{
	SynContainer sc = new SynContainer();
	Productor(SynContainer sc){
		this.sc = sc;
	}
	public void run() {
		Random random = new Random();
		for(int i = 1;i<=100;i++) {
			try {
				Thread.sleep(random.nextInt(80));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			this.sc.push(new toy(i));
			System.out.println("生产了第"+i+"只鸡"+",剩余"+sc.getNums());
		}
	}
}

class Consumer extends Thread{
	SynContainer sc = new SynContainer();
	Consumer(SynContainer sc){
		this.sc = sc;
	}
	public void run() {
		Random random = new Random();
		for(int i = 0;i<100;i++) {
			try {
				Thread.sleep(random.nextInt(100));
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			toy t = this.sc.pop();
			System.out.println("消费了第" +t.getId()+"只鸡"+",剩余"+sc.getNums());
		}
	}
}
class SynContainer{
	private toy[] toys = new toy[10];
	private int nums = 0; 
	public int getNums() {
		return nums;
	}
	public synchronized void push(toy t) { 
		if(nums >= 10) { 
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} 
		}
		toys[nums] = t;
		nums++; 
		this.notifyAll();
	}
	public synchronized toy pop() {
		if(nums <= 0) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		nums--;
		toy t = toys[nums];
		this.notifyAll();
		return t;
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_java_29

信号灯法

代码测试

package ProcessThreadTest;

public class ProductorConsumerTest1 {
	public static void main(String[] args) {
		TV tv = new TV();
		new Watcher(tv).start();
		new Player(tv).start();
	}
}
class TV{
	String program;
	boolean flag = true;//true表示开始未表演状态 
	public synchronized void play(String program) {
		if(!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		System.out.println("演员表演了:"+program);
		this.notifyAll(); //通知观众来看
		this.program = program;
		this.flag = !this.flag;
	}
	
	public synchronized void watch() {
		if(flag) {
			try {
				this.wait();
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("观看了"+program);
		this.notifyAll();
		this.flag = !this.flag;
	}
}
class Player extends Thread{
	TV tv;
	public Player(TV tv) {
		this.tv = tv;
	}
	public void run() {
		for(int i = 0;i<10;i++) { 
			this.tv.play("节目"+i); 
		}
	}
}
class Watcher extends Thread{
	TV tv;
	public Watcher(TV tv) {
		this.tv = tv;
	}
	public void run() {
		for(int i = 0;i<10;i++) { 
			this.tv.watch(); 
		}
	}
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_java_30

线程池

特点

背景:经常创建和销毁、使用量特别大的资源,比如并发情况下的线程,对性能影响很大。 思路:提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。 可以避免频繁创建销毁、实现重复利用。
好处: 提高响应速度;降低资源消耗;便于线程管理。

corePoolSize:核心池的大小 
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止

JDK 5.0起提供了线程池相关API:ExecutorServiceExecutors

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor 
void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable <T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行 Callable
void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池

测试代码

package ProcessThreadTest;

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

public class ThreadPoolTest {
	public static void main(String[] args) {
		ExecutorService service = Executors.newFixedThreadPool(10);
		
		service.execute(new MyThread00());
		service.execute(new MyThread00());
		service.execute(new MyThread00());
		service.execute(new MyThread00());
		service.execute(new MyThread00());
		service.execute(new MyThread00());
		
		service.shutdown();
	}
}

class MyThread00 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i = 0;i<1;i++) {
			System.out.println(Thread.currentThread().getName()+i);
		}
	}
	
}

运行结果

Java 多线程GC JAVA 多线程 采集plc_优先级_31

学习链接

https://kuangstudy.com/course