【JavaSE学习笔记】多线程01_Thread,Runnable,synchronize同步机制

多线程01

A.进程

要学习多线程,得学习线程,但线程是依赖于进程而存在的,所以先学习进程

1)什么是进程

通过查看任务管理器,发现正在运行的程序就是一个进程

2)多进程有什么意义

现在的计算机是一个多进程计算机,在去做一件事情的同时可以做另一件事情;提高CPU的使用率

打游戏---------开启了游戏进程

听音乐---------开启了音乐进程

在打游戏的同时去听音乐,这两个进程是同时发生的吗?

不是同时在进行,感觉他们在同时进行,这个因为CPU在两者之间进行着高效的切换(抢占CPU的执行权)

B.线程

1)什么是线程

线程是依赖于进程而存在的,把线程可以看作是进程中的某一个任务

比如迅雷软件,扫雷(开始--->玩的同时--->计时)

2)CPU执行权

具有随机性!(每一个线程在抢占CPU的执行权具有随机性)

3)单线程

程序的执行路径只有一条

4)多线程

程序的执行路径有多条


5)并行和并发

都是同时的意思

前者是逻辑上的同时,指的是在同一个时间内(时间段)同时发生

后者是物理上的同时,指的是在同一个时间点(10:30:30进行开抢:购物商城...)上同时发生

6)面试题

Jvm:Java虚拟机是多线程的吗?

程序能够被执行:是因为里面main()方法,main()被Jvm所识别,启动Jvm,实质上式启动了主线程(main方法所在的就是主线程),通过以下代码:两个输出语句之间做了多次创建对象,程序依然能够执行打印出来,Java语言会自动启动垃圾回收期(垃圾回收线程),保证内存不会溢出

java虚拟机是多线程,至少开启了两个线程:

main():主线程

垃圾回收线程:保证及时的回收不用的对象

public class ThreadDemo {
	public static void main(String[] args) {
		System.out.println("hello");
		new Object();
		new Object();
		new Object();
		new Object();
		new Object();
		new Object();
		new Object();
		//............new Object();
		
		System.out.println("world");
	}
}

C.实现多线程—Thread

1)如何实现

Java语言是不能直接调用系统功能(用系统功能创建对象)

Java语言可以通过C/C++底层已经调用了系统功能

Java提供了一个类:Thread类

Java虚拟机允许应用程序并发的运行多个执行线程

2)多线程开发步骤

a.自定义一个类,该类继承自Thread类

b.重写该类中的run()方法

// 1)自定义该类继承字Thread
public class MyThread extends Thread {
	
	// 2)重写run方法
	@Override
	public void run() {
		// 该方法里面需要执行一些耗时的操作:循环语句.../sleep()
		for (int i = 0; i < 10; i++) {
			System.out.println(getName() + ":" + i);
			// getName()获取线程名称,为了区分不同线程,这里先用,后面详细讲解
		}
	}
}

c.主线程中去创建该类对象

d.启动线程

public class ThreadDemo {
	public static void main(String[] args) {
		// 创建对象
		// MyThread my = new MyThread();

		// 有对象了,启动线程,执行耗时的操作!
		// my.run();
		// 在调用一次
		// my.run();

		// 发现:调用run()方法,每次调用,执行完毕之后
		// 并没有出现线程抢占CPU的执行权出现的一种随机性
		
		/**
		 * 调用run(),其实相当于调用了一个普通方法
		 * 启动线程:start()方法,调用start(),其实是让Jvm去调用线程的run方法()
		 * 来实现执行多线程的一种随机性
		 */
		// 启动线程
		// my.start();
		// 在启动一个线程
		// java.lang.IllegalThreadStateException:非法线程状态异常:
		// 因为已经启动了一次了,不能使用同一个线程对象将start()调用多次
		// my.start();
		
		// 正确写法 创建两个线程
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		
		// 启动线程
		mt1.start();
		mt2.start();
	}
}

多次运行,观察结果

后面为了方便,只创建一个类

3)获取线程名称

public final String getName():返回该线程的名称

public final void setName(String name):改变线程名称,使之与参数 name 相同

有参构造的方式

//有参构造的方式
	public MyThread(String name){
		super(name) ;
	}

注意:这两个方法其实是面向对象里得set/get方法,在Thread里已经存在

如果自定义成员变量name,那么写set/get方法时会报错

public class ThreadDemo {
	public static void main(String[] args) {
		
		// 创建两个线程
		MyThread mt1 = new MyThread();
		MyThread mt2 = new MyThread();
		
		// 设置线程名称
		mt1.setName("线程1");
		mt2.setName("线程2");
		
		// 启动线程
		mt1.start();
		mt2.start();
	}
}

4)获取当前正在执行的线程名称

public static Thread currentThread():返回当前正在执行的线程名称

System.out.println(Thread.currentThread().getName());// main

D.Thread常用方法

1)sleep():休眠(暂停)

public static void sleep(long millis):在指定的毫秒数内让当前正在执行的线程休眠(暂停执行)

