java_CH7_多线程

多线程编程

一、基本概念

线程与进程:

进程是程序一次完整的从代码加载、执行到执行完毕的完整过程。

线程是比进程更小的执行单位,是在进程的基础上进行进一步划分。多线程是指一个进程在执行过程中可以产生多个更小的程序单元(即线程),这些线程可以同时存在、同时运行。一个进程可能包含多个同时执行的线程。

我们需要一个多任务操作系统,可以同时支持多个进程的执行。 进程比线程开销更大,因此在同类多任务计算时, 采用多线程 。由于速度差以及多处理器问题,使多线程比单线程执行效率更高

二、线程的创建

(一)实现Runable接口

实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。

在这里插入图片描述

public class SimpleThread1 implements Runnable{
	private int countDown = 5;
	private int threadNumber;
	private static int threadCount = 0;
	
	public SimpleThread1(){
		threadNumber = ++threadCount;
		System.out.println("Making " + threadNumber);
	}
	
	public void run(){  //线程体:每个线程执行countdown的递减
		while(true){
			System.out.println("Thread " + 
			threadNumber + "(" + countDown + ")");
		if(--countDown == 0) return;
		}
	}
  
	public static void main(String[] args){
		for(int i = 0; i < 5; i++){
			Thread thr = new Thread(new SimpleThread1());//创建线程 注意!!仍需包装在Thread线程类里
			thr.start();//启动线程
		}
		System.out.println("All Threads Started");
	}
}

在这里插入图片描述
可以看到各个线程之间的竞争关系,处于无序状态

(二)继承类Thread

在这里插入图片描述

Thread是Runnable的子类,Thread类的run方法是调用的Runnable的方法

public class mythread extends Thread
public class SimpleThread extends Thread {
	private int countDown = 5;
	private int threadNumber;
	private static int threadCount = 0;
	
	public SimpleThread(){
		threadNumber = ++threadCount;
		System.out.println("Making " + threadNumber);
	}
	
	public void run(){
		while(true){
			System.out.println("Thread " + 
			threadNumber + "(" + countDown + ")");
		if(--countDown == 0) return;
		}
	}
  
	public static void main(String[] args){
		for(int i = 0; i < 5; i++)
			new SimpleThread().start();//这里直接调用Thread的继承类
		System.out.println("All Threads Started");
	}
}

在这里插入图片描述

创建并启动线程 ,上面两种方式创建线程时会略有不同 ,启动线程均用start() 方法

每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称

– run方法是运行线程的主体,启动线程时,会直接被调用

联系与区别
在这里插入图片描述

Thread是Runnable的子类,Thread类的run方法是调用的Runnable的方法

继承Thread类不适合资源共享,实现Runnab接口,适合资源共享

网络多线程(TCP):

(一个人守门,然后安排多个人接待,把Socket作为入口参数传给新的线程)

/*服务器端*/
import java.io.*;
import java.net.*;

public class NetServer
{
	public static final int PORT =8080;
	public static void main(String[] args)throws Exception
	{
		ServerSocket s = null;
		try{
			InetAddress addr = InetAddress.getByName("localhost");
			s =new ServerSocket(PORT,10,addr);
			System.out.println("虚拟Web服务器启动: "+s);
			while(true){
				Socket socket =s.accept();
				SocketHandler sh = new SocketHandler(socket);
				sh.start();
				System.out.println("已开启线程处理"+socket);
			}
		}catch(Exception e){
			System.out.println(e);
			s.close();
		}
	}
}							
/*客户端*/
import java.io.*;
import java.net.*;

public class NetClient
{	
	public static final int PORT =8080;
	public static void main(String[] args) throws IOException
	{
		
		InetAddress addr = InetAddress.getByName("localhost");
		
		Socket socket =new Socket(addr,PORT);
		
		try{
			System.out.println("客户端请求: "+socket);
			
			BufferedReader in = new BufferedReader(
									new InputStreamReader(
										socket.getInputStream())) ;
				
			PrintWriter out = new PrintWriter(
									new BufferedWriter(
										new OutputStreamWriter(
											socket.getOutputStream())),true);
			out.println(socket+"想获得一些信息");
			//byte[] input = new byte[20];
			//System.in.read(input);
			out.println("END");
			String str;
			System.out.println("客户端请求发送完毕...");		
			//while((str = in.readLine()).length()!=0){
				//System.out.println("接收: "+in.readLine());
			//}
			}finally{
				System.out.println("客户端关闭...");
				socket.close();
			}
	}
}							

在这里插入图片描述

三、线程状态

在这里插入图片描述

四、控制线程的几种方式

• 线程睡眠 – sleep()方法,是静态方法,Thread.sleep()是让当前线程睡眠(异常爆发可以唤醒,大程序多使用sleep节能)

