`
IT阿狸
  • 浏览: 65568 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

Java的多线程

 
阅读更多

一、进程和线程

程序:程序是一段静态的代码,它是应用程序执行的蓝本。

进程:进程是指一种正在运行的程序,有自己的地址空间。

进程的特点:

1.动态性

2.并发性

3.独立性

 

线程的概念:

    进程内部的一个执行单元,它是程序中一个单一的顺序控制流程。如果在一个进程中同时运行了多个线程,用来完成不同的工作,则称之为多线程

线程的定义:

1.进程是系统资源分配的单位,可包括多个线程

2.线程是独立调度和分派的基本单位,共享进程资源

3.引入进程是为了多个程序并发执行,提高资源的利用率和系统吞吐量

4.引入线程是为了减少程序在并发执行时付出的时空开销

 

二、线程分类

系统级线程

(核心级线程):由操作系统内核进行管理,使用户程序可以创建、执行、撤销线程

用户及线程

管理过程全部由用户程序完成,操作系统内核只对进程进行管理

 

三、多线程的优势

1.多线程使系统空转时间减少,提高CPU利用率

2.进程间不能共享内存,但线程之间共享内存非常容易

3.使用多线程实现多任务并发比多进程的效率高

4.Java语言内置多线程功能支持,简化了Java的多线程编程

 

四、线程的创建和启动

创建(两种方法):

1.继承java.lang.Thread类,并覆盖run() 方法

class MyThread extends Thread {
     public void run( ) {
         /* 覆盖该方法*/
      }
 }

 

2.java.lang.Runnable接口,并实现run() 方法

class MyThread implements Runnable{
      public void run( ) {
            /* 实现该方法*/ 
      }
 }

 

启动:

1.新建的线程不会自动开始运行,必须通过start( )方法启动

2.启动继承Thread的线程

MyThread t = new MyThread ();
t.start();

 

3.启动实现Runnable接口的线程

MyThread mt = new MyThread ();
Thread t = new Thread(mt);
t.start();

 

PS:Java程序启动时,会立刻创建主线程,main就是在这个线程上运行。当不再产生新线程时,程序是单线程的。

 

两种线程创建方式的比较

1.继承Thread类方式的多线程:

优势:编写简单

劣势:无法继承其它父类

2.实现Runnable接口方式的多线程:

优势:可以继承其它类,多线程可共享同一个Thread对象

劣势:编程方式稍微复杂,如果需要访问当前线程,需要调用Thread.currentThread()方法

 

Thread类的常用方法:

方法 功能
static Thread currentThread() 得到当前线程
final String getName() 返回线程的名称
final void setName(String name) 将线程的名称设置为由name指定的名称
void start() 调用run( )方法启动线程,开始线程的执行
void run() 存放线程体代码

 

五、 线程的状态

1.新生:使用new关键字创建一个线程后,尚未调用其start方法之前

2.可运行:调用线程对象的start方法之后

3.阻塞:一种“不可运行”的状态,在得到一个特定的事件之后会返回到可运行状态

4.死亡:线程的run方法运行完毕或者在运行中出现未捕获的异常时



 

 

六、线程调度

优先级概述:

1.每个线程执行时都具有一定的优先级。当调度线程时,会优先考虑级别高的线程

2.默认情况下,一个线程继承其父线程的优先级

3.使用线程对象.setPriority(p)来改变线程的优先级

4.优先级影响CPU在线程间切换,切换的原则是:

a. 当一个线程通过显式放弃、睡眠或者阻塞、自愿释放控制权时,所有线程均接受检查而优先级高线程将会优先执行

b.一个线程可以被一个高优先级的线程抢占资源

c.同级别的线程之间,则通过控制权的释放,确保所有的线程均有机会运行

 

sleep()yield()对比:

  sleep()

yield()

暂停后的状态 进入被阻塞的状态,直到经过指定时间后,才进入可运行状态 直接将当前线程转入可运行状态

没有其他等待运行的线程

当前线程会继续等待指定的时间

当前线程会马上恢复执行

等待线程的优先级别

不考虑,机会均等 将优先级相同或更高的线程运行

 

setDaemon():

1.可以将指定的线程设置成后台线程

2.创建后台线程的线程结束时,后台线程也随之消亡

 

七、线程同步的必要性

1.当多个线程访问同一个数据时,容易出现线程安全问题。需要让线程同步,保证数据安全

2.线程同步

当两个或两个以上线程访问同一资源时,需要某种方式来确保资源在某一时刻只被一个线程使用

3.线程同步的实现方案

a.同步代码块

b.同步方法

 

八、线程同步的好处和缺点

好处:解决了线程安全问题

缺点:性能下降,或会带来死锁

 

九、死锁

1.当两个线程相互等待对方释放“锁”时就会发生死锁

2.出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续

3.多线程编程时应该注意避免死锁的发生

 

十、线程间通信的必要性

生产者和消费者问题:

1.假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费

2.如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止

3.如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止



 

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

 

对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费。

 

对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费。

1.synchronized可阻止并发更新同一个共享资源,实现了同步

2.synchronized不能用来实现不同线程之间的消息传递(通信)

 

Java提供了3个方法解决线程之间的通信问题:

方法名  

final void wait()

表示线程一直等待,直到其它线程通知

void wait(long timeout)

线程等待指定毫秒参数的时间

final void wait(long timeout,int nanos)

线程等待指定毫秒、微妙的时间

final void notify()

唤醒一个处于等待状态的线程

final void notifyAll()

唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行

PS:均是java.lang.Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。

 

十一、龟兔赛跑的例子

1.继承Thread类的demo

package org.e276.thread;

/**
 * 龟兔赛跑1
 * 
 * @author miao
 * 
 */
public class ThreadDemo1 {

	/**
	 * 主线程
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		MyThread1 t = new MyThread1();
		t.start();
		while (true) {
			System.out.println("兔子领先了,别骄傲!");
		}
	}
}

/**
 * 其它线程
 * 继承Thread类
 * @author miao
 * 
 */
class MyThread1 extends Thread {

	public void run() {
		while (true) {
			System.out.println("乌龟领先了,加油!");
		}
	}
}

 

 

2.实现Runnable接口的demo

package org.e276.thread;

/**
 * 龟兔赛跑2
 * 
 * @author miao
 * 
 */
public class ThreadDemo2 {
	/**
	 * 主线程
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		// 需要把MyThread2类作为线程的参数
		Thread t = new Thread(new MyThread2());
		t.start();
		while (true) {
			System.out.println("兔子领先了,别骄傲!");
		}
	}
}

/**
 * 其它线程 实现Runnable接口
 * 
 * @author miao
 * 
 */
class MyThread2 implements Runnable {

	@Override
	public void run() {
		while (true) {
			System.out.println("乌龟领先了,加油!");
		}
	}
}

 

 

 

十二、线程的插队的例子

package org.e276.thread;

/**
 * 线程的插队
 * 
 * @author miao
 * 
 */
public class JoinTest extends Thread {
	/**
	 * 有参构造方法,给当前线程起名
	 * 
	 * @param name
	 */
	public JoinTest(String name) {
		super(name);
	}

	public void run() {
		for (int i = 0; i < 5; i++) {
			System.out.println(getName() + " " + i);
		}
	}

	/**
	 * 主线程
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		for (int i = 0; i < 10; i++) {
			if (i == 5) {
				// 阻塞了原有的线程,等JoinTest运行完成以后,原有的线程继续运行
				JoinTest jt = new JoinTest("半路加入的线程");
				try {
					jt.start();
					// 如果没有join(),则当前线程要等原有的线程运行完毕后才会运行
					jt.join();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			// Thread.currentThread().getName() 表示获取当前线程的名字
			System.out.println(Thread.currentThread().getName() + "" + i);
		}
	}
}

 

 

 

十三、线程的睡眠的例子

package org.e276.thread;

/**
 * 线程的睡眠
 * 
 * @author miao
 * 
 */
public class SleepTest {

	public static void main(String[] args) {
		System.out.println("Wait");
		// 让主线程等待5秒后再执行
		Wait.bySec(5);
		// 提升恢复执行
		System.out.println("start");
	}
}

/**
 * 另一线程类
 * 
 * @author miao
 * 
 */
class Wait {
	/**
	 * 秒
	 * 
	 * @param s
	 */
	public static void bySec(long s) {
		// sleep 5个1秒
		for (int i = 0; i < s; i++) {
			System.out.println(i + 1 + "秒");
			try {
				// sleep 1秒
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

 

 

 

十四、后台线程的例子

package org.e276.thread;

/**
 * 后台线程
 * 
 * @author miao
 * 
 */
public class DaemonTest extends Thread {

	public void run() {
		while (true) {
			System.out.println(getName());
		}
	}

	/**
	 * 主线程
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		DaemonTest dt = new DaemonTest();
		// 设置为后台线程
		dt.setDaemon(true);
		dt.setName("后台线程");
		dt.start();
		// 主线程循环10次
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + "" + i);
		}
	}
}

 

 

 

十五、线程同步的例子,模拟同时取钱

1.创建银行账户类Account

package org.e276.bank;

/**
 * 账户
 * 
 * @author miao
 * 
 */
public class Account {

	// 余额
	private int balance = 500;

	// 检查余额
	public int getBalance() {
		return balance;
	}

	// 取款
	public void withdraw(int amount) {
		balance = balance - amount;
	}
	
}

 

 

2.创建取钱线程TestAccount

package org.e276.bank;
/**
 * 取钱线程TestAccount
 * @author miao
 *
 */
public class TestAccount implements Runnable {

	// 所有的用此TestAccount对象创建的线程共享同一个线程
	private Account acct = new Account();

	@Override
	public void run() {
		for (int i = 0; i < 5; i++) {
			// 取款,每次取100
			makeWithdrawal(100);
			if (acct.getBalance() < 0) {
				System.out.println("账户透支了!");
			}
		}
	}

	/**
	 * 取钱的方法 synchronized 可以加在void前或者方法内作为代码块,代表线程同步
	 * 
	 * @param amt
	 */
	private synchronized void makeWithdrawal(int amt) {
		if (acct.getBalance() >= amt) {
			System.out.println(Thread.currentThread().getName() + "准备取款,余额:" + acct.getBalance());
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 如果余额足够,则取款
			acct.withdraw(amt);
			System.out.println(Thread.currentThread().getName() + "完成取款,余额:" + acct.getBalance());
		} else {
			// 余额不够就给出提示
			System.out.println("余额不足以支付" + Thread.currentThread().getName() + "的取款,余额:"
					+ acct.getBalance());
		}
	}

}

 

 

3.创建测试类TestWithdrawal

package org.e276.bank;
/**
 * 测试类
 * @author miao
 *
 */
public class TestWithdrawal {

	public static void main(String[] args) {
		//创建两个线程分别表示张三和她妻子
		TestAccount t = new TestAccount();
		Thread one = new Thread(t);
		Thread two = new Thread(t);
		one.setName("张三");
		two.setName("妻子");
		one.start();
		two.start();
	}
	
}

 

 

 

十六、线程间通信的例子

package org.e276.communication;

/**
 * 生产者消费者问题 线程间的通信
 * 
 * @author miao
 * 
 */
class SharedData {

	private char c;

	// 信号量
	private boolean isProduced = false;

	/**
	 * 同步方法putShareChar()
	 * 
	 * @param c
	 */
	public synchronized void putShareChar(char c) {
		// 如果产品还没消费,则生产者等待
		if (isProduced) {
			try {
				System.out.println("消费者还未消费,因此生产者停止生产");
				// 生产者等待,这是Object的方法
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.c = c;
		// 标记已经生产
		isProduced = true;
		// 通知消费者已经生产,可以消费,这也是Object的方法
		notify();
		System.out.println("生产了产品" + c + ",通知消费者消费。。。");
	}

	/**
	 * 同步方法getShareChar()
	 * 
	 * @return
	 */
	public synchronized char getShareChar() {
		// 如果产品还未生产,则消费者等待
		if (!isProduced) {
			try {
				System.out.println("消费者还未消费,因此生产者停止生产");
				// 消费者等待,这是Object的方法
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		// 标记已经消费
		isProduced = false;
		// 通知需要生产
		notify();
		System.out.println("消费者消费了产品" + c + ",通知生产者生产。。。");
		return this.c;
	}
}

/**
 * 生产者线程
 * 
 * @author miao
 * 
 */
class Producer extends Thread {

	private SharedData s;

	Producer(SharedData s) {
		this.s = s;
	}

	public void run() {
		for (char ch = 'A'; ch <= 'D'; ch++) {
			try {
				Thread.sleep((int) (Math.random() * 2000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 将产品放入仓库
			s.putShareChar(ch);
		}
	}
}

/**
 * 消费者线程
 * 
 * @author miao
 * 
 */
class Consumer extends Thread {

	private SharedData s;

	Consumer(SharedData s) {
		this.s = s;
	}

	public void run() {
		char ch;
		do {
			try {
				Thread.sleep((int) (Math.random() * 2000));
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 从仓库中取出产品
			ch = s.getShareChar();
		} while (ch != 'D');
	}
}

/**
 * 测试类
 * 
 * @author miao
 * 
 */
public class CommunicationDemo {

	public static void main(String[] args) {
		// 共享同一个共享资源
		SharedData s = new SharedData();
		// 生产者线程
		new Producer(s).start();
		// 消费者线程
		new Consumer(s).start();
	}

}

 

 

 

十七、demo

E276-Thread.zip

  • 大小: 10 KB
  • 大小: 48.2 KB
分享到:
评论

相关推荐

    Java多线程设计模式上传文件

    Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...

    java多线程读取文件

    Java多线程读大文件 java多线程写文件:多线程往队列中写入数据

    java多线程ppt

    java多线程PPT 多线程基本概念 创建线程的方式 线程的挂起与唤醒 多线程问题

    java 多线程操作数据库

    一个java 多线程操作数据库应用程序!!!

    java多线程经典案例

    java多线程经典案例,线程同步、线程通信、线程阻塞等经典案例

    Java多线程编程技术

    《Java多线程编程核心技术》建议猿友们读两遍,因为其写得没有那么抽象,第一遍有些概念不是很理解,可以先跳过并记录起来,第一遍阅读的目的主要是了解整个架构。第二遍再慢慢品味,并贯穿全部是指点来思考,并将...

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南(核心篇) 高清pdf带目录 随着现代处理器的生产工艺从提升处理器主频频率转向多核化,即在一块芯片上集成多个处理器内核(Core),多核处理器(Multicore Processor)离我们越来越近了――如今...

    Java多线程知识点总结

    该文档总结了Java多线程相关的知识点,分享给大家,简单易懂!

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    java多线程通信图解

    一张图方便理解和掌握java 多线程之间通信的实质 java 多线程 其实就是每个线程都拥有自己的内存空间,多线程之间的通信,比例A线程修改了主内存(main方法的线程)变量,需要把A线程修改的结果同步到主线程中,...

    java多线程处理数据库数据

    java多线程处理数据库数据,使用并发包,无框架,可批量处数据库数据,进行增删改。。等等操作。

    java多线程,对多线程,线程池进行封装,方便使用

    java多线程,对多线程,线程池进行封装,方便使用

    Java多线程编程经验

    现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 线程是指进程中的一个执行流程,一个进程中可以运行多个线程。...本文档提供Java多线程编程经验,方便广大Java爱好者研究学习Java多线程

    java多线程处理大数据

    java多线程处理大数据,可根据配置的线程数,任务去调度处理

    java多线程并发

    java多线程并发的在新窗口

    Java多线程机制(讲述java里面与多线程有关的函数)

    Java多线程机制 9.1 Java中的线程 9.2 Thread的子类创建线程 9.3 使用Runable接口 9.4 线程的常用方法 9.5 GUI线程 9.6 线程同步 9.7 在同步方法中使用wait()、notify 和notifyAll()方法 9.8 挂起、恢复和终止线程 ...

    java多线程核心技术

    资深Java专家10年经验总结,全程案例式讲解,首本全面介绍Java多线程编程技术的专著 结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如...

    java多线程实现大批量数据导入源码

    java多线程实现大批量数据切分成指定份数的数据,然后多线程处理入库或者导出,线程的个数和每份数据的数量都可以控制

    java多线程查询数据库

    java多线程并发查询数据库,使用线程池控制分页,并发查询。

    java多线程模拟队列实现排队叫号

    java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号 java多线程模拟队列实现排队叫号,多线程模拟排队叫号取号

Global site tag (gtag.js) - Google Analytics