一、实现多线程
1.简单了解多线程
答:是指从软件或者硬件上实现多个线程并发执行的技术
具有多线程能力的计算机因有硬件支持而能够在同一时间执行多个线程,提升性能
2.并发和并行
并行: 在同一时刻,有多个指令在多个cpu上同时执行
并发: 在同一时间段,有多个指令在单个CPU上交替执行
3.进程和线程
1、进程:是正在运行的程序
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位
动态性:进程的实质是程序的一次执行过程,进程是动态产生,动态消亡的
并发性:任何进程都可以同其他进程一起并发执行
2、线程:是进程中的单个顺序控制流,是一条执行路径
单线程:一个进程如果只有一条执行路径,则称为单线程程序
多线程:一个进程如果有多条执行路径,则称为多线程程序
4、实现多线程方法一:继承Thread类
1、方法介绍
方法名 | 说明 |
void run() | 在线程开启后,此方法将被调用执行 |
void start() | 使此线程开始执行,java虚拟机会调用run()方法 |
2、实现步骤
1.定义一个类继承Thread类
2.在类中重写run()方法
3.创建类的对象
4.启动线程
3、代码演示
public class Ticket extends Thread{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"卖出第"+i+"张票");
}
}
}
public class Test {
public static void main(String[] args) {
//创建实体对象
Ticket t1 = new Ticket();
//创建Thread类的对象,把实体对象作为构造方法的参数
Thread su = new Thread(t1, "苏沐橙");
Thread yan = new Thread(t1, "焰灵姬");
//此线程开始启动
su.start();
yan.start();
}
}
5、实现多线程方法二:实现Runnable接口
1、Thread构造方法
方法名 | 说明 |
Thread() | 分配一个新的Thread对象 |
2、实现步骤
-
定义一个类MyRunnable实现Runnable接口
-
在MyRunnable类中重写run()方法
-
创建MyRunnable类的对象
-
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
-
启动线程
3、代码演示
public class Test2 {
public static void main(String[] args) {
Ticket2 t = new Ticket2();
Thread thr = new Thread(t);
//修改名字
thr.setName("苏沐橙");
//开启线程
thr.start();
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"卖出"+i+"张票");
}
}
}
public class Ticket2 implements Runnable{
@Override
public void run() {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"卖出"+i+"张票");
}
}
}
6、实现多线程方式三:实现Callable接口
1、方法介绍
方法名 | 说明 |
call() | 计算结果,如果无法计算结果,则抛出一个异常 |
FutureTask() | 创建一个FutureTask,一但运行就执行给定的Callable |
get() | 如有必要,等待计算完成,然后获取其结果 |
2、实现步骤
1.定义一个类MyCallable实现Callable接口
2.在类中重写call()方法
3.创建类的对象
4.创建Future的实现类FutureTask对象,把类对象作为构造方法的参数
5.创建Thread类的对象,把FutureTask对象作为构造方法的参数
6.启动线程
7.在调用get方法,就可以获取线程结束之后的结果
3、代码演示
public class Test3 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Ticket3 t = new Ticket3();
//可以获取线程执行完毕之后的结果.也可以作为参数传递给Thread对象
FutureTask<String> task = new FutureTask<String>(t);
Thread tr = new Thread(task,"苏沐橙");
tr.start();
String s = task.get();
System.out.println(s);
}
}
public class Ticket3 implements Callable<String> {
@Override
public String call() throws Exception {
for (int i = 1; i <= 10; i++) {
System.out.println(Thread.currentThread().getName()+"你喜欢我吗?");
}
return "我喜欢你";
}
}
4、三种实现方式的对比
1、实现Runnable,Callable接口
1.好处: 扩展性强,实现该接口的同时还可以继承其他的类
2.缺点:编程相对复杂,不能直接使用Thread类中的方法
2、继承Thread类
1.好处:编程比较简单,可以直接使用Thread类中的方法
2.缺点:可以扩展性较差,不能再继承其他的类
7、设置和获取线程名称
1、方法介绍
方法名 | 说明 |
---|---|
void setName(String name) | 将此线程的名称更改为等于参数name |
String getName() | 返回此线程的名称 |
Thread currentThread() | 返回对当前正在执行的线程对象的引用 |
2、线程睡眠方法
方法名 | 说明 |
static void sleep(long millis) | 使当前正在执行的线程睡眠指定的毫秒数 |
二、线程同步
1、同步代码块解决数据安全问题
1、安全问题出现的条件
1.是多线程环境
2.有共享数据
3.有多条语句操作共享数据
2、可以用同步代码块方法来解决
2、同步代码块格式:
synchronized(任意对象){
多线程语句操作共享数据
}
1、 synchronized(任意对象):就相当于给代码加了锁,任意对象可以看成是一把锁
2、同步的好处和弊端
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
2、代码演示
public class Ticket4 implements Runnable {
private Object obj = new Object();
private int tickets = 20;
@Override
public void run() {
while (true){
synchronized (obj){
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--;
}
}
}
}
}
public class Test4 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Ticket4 t1 = new Ticket4();
Thread tr = new Thread(t1, "苏沐橙");
Thread tr2 = new Thread(t1, "焰灵姬");
tr.start();
tr2.start();
}
}
3、同步方法解决数据安全问题
1、同步方法的格式:
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法锁对象是:this
2、静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是: 类名.class
3、代码演示:
public class Ticket5 implements Runnable{
private static int ticket = 20;
@Override
public void run() {
while (true){
if ("苏沐橙".equals(Thread.currentThread().getName())){
//同步方法
boolean b = synchronizedMode();
if (b){
break;
}
}
if ("焰灵姬".equals(Thread.currentThread().getName())) {
synchronized (Ticket5.class){
if (ticket == 0){
break;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
}
}
}
}
private static synchronized boolean synchronizedMode(){
if (ticket==0){
return true;
}else {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
return false;
}
}
}
public class Test5 {
public static void main(String[] args) {
Ticket5 ti = new Ticket5();
Thread tr = new Thread(ti, "苏沐橙");
Thread tr2 = new Thread(ti, "焰灵姬");
tr.start();
tr2.start();
}
}
三、lock锁
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化
1、ReentrantLock构造方法
方法名 | 说明 |
ReentrantLock() | 创建一个ReentrantLock的实例 |
2、加锁解锁方法
方法名 | 说明 |
void lock() | 获得锁 |
void unlock() | 释放锁 |
3、代码演示
public class Ticket5 implements Runnable{
private static int ticket = 20;
private static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
if ("苏沐橙".equals(Thread.currentThread().getName())){
//同步方法
boolean b = synchronizedMode();
if (b){
break;
}
}
if ("焰灵姬".equals(Thread.currentThread().getName())) {
synchronized (Ticket5.class){
if (ticket == 0){
break;
}else {
try {
lock.lock();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
}
}
}
}
}
private static synchronized boolean synchronizedMode(){
if (ticket==0){
return true;
}else {
try {
lock.lock();
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
ticket--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + ticket + "张票");
return false;
}
}
}
public class Test5 {
public static void main(String[] args) {
Ticket5 ti = new Ticket5();
Thread tr = new Thread(ti, "苏沐橙");
Thread tr2 = new Thread(ti, "焰灵姬");
tr.start();
tr2.start();
}
}