JAVA-多线程

进程:正在进行中的程序

线程:就是进程中一个负责程序执行的控制单元(执行路径。)

一个进程中可以有多条执行路径,称为多线程。(比如,360软件,可同时杀毒,体检,清理垃圾等等,每一个功能相当于一条执行路径,同时执行,执行多条路径,也就是多线程了)。

也就是说,当我们想让多部分代码同时执行的时候,就是多线程了。每一个线程都有自己运行的内容,这个内容可以称为线程要执行的任务。

好处与弊端:

好处:解决了多部分同时运行的问题,充分利用CPU资源,简化编程模型,带来良好的用户体验。

弊端:线程太多回到效率的降低。

其实应用程序的执行都是cpu在做着快速的切换完成的,这个切换时随机的。

类 Thread:

主线程:

main()方法即为主线程入口

产生其他子线程的线程

必须最后完成执行,因为它执行各种关闭动作

Thread td = Thread.currentThread();
System.out.println("当前线程是:"+td.getName());
td.setName("萌萌");
System.out.println("当前线程是:"+td.getName());

运行结果:

当前线程是:main
当前线程是:萌萌

在java中实现多线程的两种方式:

继承Thread类,实现Runnable接口。

class MyThread implements Runnable
class MyRunnable implements Runnable

Thread类构造方法:

Thread类常用方法:

start(); 启动线程
getId(); 获得线程ID
getName(); 获得线程名字
getPriority(); 获得优先权
isAlive(); 判断线程是否活动
isDaemon(); 判断是否守护线程
getState(); 获得线程状态
sleep(long mill); 休眠线程
join(); 等待线程结束
yield(); 放弃cpu使用权利
interrupt(); 中断线程
currentThread(); 获得正在执行的线程对象

Runnable接口:

Runnable接口方法:

 

run()方法和start()方法的区别:

Tread类中start()方法是开始线程的方法。start()方法会用特殊的方法自动调用run()方法。run()方法是Tread的具体实现。
继承了Thread类后就通过重写run()方法来说明线程的行为,调用start()方法来开始线程。

小示例:这里使用了JUNIT测试类,java里自带的,省略了main方法

public class Demo {
	@Test
	public void test1(){
		MyThread t1 = new MyThread();
		Thread td = new Thread(t1);
		td.start();
	}
}
class MyThread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name+":"+i);
		}
		System.out.println("结束~");
	}
}

输出结果:这个输出结果每次都不一样,多线程是随机的。

Thread-0:0
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-0:10
Thread-0:11
Thread-0:12
Thread-0:13
Thread-0:14
Thread-0:15
Thread-0:16
Thread-0:17
Thread-0:18
Thread-0:19
Thread-0:20
Thread-0:21
Thread-0:22
Thread-0:23
Thread-0:24
Thread-0:25
Thread-0:26
Thread-0:27
Thread-0:28
Thread-0:29
Thread-0:30
Thread-0:31
Thread-0:32
Thread-0:33
Thread-0:34
Thread-0:35
Thread-0:36
Thread-0:37
Thread-0:38
Thread-0:39
Thread-0:40
Thread-0:41
Thread-0:42
Thread-0:43

小修改一下:

public class Demo {
	@Test
	public void test1(){
		MyThread t1 = new MyThread();
		Thread td1 = new Thread(t1,"线程1");
		Thread td2= new Thread(t1,"线程2");
		td1.start();
		td2.start();
	}
}
class MyThread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name+":"+i);
		}
		System.out.println("结束~");
	}
}

部分输出结果:随机的

线程1:45
线程1:46
线程1:47
线程2:0
线程1:48
线程2:1
线程1:49
线程2:2
线程1:50
线程2:3
线程1:51
线程2:4
线程1:52
线程2:5
线程1:53
线程2:6
线程1:54
线程2:7
线程1:55
线程2:8
线程1:56

线程的状态(五个):创建、就绪、阻塞、运行、死亡。

线程调度:线程带的优先级1-10表示,1表示优先级最低,10表示优先级最高,默认是5。这些优先级对象一个Thread类的公用静态变量。

public static final int NORM_PRIORITY = 5;
public static final int MAX_PRIORITY = 10;
public static final int MIN_PRIORITY = 1;

优先级示例:

public class Demo {
	@Test
	public void test1(){
		MyThread t1 = new MyThread();
		Thread td1 = new Thread(t1,"线程1");
		Thread td2= new Thread(t1,"线程2");
		
		System.out.println(td1.getPriority());//获取优先级
		System.out.println(td2.getPriority());
		td1.start();
		td2.start();
	}
}

class MyThread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name+":"+i);
		}
		System.out.println("结束~");
	}
}

输出结果,优先级默认都是5:

5
5
线程1:0
线程1:1
线程1:2
线程1:3
线程1:4
线程1:5
线程1:6
线程1:7
线程1:8
线程1:9
线程1:10
线程2:0
线程1:11
线程2:1
线程1:12
线程2:2
线程1:13
线程2:3
线程1:14
线程2:4
线程1:15
线程2:5
线程1:16
线程2:6
线程1:17
线程2:7
线程1:18
线程2:8
线程1:19
线程2:9
线程1:20
线程2:10
线程1:21
线程2:11
线程1:22
线程2:12
线程1:23
线程2:13
线程1:24
线程2:14
线程1:25
线程2:15
线程1:26
线程2:16
线程1:27
线程2:17
线程1:28
线程2:18
线程2:19
线程1:29
线程2:20
线程1:30
线程2:21
线程1:31
线程2:22
线程1:32
线程2:23
线程1:33