import java.util.Date;

class ThreadSleep extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(getName() + ":" + ",执行时间:" + new Date());
			try {
				// 休眠1秒
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

public class ThreadSleepDemo {
	public static void main(String[] args) {
		// 创建三个现成对象
		ThreadSleep ts1 = new ThreadSleep();
		ThreadSleep ts2 = new ThreadSleep();
		ThreadSleep ts3 = new ThreadSleep();
		
		// 给三个线程设置名称
		ts1.setName("线程1");
		ts2.setName("线程2");
		ts3.setName("线程3");
		
		// 启动线程
		ts1.start();
		ts2.start();
		ts3.start();
	}
}

2)join():等待该线程终止

public final void join():等待该线程终止

class ThreadJoin extends Thread {

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

public class ThreadJoinDemo {
	public static void main(String[] args) {
		ThreadJoin tj1 = new ThreadJoin();
		ThreadJoin tj2 = new ThreadJoin();
		ThreadJoin tj3 = new ThreadJoin();

		tj1.setName("线程1");
		tj2.setName("线程2");
		tj3.setName("线程3");

		tj1.start();
		try {
			tj1.join();// 等待tj1线程终止
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 当线程1运行结束,后两个才开始争夺CPU执行权
		tj2.start();
		tj3.start();
	}
}

3)Yeild():暂停当前(中断),执行其他

public static void yield():暂停当前正在执行的线程对象,并执行其他线程。

线程的执行具有随机性,并且不能保证该线程永远暂停,可能抢占到了CPU的执行权就会执行它...

class ThreadYeild extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			
			// 当执行i == 4 时,线程中断,执行其他线程
			if (i == 4) {
				Thread.yield();
				System.out.println(getName() + ":中断");
			}
			System.out.println(getName() + ":" + i);
		}
	}
}

public class ThreadYeildDemo {
	public static void main(String[] args) {
		ThreadYeild ty1 = new ThreadYeild();
		ThreadYeild ty2 = new ThreadYeild();
		ThreadYeild ty3 = new ThreadYeild();
		
		ty1.setName("线程1");
		ty2.setName("线程2");
		ty3.setName("线程3");
		
		ty1.start();
		ty2.start();
		ty3.start();
	}
}

4)stop()和interrupt()

public final void stop():强制停止

public void interrupt():中断线程,中断当前线程的这种状态(打破了一种状态)

import java.util.Date;

class ThreadStop extends Thread {
	@Override
	public void run() {
		System.out.println("开始执行:" + new Date());

		// 睡眠10s
		try {
			Thread.sleep(10000);
		} catch (InterruptedException e) {
			System.out.println("线程被强行中止...");
		}

		System.out.println("结束执行:" + new Date());
	}
}

public class ThreadStopDemo {
	public static void main(String[] args) {
		ThreadStop ts = new ThreadStop();

		ts.start();

		// 休眠3s,不执行就终止
		try {
			Thread.sleep(3000);
			// ts.stop();// 过时了,但可以用
			ts.interrupt();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

5)setDemon():守护线程

public final void setDaemon(boolean on):将该线程标记为守护线程或用户线程

当正在运行的线程都是守护线程时,Java 虚拟机退出

该方法必须在启动线程前调用!

on:设置为true,则表示该线程是守护线程!

class ThreadDemon extends Thread {
	@Override
	public void run() {
		for (int i = 0; i < 20; i++) {
			System.out.println(getName() + ":" + i);
		}
	}
}

public class ThreadDemonDemo {
	public static void main(String[] args) {
		ThreadDemon td1 = new ThreadDemon();
		ThreadDemon td2 = new ThreadDemon();
		ThreadDemon td3 = new ThreadDemon();

		td1.setName("主  公");
		td2.setName("忠臣1");
		td3.setName("忠臣2");

		td2.setDaemon(true);
		td3.setDaemon(true);

		td1.start();
		try {
			// 主公执行完,只剩守护线程,不会立即结束,但再执行几次,程序终止
			td1.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		td2.start();
		td3.start();

	}
}

E.实现多线程—Runnable(开发常用)

1)步骤

a.创建声明Runnable接口的类

b.实现Rannable里面的run方法

// 自定义这个类实现Runnable接口,实现run()方法
public class MyRunnable implements Runnable {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			// getName()只有在Thread中有
			System.out.println(Thread.currentThread().getName() + ":" + i);
		}
	}

}

c.主线程中创建自定义类对象

d.创建Thread类对象,将第三步的对象作为参数进行传递来启动线程

public class ThreadDemo {
	public static void main(String[] args) {
		MyRunnable mr = new MyRunnable();

		// Thread t1 = new Thread(mr);
		// Thread t2 = new Thread(mr);
		
		// 第二种方法
		Thread t1 = new Thread(mr, "线程1");
		Thread t2 = new Thread(mr, "线程2");
		
		t1.start();
		t2.start();
	}
}

2)两种方法的区别


3)生命周期


F.synchornized同步机制

1)需求

