您的位置:首页 > 编程语言 > Java开发

java 多线程

2015-12-11 14:20 435 查看
要想了解多线程,必须先了解线程,要想了解线程,必须先了解进程,因为线程是依赖于进程而存在的。

什么是进程?

  1、进程就是正在运行的程序

  2、进程是系统进行资源分配和调用的独立单位,每一个进程都有自己独立的内存空间和系统资源

多进程的意义:

  可以在一段时间内执行多个程序,提高cpu的使用率。例如可以一边玩游戏,一边听音乐。

  注意:多个任务不是同时执行的,因为cpu在在一个时间点上只能做一件事,只不过是cpu在做着程序间的高效切换,让我们感觉像是同时执行的。

什么是线程?

  在一个进程内可以执行多个任务,而每一个任务可以看成是一个线程

  线程:线程是程序的执行单元或叫执行路径。是程序使用cpu的基本单位

  单线程:程序只有一条执行路径

  多线程:程序有多条执行路径

多线程的意义:

  多线程的存在不是提高程序的执行速度,而是提高了程序的使用率。

  程序的执行其实都是在抢cpu的资源,cpu的执行权。

  如果某个程序执行路径比较多,就有更高的几率抢到cpu资源。

  我们不敢保证哪个线程在哪个时刻抢到,所有线程的执行具有随机性。

并行和并发:

  并行是逻辑上同时发生,指在一个时间段内同时运行多个程序

  并发是物理上同时发生,指在一个时间点上同时运行多个程序

java程序的运行原理:

  由java命令启动jvm,jvm启动,就相当于启动了一个进程,接着由该进程创建一个主线程去调用main方法

  jvm虚拟机的启动时多线程的,原因是垃圾回收线程也要启动,否则会造成内存溢出。加上主线程,所有至少有两个线程启动。

java为我们提供了Tread类来实现多线程:

Thread类

常用方法:

String getName() :获取线程名

void setName():设置线程名

void setPriority(10); //设置线程优先级,取值为范围是 0-10,默认是5

static Thread currentTread() :返回当前正在执行的线程对象

static void sleep(long millis):休眠,单位为毫秒

final void join():等待该线程终止,在线程启动后调用,在此之后的线程等此线程结束后才执行

final void setDaemon(boolean on) :将该线程标记为守护线程或用户线程。当正在运行的线程都是守护线程时,Java 虚拟机退出。该方法必须在启动线程前调用。

static final void stop():停止线程,已过时,但是还可以使用

void interrupt() :中断线程,把线程的状态终止,并抛出一个InterruptedException异常

创建方式一:

继承Thread类,步骤如下:

1、创建子类继承Tread类

2、重写run方法

3、创建子类实例对象

4、子类对象调用start()方法启动线程

注意:run方法和start方法的区别?

  run() 仅仅是封装了线程执行的代码,和普通的方法一样

  start() 首先是启动了线程,然后由jvm去调用该线程的run方法

创建代码如下:

//创建Mythread类继承Thread
public class Mythread extends Thread{

//重写run方法
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println(getName()+"--"+i);
}
}
}


public static void main(String[] args) {

//创建子类对象
Mythread mythread=new Mythread();
//开启线程
mythread.start();
}


创建方式二:

实现Runnable接口,步骤如下:

1、自定义类实现Runnable接口

2、子类重写run方法

3、创建子类对象

4、调用Thread构造方法,将子类对象当作参数传入

和方式一比较,方式二的好处:

a、可以避免由于java单继承性带来的局限性 b、适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码有效分类,体现了较好的面向对象设计思想。

创建代码如下:

//创建 RunThread 类实现Runnable接口
public class RunThread implements Runnable {

//重写run方法
@Override
public void run() {

}
}


public static void main(String[] args) {

//创建runThread接口实现类对象
RunThread runThread=new RunThread();

//调用Thread构造方法,传入runThread对象作为构造参数,创建线程
Thread thread=new Thread(runThread);
thread.start();
}


多线程安全问题产生的原因及解决办法

1、是否是由多线程

2、是否有共享数据

3、是否有多条语句操作共享数据

分析以上3点,如何3个条件都满足,就会产生安全问题。

解决办法:synchronized

为解决多线程安全问题,java提供了同步机制,将需要同步的代码写在同步代码块中

用法如下所示:

//同步能解决线程安全问题的根本原因就在于监视对象obj,该对象就如同一把锁,多个线程必须是同一把锁。此时的锁是任意对象
private void sysFunc1(){
Object obj=new Object();
synchronized (obj){
//需要同步的代码
}
}

//如果synchronized写在实例方法上,此时的锁是this
private synchronized void sysFunc2(){
//需要同步的代码
}

//如果synchronized写在静态方法上,此时锁是类的字节码文件对象。XXX.class
private static synchronized void sysFunc3(){
//需要同步的代码
}


同步的好处:可以解决线程安全问题

同步的弊端:当线程较多,每个线程都会去判断同步锁对象,这是很耗费资源的,无形中会降低程序的运行效率

线程间的通信:

生产者与消费者问题:

public static void main(String[] args) {

Student s=new Student();
Thread set=new Thread(new SetThread(s));
Thread get=new Thread(new GetThread(s));

set.start();
get.start();
}


生产者线程类代码:

/**
* 生产者
*/
public class SetThread implements Runnable {

private Student s;
private int x;
public SetThread(Student s){
this.s=s;
}
@Override
public void run() {

while (true){
//SetThread是生产者,生产资源的代码如果不加锁,会出现名字和年龄对不上的情况
synchronized (s){
if(x%2==0){
s.setName("zhangsan");
s.setAge(30);
}else {
s.setName("lisi");
s.setAge(40);
}
x++;
}

}
}
}


消费者线程类代码:

/**
* 消费者
*/
public class GetThread implements Runnable {
Student s;
public GetThread(Student s){
this.s=s;
}
@Override
public void run() {
while (true){
//GetThread 为消费者,消费SetThread类生产的资源,必须加上锁否则会出现名字和年龄不一致。
synchronized (s){
System.out.println(s.getName()+"--"+s.getAge());
}

}
}
}


生产资源Student类:

public class Student {
private String name;
private int age;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: