谈谈java中的并发(一)

一.并发的定义

并发:对于这个概念一直就是没怎么搞懂,就是感觉特别的生疏,(自己从从字面上理解就是多个东西,一起出发),所以就上网上查了一些资料:

同时拥有两个或多个线程,如果程序在单核处理器上运行,多个线程将交替地换入或者换出内存,这些线程是同时“存在”的,每个线程都处于执行过程中的某个状态,如果运行在多核处理器上,此时,程序中的每个线程都将分配到一个处理器核上,因此可以同时运行。

高并发(High Concurrency):
是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。

两者区别:

并发:多个线程操作相同的资源,保证线程安全,合理使用资源。

高并发(High Concurrency):服务能同时处理很多请求,提高程序性能

二.线程的定义

1.线程(英语:thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。

在网上查资料线程是这么定义的,其实说简单点,线程其实就是可以多个同时进行,也称之为多线程。下面是个生活中的例子

比如一天突然来一个电话,张三跟我说:“他的朋友李四的卡里面没有钱了,想让我借给他的朋友点

,我之后便去银行,到了银行,我先把钱转给张三,之后张三再把钱转到他的朋友李四的卡里。

其实,这个例子就是多线程的同时操作,第一个线程是  我把钱转到张三的卡里面  ;第二个线程是  张三在把钱转到李四的卡里。
在有一个例子:都听过龟兔赛跑的事情吧!这其实也是多线程的问题

兔子和乌龟同时从原点出发,比谁先到终点,这其实就是两个多线程共同进行,且互不影响。
在譬如说:咱们运行一个程序,程序没开始运行的时候是静态的,经过进程,再到线程,

它真正执行的也就是线程,其中main()称之为主线程。
在实际生活中多线程也是非常有用的,

比如,我们在浏览器上可以同时下载几张图片。

上面所说的都是线程的例子,它还是与我们生活密切相关的,用处也是非常大的。

线程就是独立的执行路径

在程序运行时,即使没有自己创建线程,后台也会有多少少线程,如主线程,gc线程

main()称之为主线程,为系统的入口,用于执行整个程序

在一个进程中,如果开辟了多少线程,线程的运行由调度器安排调度,调度器是与操作系统紧密相关的,先后顺序是不能认为的干预的。

对同一份资源操作时,会存在资源抢夺的问题,需要加入并发控制

每个线程在自己的工作内存交互,内存控制不会造成吧数据不一致。

三.怎样实现多线程?

1.继承Thread类实现多线程的能力

package com.kuang.demo1;

//创建Thread类  重写run()方法  调用start()方法
//线程开始不一定立即执行,由CPU执行
public class TestThread1 extends Thread{
	@Override
	public void run() {
		//run()方法线程
		for(int i=0;i<20;i++) {
			System.out.println("我爱写代码--" +i);
		}
	}
	
	public static void main(String[] args) {
		//main线程  主线程
		
		//创建一个线程
		TestThread1 testThread1 = new TestThread1();
		
		//调用start()方法开启线程
		testThread1.start();
		
		for(int i=0;i<20;i++) {
			System.out.println("我爱学习多线程" + i);
		}
	}
}

2.实现Runnable接口建立多线程

package com.kuang.demo1;

//创建线程的第二种 首先实现runnable接口,接着重写run()方法,执行线程时需要丢入runnable接口实现类,调用start方法
public class TestThread2 implements Runnable{
	@Override
	public void run() {
		//run方法线程体
		for(int i=0;i<20;i++) {
			System.out.println("我在看代码--" + i);
		}
	}
	
	public static void main(String [] args) {
		//创建runnable实现接口类的线程
		TestThread2 testThread2 = new  TestThread2();
		new Thread(testThread2).start();
		
		for(int i=0;i<20;i++) {
			System.out.println("我在学习多线程--" + i);
		}
	}
}

3.实现Callable接口(这个我就不举代码例子了,了解即可)

1.实现Callable接口,需要返回值类型

2.重写call方法,需要抛出异常

3.创建目标对象

4.创建执行服务:ExecutorService ser = Excutors.newFixedThreadPool(1);

5.提交执行:Future<Boolean> result1 = ser.submit(t1);

6.获取结果:boolean r1 = result.get();

7.关闭服务:ser.shutdownNow();

项目实例——龟兔赛跑

package com.kuang.demo1;

//模拟龟兔赛跑
public class Race implements Runnable {
	
	//胜利者
	private static String winner;
	
	@Override
	public void run() {
		for(int i=0;i<=100;i++) {
			
			//模拟兔子休息,设置让他十步卡以下
			if(Thread.currentThread().getName().equals("兔子")&&( i+1)%10==0) {
				try {
					Thread.sleep(1);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			//判断比赛是否结束
			boolean flag = gameOver(i);
			//如果比赛结束,就停止程序
			if(flag) {
				break;
			}
			System.out.println(Thread.currentThread().getName() +"-->跑了" + i + "步");
		}
	}
	
	//判断是否完成比赛
	private boolean gameOver(int steps) {
		//判断是否有胜利者
		if(winner!=null) {
			return true;
		}{
		if(steps>=100) {
			winner = Thread.currentThread().getName();
			System.out.println("winner is " + winner);
			return true;
		} 
	}
		return false;
	}
	
	public static void main(String[] args) {
		Race race = new Race();
		
		new Thread(race,"兔子").start();
		new Thread(race,"乌龟").start();
	}
}

运行结果:

兔子-->跑了0步
兔子-->跑了1步
乌龟-->跑了0步
乌龟-->跑了1步
兔子-->跑了2步
乌龟-->跑了2步
乌龟-->跑了3步
乌龟-->跑了4步
兔子-->跑了3步
乌龟-->跑了5步
乌龟-->跑了6步
兔子-->跑了4步
兔子-->跑了5步
乌龟-->跑了7步
乌龟-->跑了8步
乌龟-->跑了9步
兔子-->跑了6步
兔子-->跑了7步
兔子-->跑了8步
乌龟-->跑了10步
乌龟-->跑了11步
乌龟-->跑了12步
乌龟-->跑了13步
乌龟-->跑了14步
乌龟-->跑了15步
乌龟-->跑了16步
乌龟-->跑了17步
乌龟-->跑了18步
乌龟-->跑了19步
乌龟-->跑了20步
乌龟-->跑了21步
乌龟-->跑了22步
乌龟-->跑了23步
乌龟-->跑了24步
乌龟-->跑了25步
乌龟-->跑了26步
乌龟-->跑了27步
乌龟-->跑了28步
乌龟-->跑了29步
乌龟-->跑了30步
乌龟-->跑了31步
乌龟-->跑了32步
乌龟-->跑了33步
乌龟-->跑了34步
乌龟-->跑了35步
乌龟-->跑了36步
乌龟-->跑了37步
乌龟-->跑了38步
乌龟-->跑了39步
乌龟-->跑了40步
乌龟-->跑了41步
乌龟-->跑了42步
乌龟-->跑了43步
乌龟-->跑了44步
乌龟-->跑了45步
乌龟-->跑了46步
乌龟-->跑了47步
乌龟-->跑了48步
乌龟-->跑了49步
乌龟-->跑了50步
乌龟-->跑了51步
乌龟-->跑了52步
乌龟-->跑了53步
乌龟-->跑了54步
乌龟-->跑了55步
乌龟-->跑了56步
乌龟-->跑了57步
乌龟-->跑了58步
乌龟-->跑了59步
乌龟-->跑了60步
乌龟-->跑了61步
乌龟-->跑了62步
乌龟-->跑了63步
乌龟-->跑了64步
乌龟-->跑了65步
乌龟-->跑了66步
乌龟-->跑了67步
乌龟-->跑了68步
乌龟-->跑了69步
乌龟-->跑了70步
乌龟-->跑了71步
乌龟-->跑了72步
乌龟-->跑了73步
乌龟-->跑了74步
乌龟-->跑了75步
乌龟-->跑了76步
乌龟-->跑了77步
乌龟-->跑了78步
乌龟-->跑了79步
乌龟-->跑了80步
乌龟-->跑了81步
乌龟-->跑了82步
乌龟-->跑了83步
乌龟-->跑了84步
乌龟-->跑了85步
乌龟-->跑了86步
乌龟-->跑了87步
乌龟-->跑了88步
乌龟-->跑了89步
乌龟-->跑了90步
乌龟-->跑了91步
乌龟-->跑了92步
乌龟-->跑了93步
乌龟-->跑了94步
乌龟-->跑了95步
乌龟-->跑了96步
乌龟-->跑了97步
乌龟-->跑了98步
乌龟-->跑了99步
winner is 乌龟

四.lambda表达式

Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。

Java 8的一个大亮点是引入Lambda表达式,使用它设计的代码会更加简洁。当开发者在编写Lambda表达式时,也会随之被编译成一个函数式接口。

函数式接口:只包含一个抽象方法

package com.kuang.lambda;

/*
*推到lambda表达式
*函数式接口:只包含一个抽象方法
*/

public class TestLambda1{
	
	//2.静态内部类
	static class Like2 implements ILike{
		@Override
		public void Lambda() {
			System.out.println("I like Lambda2");
		}
	}
	public static void main(String[] args) {
		//用接口创建对象
		ILike like = new Like();
		like.Lambda();
		
	    like = new Like2();
		like.Lambda();
		
		//3.局部内部类
		class Like3 implements ILike{
			@Override
			public void Lambda() {
				System.out.println("I like Lambda3");
			}
		}
		
		like = new Like3();
		like.Lambda();
		
		//4.匿名内部类,需要new一个接口,没有类的名称,必须借助接口
		like = new ILike() {
			@Override
			public void Lambda() {
				System.out.println("I like Lambda4");
			}
		};
		
		like.Lambda();
		
		//5.用Lambda简化
		like = ()->{
			System.out.println("I like Lambda5");
		};
		like.Lambda();
		
	}
}

//1.定义一个接口
interface ILike{
	void Lambda();
}

//实现接口
class Like implements ILike{
	@Override
	public void Lambda() {
		System.out.println("I like Lambda1");
	}
}

运行结果:
I like Lambda1
I like Lambda2
I like Lambda3
I like Lambda4
I like Lambda5

在举一个就光是lambda用法的例子,lambda用法的核心语句就是

对象=(所要强调的)->{

};

package com.kuang.lambda;


 
public class TestLambda2{
	public static void main(String[] args) {
		ILove  love =(int a )-> {
			System.out.println("I Love You -->" + a);	
		};
			love=(a)->{
				System.out.println("I Love You -->" + a);	
			};
			love.love(2);
}
}

interface ILove{
	void love(int a);
}

运行结果:
I Love You -->2

五.线程的5种状态

New(新建),Runnable(可运行),(堵塞和等待). 运行,Terminated(终止)

1.线程停止

不推荐使用JDK提供的stop(),destory()方法

推荐线程自己停止下来

建议使用一个标志位进行终止变量 当flag=false,则终止线程运行。

例如下列实例:

整体思路:首先设立一个标志flag为真,之后进行重写,当flag真的时候输出线程。

其次设立一个公开的方法停止线程,在这里我们不要其它的方法,用stop()就可以,令this.flag=false 主函数创建一个对象,

之后主函数new Thread().start();的形式启动它 进行设置循环多少次,输出一个main线程,令i等于几的时候线程停止 对象.stop();

下面那个例子我设定的是一共进行20次循环 main()线程第五次的时候,线程终止了。

这是代码的核心要点

public class TestStop implements Runnable{
    //线程种定义线程体使用的标记
    private boolean flag=true;
    
    @Override
    public void run(){
        //线程体使用该标记
        while(flag){
            System.out.println("run......Thread");
        }
    }
    
    //对外提供方法改变标识
    public void stop(){
        this.flag=false;
    }
}

完整代码如下图所示:

package com.kuang.state;

import java.util.Iterator;

//测试stop
//j建议线程正常停止-->,利用次数,不建议死循环
//建议使用标志位-->设置一个标志为
//
public class TestStop implements Runnable {
	
	//1.设置一个标志为
	private boolean flag = true;
	@Override
	public void run() {
		int i=0;
		while(flag) {
			System.out.println("run......Thread" + i ++);
		}
	}

	//设置一个公开的方法停止线程,转换标志位
	public void stop() {
		this.flag=false;
	}
public static void main(String[] args) {
	TestStop teststop= new TestStop();
	new Thread(teststop).start();		//启动
	
	for(int i=0;i<20;i++) {
		System.out.println("main线程" + i);
		if(i==5) {
			//调用stop方法切换标志位,让该线程停止
			teststop.stop();
			System.out.println("线程停止了");
		}
	}
}
}

运行结果:

main线程0
run......Thread0
main线程1
run......Thread1
run......Thread2
main线程2
run......Thread3
main线程3
main线程4
run......Thread4
main线程5
线程停止了
run......Thread5
main线程6
main线程7
main线程8
main线程9
main线程10
main线程11
main线程12
main线程13
main线程14
main线程15
main线程16
main线程17
main线程18
main线程19

2.线程礼让Yield

在API中有一个这样的方法 static void yield()
指的是:当前正在执行的线程向另一个线程交出运行权,注意,这是一个静态方法。

要点:

1.礼让(yield)线程,让当前正在执行的线程暂停,但不堵塞。

2.将线程从运行状态转为就绪状态

3.让CPU重新调度,礼让不一定成功!看CPU的心情。

代码实例:

package com.kuang.demo1;

//测试礼让线程
//礼让不一定成功,看cpu的心情
public class TestYiled {
	
	public static void main(String[] args) {
		MyYield myYield = new MyYield();
		new Thread(myYield ,"a").start();
		new Thread(myYield ,"b").start();
		
	}
}

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

运行结果:

第一次:
b线程开始了
a线程开始了
b线程结束了
a线程结束了

第二次:
a线程开始了
a线程结束了
b线程开始了
b线程结束了

看!它的运行结果是变化的,因为“礼让”有时候不一定会成功,他需要看CPU的心情。

3.线程休眠sleep

线程休眠总结:

1.sleep(时间)指定当前堵塞的毫秒数

2.sleep存在异常interruptedException

3.sleep时间达到后线程进入就绪状态

4.*每个对象都有一个一个锁,sleep不会释放锁

下面例子是Thread.sleep()的用法

package com.kuang.demo1;

//模拟网络延迟的作用:放大问题的发生性
public class TestSleep implements Runnable {
	
	//票数
	private int ticketNums=10;
	
	@Override
	public void run() {
		while(true) {
			if(ticketNums<=0) {
				break;
			}
			
			//模拟延时
			try {
				Thread.sleep(100);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
			
			System.out.println(Thread.currentThread().getName() + "-->拿到了第" + ticketNums-- + "票");
		}
	}

public static void main(String[] args) {
	TestSleep ticket = new TestSleep();
	
	new Thread(ticket ,"小明").start();
	new Thread(ticket ,"老师").start();
	new Thread(ticket ,"黄牛党").start();
    }
}

看下面的运行结果,结果显示黄牛党拿到了-1张票,通过模拟线程,好处是把问题给呈现出来了

小明-->拿到了第9票
黄牛党-->拿到了第9票
老师-->拿到了第10票
老师-->拿到了第8票
小明-->拿到了第8票
黄牛党-->拿到了第8票
黄牛党-->拿到了第7票
小明-->拿到了第6票
老师-->拿到了第6票
老师-->拿到了第5票
小明-->拿到了第4票
黄牛党-->拿到了第3票
老师-->拿到了第2票
小明-->拿到了第2票
黄牛党-->拿到了第2票
老师-->拿到了第0票
小明-->拿到了第1票
黄牛党-->拿到了第-1票

模拟倒计时 主要练习tenDown()的用法

package com.kuang.demo1;

public class TestSleep1 {
	
	public static void main(String[] args) {
		try {
			tenDown();
		}catch(InterruptedException e) {
			e.printStackTrace();
		}
	}

	//模拟倒计时
public static void tenDown() throws InterruptedException{
	int num=10;
	while(true) {
		Thread.sleep(1000);
		System.out.println(num--);
		if(num<=0) {
			break;
		}
	}
 }
}

运行结果:

10
9
8
7
6
5
4
3
2
1

模拟日常时间倒计时

在上一个的基础上添加上Data用法,和时间格式符

package com.kuang.demo1;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TestSleep1 {
	
	public static void main(String[] args) {
		//打印当前系统时间
		Date startTime = new  Date(System.currentTimeMillis());		//获得系统当前时间
		while(true) {
			try {
				Thread.sleep(1000);
				System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));	//输出时间格式化工厂
				startTime = new Date(System.currentTimeMillis());		//更新当前时间
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

	//模拟时间倒计时
public static void tenDown() throws InterruptedException{
	int num=10;
	while(true) {
		Thread.sleep(1000);
		System.out.println(num--);
		if(num<=0) {
			break;
		}
	}
  }
}

代码结果

16:36:18
16:36:19
16:36:20
16:36:21
16:36:22
16:36:23
16:36:24
16:36:25
16:36:26
16:36:27
16:36:28
16:36:29
16:36:30
16:36:31
16:36:32
16:36:33
16:36:34
16:36:35
16:36:36
.......
.......
.......

它会一直这样输出.

4.新建线程

是通过new操作符创建一个新线程时,如new Thread(r),这个线程还没有运行,这就意味着它的状态是新建(new).

5.线程强制执行join

当线程等待另一个线程通过调度器出现一个条件,这个线程会进入等待状态.通过Object.wait(),Thread.join()方法,或者是等待java.util.concurrent库中的Lock或Condition时会出现这种情况。

总结:

1.Join合并线程,待此线程执行完成之后,在执行其他线程,其他线程堵塞

2.可以想象成插队

代码:

package com.kuang.demo1;


//测试join方法
//想象成排队
public class TestJoin implements Runnable {
	@Override
	public void run() {
		try {
			Thread.sleep(100);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		for(int i=0;i<10;i++) {
			System.out.println("线程VIP来了" + i);
		}
	}
	
	
	public static void main(String[] args) throws InterruptedException {
		//启动我们的线程
		TestJoin testjoin = new TestJoin();
		Thread thread = new Thread(testjoin);
		thread.start();
		
		
		//主线程
		for(int i=0;i<50;i++){
			if(i==25) {
				thread.join();//插队
			}
			System.out.println("main" + i);
		}
	}
}

运行结果:

main0
main1
main2
main3
main4
main5
main6
main7
main8
main9
main10
main11
main12
main13
main14
main15
main16
main17
main18
main19
main20
main21
main22
main23
main24
线程VIP来了0
线程VIP来了1
线程VIP来了2
线程VIP来了3
线程VIP来了4
线程VIP来了5
线程VIP来了6
线程VIP来了7
线程VIP来了8
线程VIP来了9
main25
main26
main27
main28
main29
main30
main31
main32
main33
main34
main35
main36
main37
main38
main39
main40
main41
main42
main43
main44
main45
main46
main47
main48
main49

在i=25之前是CPU调度的,随机,当i>25之后就开始先执行VIP了。

六.判断线程所处的状态

package com.kuang.demo1;
//观察测试线程的状态
public class TestState {

	public static void main(String[] args) throws InterruptedException {
		Thread thread = new Thread(()->{
		for(int i=0;i<5;i++) {
			try {
				Thread.sleep(1000);
			}catch(InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("我爱写代码");
 });
		
		//观察状态
		Thread.State  state = thread.getState();
		System.out.println(state);	//NEW
		
		thread.start();		//启动线程
		state = thread.getState();
		System.out.println(state);	
		
		while(state!=Thread.State.TERMINATED){
			Thread.sleep(100);
			state = thread.getState();	//更新线程状态
			System.out.println(state);	//输出状态
		}
		
	}
}

运行结果:从中可以看出一个线程所处的状态一次经过new Runnable 堵塞 运行 终止 这五个状态

NEW
RUNNABLE
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
TIMED_WAITING
我爱写代码
TERMINATED

猜你喜欢

转载自www.cnblogs.com/xiao666/p/13178826.html