补充:
并发:两个或多个事件在同一时间段发生—-交替执行—效率低
并行:两个或多个事件在同时发生—————同时执行—效率高
多线程语法
多线程是java中的一个并发的概念:
- 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配)
- 1.在java程序中也是支持的
- 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程—主线程
- 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
- 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
- 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
- 对象,就代表一个某种状态的线程
- java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread…
- 进程:表示一个个的正在运行的程序
- 线程:表示一个个正在运行的程序中的子进程(线程)
- 包含关系:一个进程包含多个线程 ```java package com.xincheng;
/**
- @author Lynn
- @create 2020-12-14-14:46 */
import java.util.Date;
/**
- 多线程是java中的一个并发的概念:
- 在计算机的运行中,所有的进程都是同时运行的(CPU会同时调用,之后会随机的获取分配) *
- 1.在java程序中也是支持的
- 2.当java执行main方法的时候,其实就是执行一个名字叫做main的线程
- 可以在main线程执行的时候,开启多个其他的线程A,B,C等等
- 多个线程A,B,C等等都是同时执行的,同时等待CPU的调用,相互抢夺CPU调用自己的时间片段
- 3.Thread类是java中的专门处理线程的api,是java.lang包下的常用类,每一个Thread类的
- 对象,就代表一个某种状态的线程 *
- java.lang包下常用的类(不需要导包):String,System,八大基本类型,Thread… *
- 进程:表示一个个的正在运行的程序
- 线程:表示一个个正在运行的程序中的子进程(线程)
包含关系:一个进程包含多个线程 */ public class ThreadDemo { //main是一个主线程 public static void main(String[] args) {
//是主线程调用的一个其他线程
Date d=new Date();
System.out.println(d);
//是主线程调用的一个其他线程
System.out.println(System.currentTimeMillis());
say();
}
public static void say(){
System.out.println("hello");
案例1
```java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:20 */
/**
- 自定义线程类 *
- 定义一个Thread的子类 *
创建线程的第一种方式:继承Thread类 */ public class MyThread extends Thread {
//重写run方法—相关的逻辑写进去 @Override public void run(){
for (int i = 0; i <50 ; i++) {
/**
* this表示当前被调用的的这个线程
* 获取现成的名字是java干的事情,我们不管
*/
System.out.println(this.getName()+":"+i);
}
} }
/**
- @author Lynn
- @create 2020-12-14-15:13 */
/**
- java中使用Thread类来创建线程: 第一种方式: *
- 步骤:
- 1.指定执行现成的目标:定义一个Thread子类,重写run方法,将相关的逻辑实现
- public void run():将需要的逻辑写在里面,该方法相当于线程中的main方法
- 2.创建自定义线程的子类对象
- 3.开启线程操作—触发
- public void start():只要调用了这个方法,线程才会启动,开始执行 *
- Thread中的相关方法:
- public final String getName():获取线程的名字
- public final void setName():设置线程的名字
- public static Thread currentThread():获取当前的线程对象 *
线程的运行默认是不可控制的,是CPU随机调用的,我们只能看到运行的结果 / public class ThreadDemo { //主线程 public static void main(String[] args) { //创建线程对象—用于开启线程 Thread t1=new MyThread(); Thread t2=new MyThread();
//开启线程—直接运行 t1.start(); t2.start();
System.out.println(“———————————————————“);
//返回一个main线程—其实就是main方法的线程 Thread mainThread=Thread.currentThread(); System.out.println(mainThread);//Thread[main,5,main]
for (int i = 0; i <50 ; i++) {
System.out.println(mainThread.getName()+":"+i);
案例2
```java package com.xincheng_01;
/**
- @author Lynn
- @create 2020-12-14-15:36 */
/**
- 创建线程的第二种方式:
- 实现Runnable接口,lang包下的: *
- 1.实现接口,并且要实现run方法
- 2.在run方法中,添加逻辑代码
- 3.通过start启动线程
*/
public class MyRunable implements Runnable{
@Override
public void run(){
} }//返回当前线程
Thread thisThread=Thread.currentThread();
for (int i = 0; i <20 ; i++) {
System.out.println(thisThread.getName()+":"+i);
}
/**
- @author Lynn
- @create 2020-12-14-15:13 */
/**
- java中使用Thread类来创建线程: *
- 第二种方式: *
- 步骤:
- 1.指定线程执行的目标,定义Runnable接口,并且实现接口,重写run方法,制定目标逻辑
- 2.通过指定线程执行目标的构造方法创建线程对象
- public Thread(Runnable target)
- 创建线程并且执行目标对象
- 通过线程执行目标对象创建线程对象
- 3.开启线程操作(start) *
-
//创建线程执行目标
MyRunable mr=new MyRunable();//线程对象--任务
//通过指定线程执行目标的构造函数来创建线程
Thread thread=new Thread(mr);//线程的名称
thread.setName("jack");
Thread thread2=new Thread(mr);//线程的名称
thread2.setName("rose");
//开启线程
thread.start();
thread2.start();
演示匿名内部类创建线程—实际开发中使用
- 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
- 2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 ```java package com.xincheng_02;
/**
- @author Lynn
- @create 2020-12-14-16:06 */
/**
- 演示匿名内部类创建线程—实际开发中使用 *
- 1.使用匿名内部类创建线程的原理和普通类创建线程的原理完全一样
2.区别在于利用匿名内部类创建线程的写法更加简洁,使用非常多 */ public class AnonysThreadDemo { public static void main(String[] args) {
//方式一:使用匿名内部类创建线程的子类对象
Thread t1 = new Thread() {
@Override
public void run() {
System.out.println("还记得去年陪你过圣诞节的人吗");
}
};
t1.start();
System.out.println("----------------------------------------------------------------");
//使用匿名内部类,创建线程的匿名子类对象,并且启动线程
new Thread() {
@Override
public void run() {
System.out.println("还记得上一次陪你去看演唱会的人吗");
}
}.start();
//方式二:使用匿名内部类的方式,创建执行目标类的对象
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("她来听我的演唱会");
}
};
//传入到构造函数
Thread t2 = new Thread(r);
t2.start();
System.out.println("-----------------------------");
//组合写法-精简写法
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("往事只能回味");
}
}).start();
//组合写法--普通写法
Thread t3=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("往事只能回味");
}
});
t3.start();
练习(售卖车票)
```java package com.xincheng_03;
/**
- @author Lynn
- @create 2020-12-14-16:29 */
import javax.crypto.interfaces.PBEKey;
/**
- 这是一个任务
使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;
//卖票的逻辑 @Override public void run() {
//模拟车站不停地卖票--卖光为止
while (true){
//有票就卖
if (number>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
}else {
//没有票了
break;
}
}
} }
/**
- @author Lynn
- @create 2020-12-14-16:25 */
/**
- 多线程模拟售卖火车票 *
- 要求使用第二种方式实现,方便数据共享 *
- 步骤:
- 1.定义卖票的线程执行目标:(任务)
- 在成员变量定义为票数100张,卖掉一张,改数字就减去1,一直到0为止
- 重写run方法,完成案例
- 2.创建买票的线程且执行该目标的对象
- 3.使用该卖票子线程执行对象创建多个线程任务
4.开启多个线程 */ public class TicketDemo { public static void main(String[] args) {
//创建车票的线程执行目标对象
Ticket ticket=new Ticket();
/*//使用该卖票的线程执行目标的对象创建多个任务
Thread t1 = new Thread(ticket,"jack.Ma");
Thread t2 = new Thread(ticket,"Huateng.Ma");
Thread t3 = new Thread(ticket,"Yanhong.Li");
t1.start();
t2.start();
t3.start();*/
new Thread(ticket,"jack.Ma").start();
new Thread(ticket,"Huateng.Ma").start();
new Thread(ticket,"Yanhong.Li").start();
回顾
线程池与线程生命周期
线程生命周期图示
线程池
```java package com.igeek_01;
/**
- @author Lynn
- @create 2020-12-14-16:29 */
/**
- 这是一个任务
使用第二种方式—使用Runnable接口 */ public class Ticket implements Runnable{ //定义车票数 private int number=100;
//卖票的逻辑 @Override public void run() {
//模拟车站不停地卖票--卖光为止
while (true){
//有票就卖
if (number>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
}else {
//没有票了
break;
}
}
} }
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;
/**
- @author Lynn
@create 2020-12-15-9:08 */ public class ThreadPoolDemo { public static void main(String[] args) {
//实例化线程池
/**
* ExecutorService是一个接口,专门处理线程池的
* Executors是一个工具类,提供了很多的方法服务于线程
* newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程
*/
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//创建线程的执行目标
Ticket ticket=new Ticket();
//向线程池中提交任务目标
threadPool.submit(ticket);
threadPool.submit(ticket);
threadPool.submit(ticket);
threadPool.submit(ticket);
threadPool.submit(ticket);
//在适当的时候也可以关闭线程池,一般不关闭
//意味着线程池中的所有的线程都销毁
// threadPool.shutdown(); } } ```
Callable接口
package com.igeek_02;
/**
* @author Lynn
* @create 2020-12-14-16:29
*/
/**
* 这是一个任务
* 使用第二种方式--使用Runnable接口
*/
public class Ticket implements Runnable{
//定义车票数
private int number=100;
//卖票的逻辑
@Override
public void run() {
//模拟车站不停地卖票--卖光为止
while (true){
//有票就卖
if (number>0){
System.out.println(Thread.currentThread().getName()+"正在销售第"+(number--)+"张票");
}else {
//没有票了
break;
}
}
}
}
package com.igeek_02;
import java.util.concurrent.Callable;
/**
* @author Lynn
* @create 2020-12-15-10:03
*/
public class MyCallable implements Callable<String> {
/**
* 定义一个线程执行的目标逻辑
* @return
* @throws Exception
*/
@Override
public String call() throws Exception {
return "我是线程任务的返回值结果";
}
}
package com.igeek_02;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* @author Lynn
* @create 2020-12-15-9:08
*/
public class ThreadPoolDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//实例化线程池
/**
* ExecutorService是一个接口,专门处理线程池的
* Executors是一个工具类,提供了很多的方法服务于线程
* newFixedThreadPool(3)该方法传入的参数表示在线程启动的时候初始化几个线程
*/
ExecutorService threadPool= Executors.newFixedThreadPool(3);
//创建线程的执行目标
Ticket ticket=new Ticket();
//利用Callable接口返回Future,(没有返回值的run方法,返回的是null,没有意义)
Future<?> ticketFuture = threadPool.submit(ticket);
System.out.println(ticketFuture.get());
//创建带有返回值的线程执行目标
MyCallable callable=new MyCallable();
//向线程中提交任务,并返回线程目标的执行结果
Future<String> future1= threadPool.submit(callable);
Future<String> future2= threadPool.submit(callable);
Future<String> future3= threadPool.submit(callable);
//从执行结果中返回call的具体值
String result1=future1.get();
String result2=future2.get();
String result3=future3.get();
System.out.println(result1);
System.out.println(result2);
System.out.println(result3);
//在适当的时候也可以关闭线程池,一般不关闭
//意味着线程池中的所有的线程都销毁
// threadPool.shutdown();
}
}
线程安全问题
线程安全问题的解决方案—-加锁
package com.igeek_04;
/**
* @author Lynn
* @create 2020-12-15-10:32
*/
/**
* 使用第二种方式创建线程,原因是执行线程目标对象共享
*
* 定义一个卖票的线程执行目标对象--加锁
*/
public class Ticket implements Runnable{
//票数
private int number=100;
//声明成员变量为定义锁对象
private Object lock=new Object();
@Override
public void run() {
while (true){
synchronized (lock){
//线程暂停
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就卖
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}else {
//没票了
break;
}
}
}
}
}
package com.igeek_04;
/**
* @author Lynn
* @create 2020-12-15-10:44
*/
/**
* 线程安全问题的解决方案 :
* 同步代码块
* synchronized(锁对象){
* 可能出现线程安全问题的代码
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//实例化目标
Ticket ticket=new Ticket();
//使用该卖票的目标创建多个线程
Thread thread1=new Thread(ticket,"Jack");
Thread thread2=new Thread(ticket,"Rose");
Thread thread3=new Thread(ticket,"Tom");
//启动多个线程
thread1.start();
thread2.start();
thread3.start();
}
}
优化
package com.igeek_05;
/**
* @author Lynn
* @create 2020-12-15-10:32
*/
/**
* 使用第二种方式创建线程,原因是执行线程目标对象共享
*
* 定义一个卖票的线程执行目标对象--加锁
*/
public class Ticket implements Runnable{
//票数
private int number=100;
//声明成员变量为定义锁对象
private Object lock=new Object();
//创建一个标记,让不同的人来执行不同的代码块
private int x=0;
@Override
public void run() {
while (true){
//进行取余的运算,模拟卖票的人
if(x%2==0){
//代码块
//将完整的一套动作使用synchronized包裹
synchronized (lock){
//线程暂停
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就卖
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}else {
//没票了
break;
}
}
}else {
//代码块
//将完整的一套动作使用synchronized包裹
synchronized (lock){
//线程暂停
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就卖
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}else {
//没票了
break;
}
}
}
x++;
}
}
}
package com.igeek_05;
/**
* @author Lynn
* @create 2020-12-15-10:44
*/
/**
* 线程安全问题的解决方案 :
* 同步代码块
* synchronized(锁对象){
* 可能出现线程安全问题的代码
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//实例化目标
Ticket ticket=new Ticket();
//使用该卖票的目标创建多个线程
Thread thread1=new Thread(ticket,"Jack");
Thread thread2=new Thread(ticket,"Rose");
Thread thread3=new Thread(ticket,"Tom");
//启动多个线程
thread1.start();
thread2.start();
thread3.start();
}
}
同步方法(用synchronized修饰)有静态和非静态的
非静态方法
package com.igeek_06;
/**
* @author Lynn
* @create 2020-12-15-10:32
*/
/**
* 使用第二种方式创建线程,原因是执行线程目标对象共享
*
* 定义一个卖票的线程执行目标对象--加锁
*
* 演示synchronized可以用来修饰方法,叫做同步方法
*
* 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,
* 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象
*/
public class Ticket implements Runnable{
//票数
private int number=100;
//声明成员变量为定义锁对象
private Object lock=new Object();
//创建一个标记,让不同的人来执行不同的代码块
private int x=0;
@Override
public void run() {
while (true){
//进行取余的运算,模拟卖票的人
if(x%2==0){
//代码块
//将完整的一套动作使用synchronized包裹
//把当前这个被调用的对象锁住
synchronized (this){
//线程暂停
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就卖
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}else {
this.sell();
}
}
}
//当票数=0,就表示没票了
if(number<0){
break;
}
x++;
}
}
public synchronized void sell(){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}
}
}
package com.igeek_06;
/**
* @author Lynn
* @create 2020-12-15-10:44
*/
import com.igeek_05.Ticket;
/**
* 线程安全问题的解决方案 :
* 同步代码块
* synchronized(锁对象){
* 可能出现线程安全问题的代码
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//实例化目标
com.igeek_05.Ticket ticket=new Ticket();
//使用该卖票的目标创建多个线程
Thread thread1=new Thread(ticket,"Jack");
Thread thread2=new Thread(ticket,"Rose");
Thread thread3=new Thread(ticket,"Tom");
//启动多个线程
thread1.start();
thread2.start();
thread3.start();
}
}
静态方法
package com.igeek_07;
/**
* @author Lynn
* @create 2020-12-15-10:32
*/
/**
* 使用第二种方式创建线程,原因是执行线程目标对象共享
*
* 定义一个卖票的线程执行目标对象--加锁
*
* 演示synchronized可以用来修饰方法,叫做同步方法
* 同时也支持修饰静态方法!!!
*
* 当前案例中,使用对象锁对当前的这个Ticket加锁,目的是this是当前正在被调用的这个Ticket对象,
* 不管是synchronized代码块中还是封装的一个synchronized方法,都可以保证使用的是同一个锁对象
*/
public class Ticket implements Runnable{
//票数--静态方法不能调用非静态资源
private static int number=100;
//声明成员变量为定义锁对象
private Object lock=new Object();
//创建一个标记,让不同的人来执行不同的代码块
private int x=0;
@Override
public void run() {
while (true){
//进行取余的运算,模拟卖票的人
if(x%2==0){
//代码块
//将完整的一套动作使用synchronized包裹
//类名直接调用静态方法
synchronized (Ticket.class){
//线程暂停
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
//有票就卖
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}/*else {
//没票了
break;
}*/
}
}else {
// this.sell();
Ticket.sell();
}
//当票数=0,就表示没票了
if(number<0){
break;
}
x++;
}
}
public static synchronized void sell(){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (number>0){
String threadName=Thread.currentThread().getName();
System.out.println(threadName+"正在销售第"+number+"张票");
number--;
}
}
}
package com.igeek_07;
/**
* @author Lynn
* @create 2020-12-15-10:44
*/
import com.igeek_05.Ticket;
/**
* 线程安全问题的解决方案 :
* 同步代码块
* synchronized(锁对象){
* 可能出现线程安全问题的代码
* }
*/
public class ThreadDemo {
public static void main(String[] args) {
//实例化目标
Ticket ticket=new Ticket();
//使用该卖票的目标创建多个线程
Thread thread1=new Thread(ticket,"Jack");
Thread thread2=new Thread(ticket,"Rose");
Thread thread3=new Thread(ticket,"Tom");
//启动多个线程
thread1.start();
thread2.start();
thread3.start();
}
}
回顾
演示等待和唤醒机制
案例
package com.igeek_08;
/**
* @author Lynn
* @create 2020-12-15-15:18
*/
/**
* 资源类(实体类)
*
* 定义共享的数据,其中成员:name,sex
*/
public class Person {
//不使用private是为了便于后面的操作
String name;
String sex;
//状态标记
/**
* flag:
* true:有数据,生产者等待被消费
* false:没有数据,生产者生产,消费者等待
*/
boolean flag;
public Person(){
super();
}
}
package com.igeek_08;
/**
* @author Lynn
* @create 2020-12-15-15:20
*/
/**
* 生产者
*
* 生产者的执行目标:
* 反复为Person对象赋值:Jack 男 与 Rose 女
*/
public class PutIn implements Runnable{
//实例化对象--声明
private Person person;
//创建构造方法用于接收外部传入的共享数据Person
public PutIn(Person person){
this.person=person;
}
@Override
public void run() {
//使用循环达到反复赋值的目的
//如果是奇数,就生成Jack 男,如果是偶数,救生成Rose 女
int i=0;
while (true){
synchronized (person){
//判断状态flag是true还是false
while (person.flag){
//有数据,就等待
try {
person.wait();//线程阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//如果没有数据或者是刚被唤醒
//判断生产的内容
if (i%2==1){//男
person.name="Jack";
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
person.sex="男";
}else {//女
person.name="Rose";
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
person.sex="女";
}
System.out.println(person);
System.out.println("生产了"+person.name+"-"+person.sex);
//切换状态为true
person.flag=true;
//如果有消费者等待,就唤醒消费者,如果没有消费者等待,就不需要唤醒,相当于没有执行
person.notify();
}
i++;
}
}
}
package com.igeek_08;
/**
* @author Lynn
* @create 2020-12-15-15:29
*/
/**
* 消费者目标对象
*
* 反复的获取Person对象,并且打印
*/
public class GetOut implements Runnable{
//实例化共享资源
private Person person;
public GetOut(Person person) {
this.person=person;
}
@Override
public void run() {
//使用循环获取
while (true) {
//利用共享对象作为锁
synchronized (person){
//获取判断状态--flag
while (!person.flag){
//没有数据就等待生产
try {
person.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//有数据或者是刚刚被唤醒
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//进行获取操作
String name= person.name;
String sex= person.sex;
System.out.println(person);
System.out.println("消费了:"+name+"-"+sex);
//切换状态为false
person.flag=false;
//唤醒生产者,如果有就唤醒,如果没有就不需要唤醒,相当于没有执行
person.notify();
}
}
}
}
package com.igeek_08;
/**
* @author Lynn
* @create 2020-12-15-15:15
*/
/**
* 演示等待和唤醒机制
*/
public class WaitNotifyDemo {
public static void main(String[] args) {
//实例化对象
Person person=new Person();
//创建生产者的执行目标
PutIn putIn=new PutIn(person);
//创建消费者的执行目标
GetOut getOut=new GetOut(person);
//开启生产者线程
Thread putInThread=new Thread(putIn);
putInThread.start();
//开启消费者线程
Thread getOutThread=new Thread(getOut);
getOutThread.start();
}
}