? a.子类继承Thread类具备多线程能力
? b.启动线程:子类对象.start()
? c.不建议使用:避免OOP单继承局限性
package com.tang;
/*
* 多线程学习的第一个demo
* */
public class Demoted01 extends Thread {
public void run(){
// run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
// main线程,主线程
// 创建一个线程对象
Demoted01 demoted01 = new Demoted01();
// 调用start() 方法开启线程
demoted01.start();
for (int j = 0; j < 30; j++) {
System.out.println("我在学习多线程---"+j);
}
}
}
? a.实现接口Runnable具有多线程能力
? b.启动线程:传入目标对象+Thread对象.start()
? c.推荐使用:避免单继承局限性,灵活方便,方便同一个对象被多个线程使用
实现步骤:
? 1.创建一个Runnable接口的实现类。
? 2.在实现类中重写Runnable接口的run方法,设置线程任务。
? 3.创建一个Runnable接口的实现类对象
? 4.创建Thread类对象,构造方法中传递Runnable接口的实现类对象。
? 5,。调用Thread类中的start方法,开启新的线程执行方法。
package com.tang;
/*
*1.定义MyRunnable类实现Runnable接口
*2.实现run方法,编写线程执行体
*3.创建线程对象,调用start()方法启动线程
*
*
* 推荐使用的创建线程的方式
* 实现runnable接口,重写run方法,=
* */
public class DemoRunnable implements Runnable {
@Override
public void run() {
// run方法线程体
for (int i = 0; i < 20; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
// 创建runnable接口的实现类对象
DemoRunnable demoRunnable = new DemoRunnable();
// 创建线程对线,通过线程对象来开启我们的线程,代理
new Thread(demoRunnable).start();
for (int i = 0; i < 30; i++) {
System.out.println("我在学习多线程---"+i);
}
}
}
//一份资源
startThread4 station = new StartThread4();
//多个代理
new Thread(station,"小明").start();
new Thread(station,"老师").start();
new Thread(station,"小红").start();
? 1.避免了单继承的局限性
? 一个类只能继承一个类,类继承了Thread类就不能继承其他的类。
? 实现了Runnable接口,还可以继承其他的类,实现其他的接口。
? 2.增强了程序的扩展性,降低了程序的耦合性(解耦)
? 实现Runnable接口的方式,把设置线程任务和开启新线程进行了分离(解耦)。
? 实现类中,重写了run方法:用来设置线程任务
? 创建Thread类对象,调用start方法:用来开启新线程。
? 把子类继承父类,重写父类方法,创建子类对象合一步完成。
? 把实现类接口,重写接口中的方法,创建实现类对象合一步完成。
? 匿名内部类的最终产物:子类/实现类对象,而这个类没有名字。
new 父类/接口(){
重复父类/接口中的方法
}
package DemoInnerClass;
/*
* 匿名内部内创建Thread多线程
* */
public class DemoMain01 {
public static void main(String[] args) {
new Thread(){
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("JAVA,你好"+i);
}
}
}.start();
test();
}
private static void test() {
for (int i = 0; i < 30; i++) {
System.out.println("HelloWorld"+i);
}
}
}
package DemoInnerClass;
/*
* 匿名内部类创建Runnable多线程
* */
public class DemoMain02 {
public static void main(String[] args) {
Runnable ra = new Runnable() {
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println("HelloWorld"+i);
}
}
};
new Thread(ra).start();
test2();
}
private static void test2() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName()+"--->"+i);
}
}
}
格式:
synchronized(锁对象){
可能会出现线程安全问题的代码(访问了共享数据)
}
【注意】:
? 1.通过代码块中的锁对象,可以使用任意的对象。
? 2.但是必须保证多个线程使用的锁对象是同一个。
? 3.锁对象作用:把同步代码块锁住,只让一个县城在同步代码块中执行。
实现类代码:
package LockObject;
public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100;
//定义一个锁对象
Object o1 = new Object();
@Override
public void run() {
while (true) {
synchronized (o1){
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
}
}
}
}
}
使用步骤:
? 1.把访问了共享数据的代码抽取出来,放到方法中。
? 2.在方法上添加synchronized修饰符
格式:定义方法的格式:
修饰符 synchronized 返回值类型 方法名称(参数列表){
可能会出现线程安全问题的代码(访问了共享数据的代码)
}
实现类代码:
package DemoMethod;
public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100;
@Override
public void run() {
while (true) {
run1();
}
}
public synchronized void run1(){
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
}
}
}
java.until.concurrent.locks.lock接口
lock实现提供了比使用synchronized方法和语句可获得的更广泛的锁定操作。
lock接口中的方法:
? void lock()获取锁
? void unlock() 释放锁
java.until.concurrent.locks.ReentrantLock implements Lock接口
使用步骤:
? 1.在成员位置创建一个ReentrantLock对象
? 2.在可能会出现安全问题的代码前调用lock接口中的方法lock获取锁。
? 3.在可能会出现安全问题的代码后调用lock接口中的方法unlock释放锁。
实现类的代码:
package DemoLock;
import java.util.concurrent.locks.ReentrantLock;
public class RunnableImpl implements Runnable {
// 定义一个多线程的共享资源
private int ticket = 100;
//创建一个ReentrantLock对象
ReentrantLock r1 = new ReentrantLock();
@Override
public void run() {
while (true) {
r1.lock();
if (ticket > 0) {
// sleep提高出现安全问题的概率
try {
Thread.sleep(10);
System.out.println(Thread.currentThread().getName() + "--->正在售卖第" + ticket + "张票!");
ticket--;
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
r1.unlock();
}
}
}
}
}
调用方法的代码:
package DemoLock;
public class DemoMain {
public static void main(String[] args) {
RunnableImpl ra = new RunnableImpl();
Thread mt1 = new Thread(ra);
Thread mt2 = new Thread(ra);
Thread mt3 = new Thread(ra);
mt1.start();
mt2.start();
mt3.start();
}
}
? 创建一个顾客线程(消费者):告知老板要的包子的种类和数量,调用wait方法,放弃cpu的执行,进入到WAITING状态(无线等待)
? 创建一个老板线程(生产者):花了5秒做包子,做好包子之后,调用notify方法,唤醒顾客吃包子。
【注意】:
? 顾客和老板线程必须使用同步代码块包裹起来,保证等待和唤醒只能有一个在执行。
? 同步使用的锁对象必须保证唯一。
? 只有锁对象才能调用wait和notify方法
Object类中的方法:
? void wait()
? 在其他线程调用此对象的notify方法或notifyALL方法,导致当前线程等待。
? void notify()
? 唤醒在此对象监视器上等待的单个线程。
? 会继续执行wait方法之后的代码。
? 1.使用sleep(long m)方法,在毫秒值结束之后,线程睡醒进入到Runnable/Blocked状态。
? 2.使用wait(long m)方法,wait方法如果在毫秒值结束之后,还没有被notify唤醒,就会自动醒来,线程睡醒进入到Runnable/Blocked状态。
唤醒的方法:
? void notify() 唤醒在此对象监视器上等待的单个线程。
? void notifyALL() 唤醒在此对象监视器上等待的所有线程。
? java.util.concurrent.Executors:线程池的工厂类,用来生成线程池。
? static ExecutorService newFixedThreadPool(int nThreads) 创建一个指定线程数量的线程池。
? 参数:
? 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销毁线程池(不建议执行)
? 由三部分组成:
? a.一些参数
? b.一个箭头
? c.一段代码
? 格式:
? (参数列表) -> {一些重写方法的代码};
? 解释说明格式:
? ():接口中抽象方法的参数列表,没有参数,就空着;有参数就写出参数,多个参数使用逗号分隔。
? ->:传递的意思,把参数传递给方法体{}
? {}:重写接口的抽象方法的方法体
? 1.使用Lambda必须具有接口,且要求接口中有且仅有一个抽象方法。
? 无论是JDK内置的Runnable、Comparator接口还是自定义的接口,只有当接口中的抽象方法存在且唯一时,才可以使用Lambda。
? 2.使用Lambda必须具有上下文推断。
? 也就是方法的参数或局部变量类型必须为Lambda对应的借口了偶像,才能使用Lambda作为该接口的实例。
备注:有且仅有一个抽象方法的接口,称为“函数式接口”。
原文:https://www.cnblogs.com/KingTL/p/12945274.html