我想复习一下多线程就来博客整理发出来大家一起努力了
第一什么是多线程,如何去完成一个多线程
我们在之前完成的代码,是从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 并发编程实践》中,作者做出了如此定义:多个线程访问一个类对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,并且不需要额外的同步及在调用方法代码不必作其他的协调,这个类的行为仍然是正确的,那么这个类是线程安全的。
先写到这里吧,我后期在完善