某电影院目前正在上映贺岁大片(战狼2),共有100张票,而它有3个售票窗口售票,多线程实现

这不是最终版,最终版在后面

public class SellTicket implements Runnable {
	
	//定义门票的变量
	private int tickets = 100 ;
	@Override
	public void run() {
		//为了模拟电影院一一直有票
		while(true){
			if(tickets>0){
				System.out.println(Thread.currentThread().getName()+"正在出售第:"+(tickets--)
						+"张门票");
			}
		}
	}

}
自己尝试,会出现什么问题?自己尝试

比如:两个窗口卖的重复;总门票数超过100张(余票出现负数)

后面讲解怎么解决

2)线程安全的检测条件

通过刚才电影院卖票案例,线程不安全(线程安全的检测条件):

1)看我们当前的环境是否属于多线程程序:是

2)当前的多线程程序中是否有共享数据:是

3)是否多条语句对共享数据进行操作:是 tickets

如何让线程安全?

要实现线程安全,那么就必须改变第三个条件(前两者不能改变)

将对共享数据的操作的多条语句包装起来

java提供了一个同步机制:sychronized

3)同步机制的使用

sychronized(对象){

多条语句对共享数据进行操作;

}

对象是谁啊?不是随意的在sychronized里面进行创建对象

同步机制:每一个线程只能使用同一个对象(sychronized当作一个锁:使用的是同一把锁)

基本完成:最终版在下一条

public class MovieTickets implements Runnable {

	// 定义门票变量,不让外界看到
	private static int tickets = 100;

	// 在成员变量中创建对象
	Object obj = new Object();

	@Override
	public void run() {
		// 建立循环,票卖完终止
		while (tickets > 0) {
			// 同步代码块(不会出现两个窗口卖出同一张票)
			// 门(开和关)
			// t1线程抢占CPU的执行权,执行t1线程
			// t2线程抢占的CPU的执行权,执行t2线程,它要进去,发现门管着,所以等待着
			// 先创建对象
			synchronized (obj) {// t1线程进来,把门关了(单词:考点!)
				if (tickets > 0) {// 这行条件必须有(保证余票不会出现负数)
					System.out.println(Thread.currentThread().getName() + "正在出售电影《战狼Ⅱ》第" 
							+ (100 - (--tickets)) + "张票,余票" + tickets + "张!");
					try {
						// 每次购票,不会瞬间同步,要延时100ms(出现线程重复的)
						Thread.sleep(100);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}
测试类

public class TicketsDemo {
	public static void main(String[] args) {
		MovieTickets mt = new MovieTickets();
		
		Thread t1 = new Thread(mt, "窗口1");
		Thread t2 = new Thread(mt, "窗口2");
		Thread t3 = new Thread(mt, "窗口3");
		
		t1.start();
		t2.start();
		t3.start();
	}
}

继承Thread方法自己测试

同步代码块锁对象可以是什么样的对象?

//在成员变量中创建对象
	private Object obj = new Object() ;
	//创建Demo类对象不,并且外界不能访问
	private Demo d  = new Demo() ;

可以是任意的对象(重点)

5)同步方法

如果一个方法进来之后是同步代码块,那么该方法就可以定义为同步方法,他的锁对象是this

静态的同步方法的锁对象是:类名.class(java中的反射机制)

最终版代码(测试类同上一条)

public class MovieTickets implements Runnable {

	// 定义门票变量
	private static int tickets = 100;

	@Override
	public void run() {
		while (tickets > 0) {
			// 封装成方法
			getTickets();
		}
	}

	// private void getTickets() {
	// 	//该方法一进来就是一个同步代码块,一个方法可以是同步方法吗?
	// 	synchronized (d){//t1线程进来,把门关了(单词:考点!)
	// 		if(tickets>0){
	// 			try {
	// 				Thread.sleep(100);//t1线程睡了100毫秒
	// 			} catch (InterruptedException e) {
	// 				e.printStackTrace();
	// 			}
	// 		System.out.println(Thread.currentThread().getName()+"正在出售第:"+ (tickets--));
	// 		}
	// 	}
	// }

	// 设置同步方法
	private static synchronized void getTickets() {
		if (tickets > 0) {
			System.out.println(Thread.currentThread().getName() + "正在出售电影《战狼Ⅱ》第" 
					+ (100 - (--tickets)) + "张票,余票" + tickets + "张!");
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}

F.线程安全

1)和线程安全相关的类有哪些呢?

StringBuffer

Vector<T>:一般不用,使用:public synchronizedList(List<T> list)

Hashtable<K,V>

import java.util.ArrayList;
import java.util.Collections;
import java.util.Hashtable;
import java.util.List;
import java.util.Vector;

public class Demo {
	public static void main(String[] args) {
		StringBuffer sb = new StringBuffer();
		
		Vector<String> v = new Vector<String>();
		
		Hashtable<String, String> hs = new Hashtable<String, String>();
		
		List<String> list = new ArrayList<String>();
		List<String> list2 = Collections.synchronizedList(list);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值