在这里插入图片描述

• 中断线程 – interrupt()方法,当线程被阻塞(sleep, wait)时,也有可 能被通知处理一些内容,通过InterruptedException 异 常来实现,注意:中断≠终止

import java.util.*;
public class TestInterrupt {
	public static void main(String[] args){
		MyThread thread = new MyThread();
		thread.start();
		try{
			Thread.sleep(10000);//当前线程main线程睡眠10秒
		}
		catch (InterruptedException e) {}
		thread.interrupt();
	}
}

class MyThread extends Thread { //线程体
	boolean flag = true;
	public void run(){
		while(flag){
			System.out.println("==="+new Date()+"===");
			try {
				sleep(1000);//线程内部,MyThread线程睡眠1秒
			}catch (InterruptedException e){
			System.out.println(e);
			return;
			}
		}
	}
}

在这里插入图片描述

10秒后主程序线程的醒来会打断类里面的线程(上面的日期一秒出来一个说明类线程在一秒睡次觉)

• 停止线程 – stop()方法,该方法已被弃用,因为会导致无法预测的 情况

​ 替代停止方法是使用boolean 标志位

public class TestThread {
	public static void main(String args[]) {
		Runner4 r = new Runner4();
		Thread t = new Thread(r);
		t.start();
		for (int i = 0; i < 100000; i++) {
			if (i % 10000 == 0 & i > 0)
				System.out.println("in thread main i=" + i);
		}
		System.out.println("Thread main is over");
		r.shutDown();
		// t.stop();
	}
}

class Runner4 implements Runnable {
	private boolean flag = true;

	public void run() {
		int i = 0;
		while (flag == true) {
			System.out.print(" " + i++);
		}
	}

	public void shutDown() {
		flag = false;
	}
}

主线程执行飞快,子线程一次次循环比较慢

**• 主动让出 ** – yield()方法,主动放弃执行的时间片

public class TestYield {
  public static void main(String[] args) {
    MyThread3 t1 = new MyThread3("t1");
    MyThread3 t2 = new MyThread3("t2");
    t1.start(); t2.start();
  }
}
class MyThread3 extends Thread {
  MyThread3(String s){super(s);}
  public void run(){
    for(int i =1;i<=100;i++){
      System.out.println(getName()+": "+i);
      if(i%10==0){
        yield();
      }
    }
  }
}

在这里插入图片描述

本来是在抢,然后到10的倍数就让给对方

(操作线程的主要方法在Thread类里)

五、线程优先级

每个线程都有优先级,调度器根据优先级 决定调度顺序 • 优先级从1-10,缺省值为5,通过 setPriority (int priority)来设定 Thread类
在这里插入图片描述

定义描述常量
public static final int MIN_PRIORITY最低优先级1
public static final int NORM_PRIORITY中等优先级(默认)5
public static final int MAX_PRIORITY最高优先级10
public class TestPriority {
	public static void main(String[] args) {
		Thread t1 = new Thread(new T1());
		Thread t2 = new Thread(new T2());
		t1.setPriority(Thread.NORM_PRIORITY + 3);//可以直接设数字
		t1.start();
		t2.start();
	}
}

class T1 implements Runnable {
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println("T1: " + i);
		}
	}
}

class T2 implements Runnable {
	public void run() {
		for(int i=0; i<10; i++) {
			System.out.println("------T2: " + i);
		}
	}
}

在这里插入图片描述

优先运行T1,再运行T2

六、互斥与锁

存在多个线程同时访问一个对象的情况, 如果不进行有效互斥,则会出现混乱

中关键的代码段需要加锁,防止 顺序执行过程中被其它线程“插入”

synchronized :关键字,不属于包和类

在这里插入图片描述

public class TestSync implements Runnable {
  Timer timer = new Timer();
  public static void main(String[] args) {
    TestSync test = new TestSync();
    Thread t1 = new Thread(test);
    Thread t2 = new Thread(test);
    t1.setName("t1"); 
    t2.setName("t2");
    t1.start(); 
    t2.start();
  }
  public void run(){
    timer.add(Thread.currentThread().getName());//取当前进程的名字
  }
}

class Timer{
  private static int num = 0;
  public void add(String name){ //给方法加锁
  	//synchronized (this) {
	    num ++;
	    try {Thread.sleep(100);} 
	    catch (InterruptedException e) {}
	    System.out.println(name+", 你是第"+num+"个使用timer的线程");
	 //}
  }
}

不加锁时:T1的顺序执行流程会被T2插入,导致T2也会使得NUM’加一,最后t1显示的结果也会是第二次调用

在这里插入图片描述

加锁后:T1能够完整地执行完代码,可以显示第一个调用(T1,T2谁先跑,要看谁抢得赢)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值