小修改一下:

public class Demo {
	@Test
	public void test1(){
		MyThread t1 = new MyThread();
		Thread td1 = new Thread(t1,"线程1");
		Thread td2= new Thread(t1,"线程2");
		
		td1.setPriority(10);//指定优先级
		td2.setPriority(1);
		td1.start();
		td2.start();
	}
}
class MyThread implements Runnable{
	@Override
	public void run() {
		for (int i = 0; i < 100; i++) {
			String name = Thread.currentThread().getName();
			System.out.println(name+":"+i);
		}
		System.out.println("结束~");
	}
}

输出结果,优先级10和1的区别:

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

线程的休眠,sleep():

sleep() 的作用是让当前线程休眠,即当前线程会从“运行状态”进入到“休眠(阻塞)状态”。

sleep()会指定休眠时间,线程休眠的时间会大于/等于该休眠时间;

在线程重新被唤醒时,它会由“阻塞状态”变成“就绪状态”,从而等待cpu的调度执行。

sleep()与wait()的比较:

wait()的作用是让当前线程由“运行状态”进入“等待(阻塞)状态”的同时,也会释放同步锁。

而sleep()的作用是也是让当前线程由“运行状态”进入到“休眠(阻塞)状态”。

但是,wait()会释放对象的同步锁,而sleep()则不会释放锁。

wait()可以指定时间也可以不指定时间。sleep()必须指定时间。

在同步中,对CPU的执行权和锁的处理不同。

wait:释放执行权,释放锁。

sleep:释放执行权,不释放锁。

线程的强制运行,join():

与sleep()方法一样,调用join()方法需要处理InterruptedException异常。

线程的礼让,yield():

yield()方法可暂停当前线程执行,允许其他具有相同优先级的线程获得运行机会,该线程仍处于就绪状态,不转为阻塞状态,此时,系统选择其他相同或更高优先级线程执行,若无其他相同或更高优先级线程,则该线程继续执行。

最后示例演示:

public class Demo {
	@Test
	public void test1() throws InterruptedException{
		PaShan ps = new PaShan();
		Thread nqr = new Thread(ps,"年轻人");
		Thread old = new Thread(ps,"老年人");
		
		nqr.start();
		old.start();
		
		Thread.currentThread().join();//暂停主线程
	}
}
class PaShan implements Runnable{
	@Override
	public void run() {
		//得到当前线程的名称
		String name = Thread.currentThread().getName();
		for (int i = 1; i <= 10; i++) {
			System.out.println(name+"爬完"+(i*100)+"米");
			//加一个判断
			if(name.equals("年轻人")){try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}}
			else{try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}}
		}
		System.out.println(name+"爬到山顶了");
	}
}

动态直观输出结果:

线程同步的实现:

当两个或多个线程需要访问同一资源时,需要以某种顺序来确保该资源某一时刻只能被一个线程使用,这就称为线程同步。

采用线程同步来控制线程的执行有两种方式,即同步代码方法和同步代码块。这两种方式都使用synchronized关键字实现。

1.同步方法

使用synchronized修饰的方法控制对类成员变量的访问。每个类实例对应一把锁,方法一旦执行,就独占该锁,知道从该方法返回时才将锁释放,此后,被阻塞的线程方能获得该锁,重新进入可执行状态。

示例:

创建Site类:

public class Site implements Runnable {
	public int count = 10;//剩余票数
	public int num = 0;//买到第几张票票
	public boolean flag = false;
	@Override
	public void run() {
		while(!flag){
			sale();
		}
	}
	public synchronized void sale(){
			if(count<=0){
				flag = true;
				return;
			}
			num++;
			count--;
				try {
					Thread.sleep(500);//模拟网络延迟
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			//第二步:显示信息
			System.out.println(Thread.currentThread().getName()+"抢到第"+num+"张票,剩余"+count+"张票!");
		}
	}

创建测试类:

public class MyTest {
	public static void main(String[] args) {
		Site site = new Site();
		Thread p1 = new Thread(site,"普通人");
		Thread p2 = new Thread(site,"代理");
		Thread p3 = new Thread(site,"黄牛");
		p1.start();
		p2.start();
		p3.start();
	}
}

输出结果:

普通人抢到第1张票,剩余9张票!
普通人抢到第2张票,剩余8张票!
普通人抢到第3张票,剩余7张票!
黄牛抢到第4张票,剩余6张票!
代理抢到第5张票,剩余5张票!
黄牛抢到第6张票,剩余4张票!
黄牛抢到第7张票,剩余3张票!
普通人抢到第8张票,剩余2张票!
普通人抢到第9张票,剩余1张票!
普通人抢到第10张票,剩余0张票!

2.同步代码块

代码块即使用{}括起来的一段代码,使用synchronized关键字修饰的代码块,称为同步代码块。

实际上实现多线程的方式还有一种:

实现Callable接口,重写call方法。Callable接口与Runnable接口的功能类似,但提供了比Runnable更强大的功能。有以下三点
(1)Callable可以在人物结束后提供一个返回值,Runnable没有提供这个功能。
(2)Callable中的call方法可以抛出异常,而Runnable的run方法不能抛出异常。
(3)运行Callable可以拿到一个Future对象,表示异步计算的结果,提供了检查计算是否完成的方法。

需要注意的是,无论用那种方式实现了多线程,调用start方法并不意味着立即执行多线程代码,而是使得线程变为可运行状态。

原文地址:https://www.cnblogs.com/yn-yinian/p/7788405.html