Java并发多线程,线程安全,基础多线程

我想复习一下多线程就来博客整理发出来大家一起努力了

第一什么是多线程,如何去完成一个多线程

我们在之前完成的代码,是从main函数以后开始的,通过层层的调用,他只有一个线程,也不会出现一些稀奇古怪的问题,但是如果你使用了多线程,就会出现一些有意思的代码。
首选我介绍一些如何创建线程,一共能够有两种方式。

第一种继承Thread

下面是一个小demo

public class MM extends Thread {
	static int count=10;
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println(count++);
			System.out.println(this.getName());
		}
	}
	public static void main(String[] args) {
		
		MM mm=new MM();
		MM mn=new MM();
		mm.start();
		mn.start();
	}
}

上面的打印的结果是
我们我们能够知道的是我们使用了两个线程,将count值加到了69,这就是多线程
第二种是实现Runnable接口,

public class NN implements Runnable{
	static int count=10;
	@Override
	public void run() {
		// TODO Auto-generated method stub
		for (int i = 0; i < 30; i++) {
			System.out.println(count++);
			System.out.println(Thread.currentThread().getName());
		}
	}
public static void main(String[] args) {
	NN nn=new NN();
	Thread t1=new Thread(nn);
	t1.start();
	NN nn1=new NN();
	Thread t11=new Thread(nn1);
	t11.start();
}
}

在这里插入图片描述一样的效果但是有细微的区别,但是一般都在使用接口,因为继承只能继承一个,但是可以实现多个接口,

还有一种,我用的会更多,是内部类的方式
public class Oo {
	static int count=0;
public static void main(String[] args) {
	for (int i = 0; i < 300; i++) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				for (int i = 0; i < 1000; i++) {
					System.out.println(count++);
				}
				
			}
		}).start();
	}

}
}

你们可以猜一下答案,我用了300个线程每一个线程加1000,一共是300000,但是结果并不是
在这里插入图片描述
在这里插入图片描述
每一次的结果理论上来说都不一样,但是应该不会等于300000,就是因为他会出现多线程引发的隐患,具体是因为什么原因呢?
_temp = i;
i = i + 1;
i = _temp;
这就是i++的底层代码,先将值取出,做运算,然后在将计算好的值赋值回去,因为是有多线程,每一个线程是争抢cpu,每个时间片只允许一个线程运行,这样的话就存在一种可能性,一个线程刚刚的加值取出,另一个线程就把值传回去了,没有计算,所以会出现值小于原计划值得存在.

下面我简单介绍一下线程的生命周期

线程会有新建、就绪、运行、阻塞和死亡状态。
1新建状态,创建一个进程具体的可以参考上面我写的
2就绪状态,新建状态完成,线程被创建以后通过Thread类里面的start()调动,
3运行状态,当就绪获得时间片以后,从就绪进入运行
4阻塞状态,当运行状态遇到一些问题的时候调用sleep()方法让线程睡眠,调用wait()方法让线程等待,调用join()方法,或者是用户的一些操作或者是IO流的操作.从阻塞的状态到运行状态也是一样的,等待sleep方法完成,或者是wait方法被notify(),或者是用户输入完成.
5,死亡状态,run方法运行完毕或者是出现异常

下面就是一些多线程中的一些常用的方法
join方法

当一个线程需要等待另一个线程完毕再执行的话,可以使用Thread的join方法。
假设线程A和线程B,在A执行时调用了B的join方法,A将被阻塞,一直等到B线程执行完毕后,A线程继续执行,就好像排队加塞。


public class Join {
public static void main(String[] args) {
	Maiin mm=new Maiin();
	mm.start();
}
}
class Maiin extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			if(i==30) {
				Maiin1 mm=new Maiin1();
				mm.setName("我是多线程");
				mm.start();
				try {
					mm.join();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println(i+"s"+this.getName());
		}
	}
}
class Maiin1 extends Thread{
	@Override
	public void run() {
		for (int i = 0; i < 50; i++) {
			this.setName("我是多线程*2");
			System.out.println(i+"s"+this.getName());
		}
	}
}
Thread.sleep();

这个方法就是在指定的毫秒数内让指定的线程暂时休眠,这个是受到系统的一些控制,具体我没有研究的很深,这个线程是静态的方法,就是直接使用Thread.sleep(long millis),里面存放的是毫秒数.
最关键的是他在sleep的时候不会放弃CPU的执行权力,也就是说他在休眠时没有线程能够使用,这个也不难我就不做过多的叙述了

消费者生产者问题

这个是一个很经典的多线程的问题,我来用代码来简述一下
第一,问题简述,生产者负责生产产品,消费者负责消费产品,产品是放在一个容器里面,容器有容量,容量有限,去用代码去实现这个过程.
第二,我们可以先完成产品的实现,完成一个Java bean

