多线程-thread
Thread类和Runnable接口都在java.lang包中。
内容:
1.多线程的基本概念
2.创建线程
3.实例-龟兔赛跑
4.线程之间的数据交流
5.线程调度
6.线程的基本控制(暂放,这里的实例是卖票那个例子)
7.线程同步(synchronized 标记)
实现线程同步化需要使用synchronized 关键字
8.线程死锁
1.多线程的概念
java 语言支持多线程技术。(multithreaded programing)
为什么要有多线程?
是在编程时,会遇到多个用户的请求,为了更好地响应多个用户的请求,并及时作出响应。
(1)线程的概念
先介绍进程和多进程的概念,再介绍线程的概念。
1.1 什么叫做进程?进程是程序的一次动态的执行过程,经历了代码开始到执行完毕的一个完整过程。多进程操作系统能并发运行多个进程。
并发运行实际上是交叉运行。
因为计算机某个时刻只能执行一个任务,
因此多进程是指多个进程交叉运行,给人的感觉好像是同时运行的。
什么叫线程?
线程是由进程派发的,是比进程更小的执行单位。
多线程是指在单个程序(或进程)中同时运行多个线程完成不同的工作,这些线程可以同时存在、同时运行,形成多条执行线程。如下图所示。
线程与进程也有共同点和不同点之处。
共同点:实现并发的一个基本单位,都要系统进行调度以提高运行效率。
不同点:(1)线程是比进程更小的执行单位;
(2)每个进程都有一个专门的内存区域,而线程却共享内存单元,线程是通过共享的内存单元来实现数据交换、实时通信等操作。
(2)线程的状态与生命周期
思考:当线程的run()方法运行完成,线程就转到(死亡)状态。
用程序进行解释线程的状态与生命周期:
(1)主类中创建新线程对象,并且start().
(2)在创建的非主类中进行写方法run().
class Thread1 extends Thread{
//3.(运行状态) 一 一种是4.死亡状态,一种是5.阻塞状态(sleep(),wait(),join().)
public void run(){
线程的任务
}
}
public class Test1{
//主进程 ,即main()方法。
public static void main(String [] argps){
Thread1 th = new Thread1(); //创建新线程的实例化对象。1.(新建状态)
th.start();//2.(就绪状态)
}
}
在一个系统中,任何时刻最多只有一个运行态线程。
sleep(),yield(),wait(),join()四个方法的定义和区别?
2.创建线程
(1)创建线程有两种方式:
1.通过继承Thread类创建线程。(喜欢用这个)
2.通过实现Runnable接口创建线程。
是否在任何情况下都能用任何一种方式创建线程?
注:(1)Thread类和Runnable接口都在java.lang包中。
(2) 创建新线程必须有run()方法。
解释:
Runnable接口:在这个接口中只定义了run()方法,这个方法是线程执行的主体,在线程获取CPY时间运行时,就是自动执行线程的run()方法,因此,新的线程要完成的任务流程应该写在run()方法中,所以创建新线程就必须有run()方法。创建线程时,可以通过实现Runnable接口获取run()方法。
Thread类:这个类是已经实现了Runnable接口的类,在这个类中除了拥有run()方法,还提高了对线程操作的方法,因此可以通过继承Thread类创建新线程。
1.通过继承Thread类创建线程
Thread类格式:
pulic class Thread extends Object implements Runnable
实例:
ThreadTest.java
package Thread;
/*步骤:
(1)主类中创建新线程对象,并且start().
(2)在创建的非主类中继承Thread类进行写方法run().
*/
//通过继承Thread类创建新线程
class Thread1 extends Thread{
//3.那么就开始执行run().[线程的任务写在run()中]
public void run(){
for(int i=0;i<100;i++)
System.out.println("Thread1在运行");
}
}
public class ThreadTest {
public static void main(String[] args) {
Thread1 th = new Thread1(); //1.创建新线程对象
th.start(); //2.启动线程Thread1
for(int i=0;i<100;i++)
System.out.println("main在运行");//4.执行完线程后,执行主线程中剩余的语句。
}
}
在这里有两条执行线索。由于线程的执行速度取决于CPU时间及速度,因此线程的程序在不同的机器上运行时,线程交替占用CPU的情况可能不一样。因此运行结果也会出现一些差别。
2.通过实现Runnable接口创建线程
格式:
class Thread1 implements Runnable{
public void run(){
}
}
public class RunnableTest {
public static void main(String[] args) {
/*与继承Thread类创建线程的区别,
就是把实现相关的创建的实例对象传给了Thread,同时Thread又创建了对象。
*/
Thread2 th2 = new Thread2();
Thread t1 = new Thread(th2);
t1.start();
}
实例:
package Thread;
/*(1)通过Runnable接口创建线程,基本与继承Thread类创建线程一致,就是在主类中有区别。
*(2)与继承Thread类创建线程的区别:
就是把实现相关的创建的实例对象传给了Thread,同时Thread又创建了对象。
* 然后利用Thread的实例化对象启动线程。
* */
class Thread2 implements Runnable{
public void run(){
for(int i=0;i<100;i++)
System.out.println("Thread1在运行");
}
}
public class RunnableTest {
public static void main(String[] args) {
/*与继承Thread类创建线程的区别,
就是把实现相关的创建的实例对象传给了Thread,同时Thread又创建了对象。
*/
Thread2 th2 = new Thread2();
Thread t1 = new Thread(th2);
t1.start();
for(int i=0;i<100;i++)
System.out.println("main在运行");
}
}
3.实例-龟兔赛跑
4.线程之间的数据交流
同一个进程中的几个线程之间是可以 共享数据的。
例如:在龟兔赛跑中,可以通过奖励他们食物的方式激励他们赛跑。
而食物就是乌龟和兔子共享的数据。
则共享数据有两种方式:
1.通过内类创建线程
2.通过构造器传递参数
1.通过内类创建线程
内类的作用是可以直接访问外类的成员变量。即把共享数据定义为外类的成员变量,乌龟和兔子定义为内类,这样就可以实现两个线程之间的数据交流了。
在本例子中,inrabtort类是包含了主类,兔子类,乌龟类。其中主类外面定义了food,作为外类的成员变量。
实例:
//把所有类都写在主类里了,且成员变量定义在主类外面。
内类:把一个类(内类)定义在另一个类(外类)中。
inrabtort.java
package thread;
public class inrabtort {
public int food = 10;
public static void main(String argps[]){
inrabtort tt = new inrabtort();
tt.go();
}
public void go(){
tortoise r1 = new tortoise();
rabbit r2 = new rabbit();
r1.start();
r2.start();
}
class tortoise extends Thread{
int i,f=0;
public void run(){
for(i=1;i<=10;i++){
System.out.println("乌龟跑到了"+i+"米处");
if(food>0){
food--;f++;
System.out.println("乌龟吃了第"+f+"个食物,还剩food="+food);
}
}
}
}
class rabbit extends Thread{
int i,f=0;
public void run(){
for(i=1;i<=10;i++){
System.out.println("兔子跑到了"+i+"米处");
if(food>0){
food--;f++;
System.out.println("兔子吃了第"+f+"个食物,还剩food="+food);
}
}
}
}
}
2.通过构造器传递参数(比较好用,不出错)
把共享数据写在一个单独的类里,然后通过构造器传递参数给各个线程。也可以对公用数据定义方法,供各个线程使用。
在本例中,把food这个共享数据单独放在一个类里,然后在兔子类和乌龟类里都定义了food fd;
然后并设置了构造器进行赋值,然后进行了引用。也就是说food类,乌龟类,兔子类,主类这四个是并列的。
实例:
package thread;
import thread.inrabtort.rabbit;
import thread.inrabtort.tortoise;
class food{
public int food =10; //共同访问的数据
}
public class structrabtort {
public static void main(String argps[]){
structrabtort tt = new structrabtort();
tt.go();
}
public void go(){
food f1 =new food();
tortoise1 r1 = new tortoise1(f1);
rabbit1 r2 = new rabbit1(f1);
r1.start();
r2.start();
}
}
class tortoise1 extends Thread{
int i;
int f=0;
food fd;
public tortoise1(food fd) {
this.fd = fd;
}
public void run(){
for(i=1;i<11;i++){
System.out.println("乌龟跑到了"+i+"米处");
if(fd.food>0){
fd.food--;f++;
System.out.println("乌龟吃了第"+f+"个食物,还剩food="+fd.food);
}
}
}
}
class rabbit1 extends Thread{
int i,f=0;
food fd;
public rabbit1(food fd) {
this.fd = fd;
}
public void run(){
for(i=1;i<11;i++){
System.out.println("兔子跑到了"+i+"米处");
if(fd.food>0){
fd.food--;f++;
System.out.println("兔子吃了第"+f+"个食物,还剩food="+fd.food);
}
}
}
}
5.线程调度
(1)为什么存在线程调度?
(2)优先级
默认优先级是5,可以通过线程实例对象.setPriority(1-10之间的数字)进行设置。
(3)sleep(),yield(),join()的介绍
使用格式:Thread.sleep(1000);
7.线程同步(synchronized 标记)
如果不会,在eclipse里输入syn+ alt +?进行提示。
实现线程同步化需要使用synchronized 关键字。
保证任意时刻只有一个线程访问该共享数据。