线程:
创建线程:
package Thread;
/**
* 创建多线程的第一种方式:创建Thread类的子类
* java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.在Thread类的子类中重写Thread类中的run方法,设置线程任务
* 3.创建Thread类的子类对象
* 4.调用Thread类中的方法start方法,开启新的线程
* void start()使该线程开始执行,虚拟机调用该线程的run方法
* 结果是两个线程并发地执行,当前线程和另外一个线程
* 多次调用一个线程是非法的,特别是当线程已经结束,不能再重新启动
*/
public class ThreadDemo1 extends Thread {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("run:"+i);
}
}
}
package Thread;
public class ThreadMainDemo {
public static void main(String[] args) {
ThreadDemo1 mt = new ThreadDemo1();
mt.start();
for (int i = 0; i < 20; i++)
System.out.println("main:"+i);
}
}
Thread类的常用方法:
package Thread;
/**
* 创建多线程的第一种方式:创建Thread类的子类
* java.lang.Thread类:是描述线程的类,我们想要实现多线程程序,就必须继承Thread类
* 实现步骤:
* 1.创建一个Thread类的子类
* 2.在Thread类的子类中重写Thread类中的run方法,设置线程任务
* 3.创建Thread类的子类对象
* 4.调用Thread类中的方法start方法,开启新的线程
* void start()使该线程开始执行,虚拟机调用该线程的run方法
* 结果是两个线程并发地执行,当前线程和另外一个线程
* 多次调用一个线程是非法的,特别是当线程已经结束,不能再重新启动
*/
public class ThreadDemo1 extends Thread {
@Override
public void run() {
/*for (int i = 0; i < 20; i++) {
System.out.println("run:"+i);}*/
/*String name = getName();
System.out.println(name);*/
Thread t = Thread.currentThread();
//System.out.println(t);
String name = t.getName();
System.out.println(name);
}
}
package Thread;
/**
* Thread类的常用方法:
* 构造方法:
* public Thread():分配一个新的线程对象
* public Thread(String name):分配一个指定名字的新的线程对象
* public Thread(Runnable target):分配一个带有指定目标的线程对象
* public Thread(Runnable target,String name):分配一个有目标有名字的线程对象
*
* 常用方法:
* public String getName():获取当前线程名称
* public void start():导出此线程开始执行;Java虚拟机调用此线程的run方法
* public void run():此线程要执行的任务在此处定义代码
* public static void sleep(long millis):使当前正在执行的线程以指定的毫秒数暂停
* public static Thread currentThread():返回对当前正在执行的线程对象的引用
*/
public class ThreadDemo2 {
public static void main(String[] args) {
ThreadDemo1 mt = new ThreadDemo1();
mt.start();
new ThreadDemo1().start();
new ThreadDemo1().start();
}
}
创建线程的第二种方式:实现Runnable接口
package Thread;
public class RunnableImpl implements Runnable {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
package Thread;
/**
* 实现步骤:
* 1.创建一个Runnable接口的实现类
* 2.在类中重写Runnable接口的run方法,设置线程任务
* 3.创建一个Runnable接口的实现类对象
* 4.创建一个Thread类对象,构造方法中传递Runnable接口的实现类
* 5.调用Thread类中的start方法,开启新的线程执行run方法
*
* 实现Runnable接口创建多线程的好处:
* 1.避免了单继承的局限性
* 2.增强了程序的扩展性,降低了程序的耦合性(把设置线程任务和开启线程进行了分离)
*
*/
public class ThreadMainDemo {
public static void main(String[] args) {
/*ThreadDemo1 mt = new ThreadDemo1();
mt.start();
for (int i = 0; i < 20; i++)
System.out.println("main:"+i);*/
RunnableImpl run = new RunnableImpl();
Thread t = new Thread(run);
t.start();
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
匿名内部类方式实现线程的创建:
package Thread;
/**
* 匿名内部类的作用:简化代码
* 把子类继承父类,重写父类的方法,创建子类对象合一步完成
* 把实现类接口,重写接口中的方法,创建实现类对象合成一步
* 匿名内部类的产物:子类/实现类对象,而这个对象没有名字
*/
public class ThreadMainDemo2 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"Thread");
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+"Runnable");
}
}
}).start();
}
}
线程安全问题:
解决方法:
1.同步代码块
package Thread;
/**
* 解决线程问题的第一种方案:使用同步代码块
* 格式:synchronized(锁对象){
* 访问了共享数据的代码
* }
* 注意:
* 1.同步代码块中的锁对象,可以是任意对象
* 2.必须保证多个线程使用的锁对象是同一个
* 3.锁对象作用:
* 把同步代码块锁住,只让一个线程在同步代码块中执行
*/
public class RunnableImpl2 implements Runnable {
private int ticket = 100;
Object obj = new Object();
@Override
public void run() {
while(true) {
synchronized (obj) {
if (ticket > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "-->正在买第" + ticket + "张票");
ticket--;
}
}
}
}
}
2.同步方法
public synchronized 返回值类型 方法名(参数列表){
//同步代码
//锁对象是this
}
3.锁机制
线程状态:
线程间通信:
package Thread;
/**
* 线程之间的通信:
* 创建一个顾客线程:告知老板要的包子种类和数量,调用wait方法,放弃cpu的执行,进入到waiting状态(无限等待)
* 创建一个老板线程:花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子
*
* 注意:顾客和老板线程必须使用代码块包裹起来,包子等待和唤醒只能有一个执行
* 同步使用的锁对象必须保证唯一
* 只有锁对象才能调用wait和notify方法
*
* Object类中的方法:
* void wait():在其他线程调用此对象的notify()方法或notifyAll()方法前,导致当前线程等待
* void notify():唤醒在对象监视器上等待的单个线程,会继续执行wait方法之后的代码
*/
public class producterConsumerDemo1 {
public static void main(String[] args) {
Object obj = new Object();
//创建一个顾客线程
new Thread(){
@Override
public void run() {
while(true){
//保证等待和唤醒线程只能有一个执行,需要使用同步技术
synchronized (obj){
System.out.println("告知老板要的包子种类和数量");
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//唤醒之后执行的代码
System.out.println("包子已经做好了,开吃");
}
}
}
}.start();
//创建一个老板线程
new Thread() {
@Override
public void run() {
while(true){
try {
Thread.sleep(5000);//花5秒做包子
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (obj){
System.out.println("老板5秒钟之后做好包子,告知顾客,可以吃包子了");
obj.notify();
}
}
}
}.start();
}
}
生产者与消费者模型:
代码实现:
package Thread;
/**
* 设置包子的属性:
* 皮、馅、包子的状态
*/
public class BaoZi {
String pi;
String xian;
boolean flag = false;
}
package Thread;
/**
* 生产者(包子铺):是一个线程类,可以继承Thread
* 设置线程任务:生产包子
* 对包子的状态进行判断:true(有包子) 包子铺调用wait方法计入等待状态
* false:没有包子 包子铺生产包子 增加一些趣味:交替生产两种包子
* 有两种状态(i%2==0),包子铺生产好了包子,修改包子的状态为true,唤醒吃货线程,让吃货吃包子
*
* 注意:包子铺线程和包子线程是互斥关系,必须使用同步技术保证两个线程只有一个在执行
* 锁对象必须保证唯一,可以使用包子对象作为锁对象
* 包子铺和吃货的类就需要把包子对象作为参数传递进来:
* 1.需要在成员位置创建一个包子变量
* 2.使用带参数构造方法,为这个包子变量赋值
*/
public class BaoZiPu extends Thread {
//1.需要在成员位置创建一个包子变量
private BaoZi bz;
//2.使用带参数构造方法,为这个包子变量赋值
public BaoZiPu(BaoZi bz) {
this.bz = bz;
}
//设置线程任务,生产包子
@Override
public void run() {
int count = 0;
while(true) {
//必须使用同步技术保证两个线程只有一个在执行
synchronized (bz) {
//对包子的状态进行判断
if (bz.flag == true) {
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒之后执行,包子铺生产包子
//增加一些趣味性:交替生产两种包子
if (count % 2 == 0) {
//生产薄皮,三鲜馅包子
bz.pi = "薄皮";
bz.xian = "三鲜馅";
} else {
//生产冰皮、牛肉大葱馅
bz.pi = "冰皮";
bz.xian = "三鲜馅";
}
count++;
System.out.println("包子铺正在生产:" + bz.pi + bz.xian + "包子");
//生产包子需要3秒钟
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bz.flag = true;
bz.notify();
System.out.println("包子铺已经好了" + bz.pi + bz.xian + "包子,吃货可以开吃了");
}
}
}
}
package Thread;
/**
* 消费者(吃货类):是一个线程类,可以继承Thread
* 设置线程任务(run):吃包子
* 对包子的状态进行判断:
* false:没有包子,吃货调用wait方法进入等待
* true:
* 吃货吃包子,吃货吃完包子,修改包子装态为false,唤醒包子铺线程,生产包子
*/
public class ChiHho extends Thread {
//1.需要在成员位置创建一个包子变量
private BaoZi bz;
//2.使用带参数构造方法,为这个包子变量赋值
public ChiHho(BaoZi bz) {
this.bz = bz;
}
@Override
public void run() {
while (true){
//必须使用同步技术保证两个线程只有一个在执行
synchronized (bz){
//对包子的状态进行判断
if(bz.flag==false){
try {
bz.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//被唤醒之后的代码,吃包子
System.out.println("吃货正在吃:" + bz.pi + bz.xian + "包子");
//吃货吃完包子,修改包子的状态
bz.flag = false;
//唤醒包子铺做包子
bz.notify();
System.out.println("吃货已经把:" + bz.pi + bz.xian + "的包子吃完了,包子铺开始生产包子");
System.out.println("==================================================");
}
}
}
}
package Thread;
public class ThreadMainDemo4 {
public static void main(String[] args) {
BaoZi bz = new BaoZi();
new BaoZiPu(bz).start();
new ChiHho(bz).start();
}
}
线程池:
package Thread;
public class RunnableImplDemo implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"创建了一个新的线程");
}
}
package Thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Executors类中的静态方法:
* static ExecutorService newFixedThreadPool(int nThreads):创建一个可用固定线程数的线程池
* 返回值:ExecutorService接口,返回的是ExecutorService接口的实现类,
* 我们可以使用ExecutorService接口接收(面向接口编程)
*
* 用来从线程池中获取线程,调用start方法,执行线程任务
* submit(Runnable task):提交一个Runnable任务用于执行
* 关闭销毁线程池的方法
* void shutdown();
*
* 使用步骤:
* 1.使用线程池的工厂类Executors里边提供的newFixedThreadPool生产一个指定数量的线程池
* 2.创建一个类,实现Runnable接口,重写run方法,设置线程任务
* 3.调用ExecutorService中的方法submit,传递线程任务,开启线程,执行run方法
* 4.调用ExecutorService中的方法shutdown销毁线程池
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(new RunnableImplDemo());
es.submit(new RunnableImplDemo());
es.submit(new RunnableImplDemo());
es.shutdown();
}
}
lambda表达式:
1.无参无返回值
package Thread;
/**
* 定义一个Cook接口,内含唯一的抽象方法makeFood
*/
public interface Cook {
//定义无参数无返回值的方法makeFood
public abstract void makeFood();
}
package Thread;
/**
* Lambda表达式的格式(三个一):
* 1.一些参数
* 2.一个箭头
* 3.一段代码
*
* (参数列表)->{一些重写方法的代码}
* 解释说明格式:
* ():接口中抽象方法的参数列表,没有参数,就空着,有参数就写出参数,多个参数用逗号分隔
* ->:传递的意思,把参数传递给方法体{}
* {}:重写接口的抽象方法的方法体
*/
public class LambdaDemo {
//使用Lambda的标准格式调用invokeCook方法,打印输出“吃饭啦!”字样
public static void main(String[] args) {
invokeCook(new Cook() {
@Override
public void makeFood() {
System.out.println("吃饭啦!");
}
});
System.out.println("==================");
//使用Lambda表达式
invokeCook(()->{
System.out.println("吃饭啦!");
});
}
//定义一个方法,参数传递Cook接口,方法内部调用Cook接口中的方法makeFood
public static void invokeCook(Cook cook){
cook.makeFood();
}
}
有参数有返回值:
package Thread;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package Thread;
import java.util.Arrays;
import java.util.Comparator;
public class PersonMainDemo {
public static void main(String[] args) {
Person[] arr = {
new Person("柳岩",38),
new Person("迪丽热巴",18),
new Person("古力娜扎",19)
};
/*Arrays.sort(arr, new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()- o2.getAge();
}
});*/
//使用Lanbda表达式
Arrays.sort(arr, (Person o1, Person o2)->{
return o1.getAge()- o2.getAge();
}
);
for (Person person : arr) {
System.out.println(person);
}
}
}
Lambda表达式的省略:
/**
* Lambda表达式:是可推导,可以省略的
* 凡是依据上下文推到出来的内容,都可以省略书写
* 可以省略的内容:
* 1.(参数列表):括号中的数据类型可以省略不写
* 2.(参数列表):括号中的参数只有一个,那么类型和()都可以省略
* 3.{一些代码}:如果{}中的代码只有一行无论是否有返回值,都可以省略({},return,分号)
*/