public class Product {
	private int id;
	private String name;
	@Override
	public String toString() {
		return "Product [id=" + id + ", name=" + name + "]";
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Product(int id, String name) {
		super();
		this.id = id;
		this.name = name;
	}
	public Product() {
		// TODO Auto-generated constructor stub
	}
	
}

这是一个产品的Java bean 我们要制造和消费的就是这个东西
第三,我们要开始完成容器,我这里使用的是LinkedList,单独完成一个类里面完成存取的功能,为了方便执行我在里面添加一个方法去打印容器里面的产品的情况

import java.util.LinkedList;

public class Contain {
 	public static LinkedList<Product> ll=new LinkedList<Product>();
	public static  int containnum=10;//这个东西就是相当于容器的最大的容量
	
	/**
	 * 这个类添加了synchronized,添加悲观锁,实现了生产者的功能,将产品存到指定的容器中,
	 * @param p
	 */
	public synchronized void put(Product p) {
		while(ll.size()>10) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				}
		
		ll.add(p);
		notifyAll();
	}
	
	/**
	 * 这个类实现了产品的拿出
	 * @return
	 */
	public synchronized Product take() {
		
		while(ll.size()==0) {
			try {
				wait();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		Product removeLast = ll.removeLast();
		notifyAll();
		return removeLast;
	}
	
	/**
	 * 这个功能实现了打印产品中的数量
	 */
	public void check() {
		System.out.println(ll.size());
	}
}

第四我们去实现一个类,去完成一下我们的方法,


public class FF {
	public  static Contain cc=new Contain(); 
	public static int j=1;
public static void main(String[] args) {
	/**
	 * 去实现拿的方法,达到消费者的角色
	 */
	for (int i = 0; i < 3; i++) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true) {
					try {
						cc.take();
						Thread.sleep(500);//为了降低打印的速度
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}).start();
	}
	/**
	 * 这个实现生产者
	 */
	for (int i = 0; i < 2; i++) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while(true) {
					try {
						Product pp=new Product(j++, "pro"+j++);
						cc.put(pp);
						System.out.println("pput");
						Thread.sleep(300);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			}
		}).start();
	}
	/**
	 * 一直去实现这个打印的方法,一直去实现他的方法
	 */
	while(true) {
		cc.check();	
	}
	
}
}

我们可以通过改变sleep里面的时间来控制生产者生产的速度和消费者消费的速度

wait notify

当线程A运行过程中遇到不满足的条件需要等待,等待另外的线程B去更改系统状态。当B更改系统状态后,唤醒等待线程A。等待线程A查看是否满足条件,如果满足则继续执行,不满足则继续等待。
也就是两者遇到wait需要去等待,等待一个notify去唤醒,这里面有一些的东西,当一个线程被wait的时候,然后,可以用notify或者notifyAll(),来唤醒,但是不一样,如果是notifyAll是唤醒所有的wait,但是notify是随机唤醒一个,由具体是哪一个虚拟机判定的.具体实现是通过设置一个变量来实现的.

public class Testwait {
	public static void main(String[] args) {
		 LoopTest lt = new LoopTest();
	new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true)
					lt.f1();
			}
		}, "线程A").start();
	new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				while (true)
					lt.f2();
			}
		}, "线程B").start();
	}
}
class LoopTest {
	boolean flag = true;
	public synchronized void f1() {
		while (flag) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} 
			System.out.println(Thread.currentThread().getName());
			flag = true;
			notify();
	}

public synchronized void f2() {
		while (!flag) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		} 
			System.out.println(Thread.currentThread().getName());
			flag = false;
			notify();
	}
}

具体的原理就是,设置一个boolean值,分别完成两个方法设置上synchronized,是每一个线程独享这个方法,然后判定Boolean的值变量,然后去输出线程的名字,然后就去唤醒另外的一个线程去继续运行,这样就能使用wait和notify,

Future设计模式

体现出一种设计模式,Future模式是就想你要去蛋糕店去定制一个,然后你给出了订单,他们完成需要时间牡,这段时间需要去等待,但是前台将订单交给后台以后,就可以去面对更多的顾客,去处理他们的问题,但是制作蛋糕的问题就交给别的人,自己实现了,但是完成的度不好,跟大家交流一下,

线程安全的定义

线程安全的概念不容易定义,在《Java 并发编程实践》中,作者做出了如此定义:多个线程访问一个类对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码不必作其他的协调,这个类的行为仍然是正确的,那么这个类是线程安全的。

先写到这里吧,我后期在完善

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又是重名了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值