java多线程编程
课程视频来源:多线程06:初识并发问题_哔哩哔哩_bilibili
线程简介
进程
编写的静态代码文件,经过编译成为可执行的二进制文件,执行后它被加载至内存,被CPU执行。这个运行的程序就是进程
线程
为何要引入线程
如视频播放时,需要三个核心模块:1. 从视频读取数据,2. 数据解压缩,3. 解压缩的数据播放。单个进程肯定是按顺序执行这3个模块,但造成的问题是画面与声音不连贯,函数之间无法并发执行,效率低。那改成多进程呢?问题依旧存在:不同进程通信不方便,维护进程的开销大。
所以需要一种新的实体,满足以下两点,它便是线程。
- 实体间可并发执行
- 共享相同的地址空间。
什么是线程
线程thread是操作系统调度的最小单位,多数情况下,线程被包含与进程process之中,是其实际运行单位。一个进程可并发多个线程,各个线程执行不同的任务。每个线程有自己独立的寄存器和栈,确保各个线程独立。
java中线程的3中创建方式
- 继承
Thread
类 - 实现
Runnable
接口 - 实现
Callable
接口
继承Thread类
创建一个子线程类Mythead继承Thead类,重写run
方法即可
package ThreadExtend;
public class MyThread extends Thread {
@Override
public void run() {
for(int i = 0;i < 10;++i){
System.out.println("son: "+ Thread.currentThread());
}
}
}
主线程调用
import ThreadExtend.MyThread;
public class Main {
public static void main(String[] args) {
test1();
for (int i = 0; i < 30; ++i) {
System.out.println("father: " + Thread.currentThread());
}
}
public static void test1()
{
MyThread t = new MyThread();
t.start();
}
}
注意:
- 只有通过
start
调用才会开启子线程,调用run
只是普通的调用。 - 主线程中只有开启线程的之后的代码,才会和子线程并发执行。
实现Runnable接口
实现Runnable
接口,重写run
方法。
package RunnableInterface;
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 0;i< 20;++i){
System.out.println("Runnable实现线程:"+Thread.currentThread());
}
}
}
创建线程:
public static void test2(){
MyRunnable t = new MyRunnable();
new Thread(t).start();
}
查看Thread.java原码文件可发现,实际上Thread类正是实现了Runnable接口:
public class Thread implements Runnable
推荐使用Runnable接口!!!
下面展示同一个对象被多个线程访问的例子
package RunnableInterface;
public class TestRunnable implements Runnable {
private int totalTicketNum;
// private String name;
TestRunnable(int num){
this.totalTicketNum = num;
}
@Override
public void run() {
buy();
}
void buy(){
try {
while(true){
synchronized (this) {
if(totalTicketNum > 0){
System.out.println(Thread.currentThread().getName() + "拿到了第" + totalTicketNum-- + "票");
}
else{
break;
}
}
Thread.sleep(300);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package RunnableInterface;
public class Main {
public static void main(String[] args) {
TestRunnable t = new TestRunnable(10);
new Thread(t,"小明").start();
new Thread(t,"小花").start();
new Thread(t,"小三").start();
}
}
问题:为啥不把buy整体同步呢?因为想让totalTicketNum 变为0的时候线程结束执行,如果synchronized 同步整个buy方法,里面的while的存在导致totalTicketNum>0时,第一个线程一直持有锁,其他线程无法进入,导致只有一个人拿到了所有票。同时为了避免因为线程执行过快让一个人拿了全部票,添加了延时。
Callable接口
特点:
- 实现Callable接口,需要返回值类型
- 重写call方法,需要抛出异常
- 创建目标对象
- 创建执行服务
ExecutorService ser = Executors.newFixedThreadPool(int nThreads);
- 提交执行
Futureinteger ft1 = ser.submit(t1);
- 获取结果
ft1.get()
- 关闭服务
ser.shutdownNow();
例子:
package CallableInterface;
import java.util.concurrent.Callable;
public class MyCall implements Callable<Integer> {
private int n;
public MyCall(int n){
this.n = n;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1;i <= n;++i){
sum += i;
}
System.out.println(Thread.currentThread().getName() + "计算1 + 2 + ... + " + n + " = " + sum);
return sum;
}
}
package CallableInterface;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyCall t1 = new MyCall(100);
MyCall t2 = new MyCall(200);
MyCall t3 = new MyCall(300);
MyCall t4 = new MyCall(400);
ExecutorService ser = Executors.newFixedThreadPool(3);
Future<Integer> ft1 = ser.submit(t1);
Future<Integer> ft2 = ser.submit(t2);
Future<Integer> ft3 = ser.submit(t3);
Future<Integer> ft4 = ser.submit(t4);
System.out.println("返回值1:" + ft1.get());
System.out.println("返回值2:" + ft2.get());
System.out.println("返回值3:" + ft3.get());
System.out.println("返回值4:" + ft4.get());
ser.shutdownNow();
}
}
执行结果:
https://s2.loli.net/2022/09/26/w3RAbNcMIu1WKTo.png
lambda表达式
为何要lambda表达式?
避免匿名内部类定义过多,关注核心逻辑,让代码更简洁。
应用场景
任何接口interface,若只包含一个抽象方法,那么可以i通过lambda表达式创建改接口对象。
特点
- 可省略参数类型
- 一个参数可省略括号
- 一条语句可省略花括号
package Lambda;
public class MyLambda {
static class Like2 implements ILike{
@Override
public void say() {
System.out.println("peipei like me");
}
}
public static void main(String[] args) {
ILike like = new Like1();
like.say();
like = new Like2();
like.say();
//局部内部类
class Like3 implements ILike{
@Override
public void say() {
System.out.println("Marry me, peipei!");
}
}
like = new Like3();
like.say();
like = new ILike() {
@Override
public void say() {
System.out.println("I will marry peipei");
}
};
like.say();
like = ( )->{
System.out.println("peipei will marry me!");
};
like.say();
}
}
//函数接口
interface ILike{
void say();
}
class Like1 implements ILike{
@Override
public void say() {
System.out.println("I love peipei");
}
}
线程状态
线程5个状态
线程方法
终止线程
一般用标志法,即外部修改变量启动或者停止线程
package ThreadExtend;
public class MyThread extends Thread {
volatile private Boolean flag = true;
@Override
public void run() {
long num = 0;
while(flag){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
num++;
}
System.out.println("num = " + num);
}
public void stopThread(){
flag = false;
}
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
for(int i = 0;i<100;++i){
if(i < 50){
Thread.sleep(10);
}
else if(i == 50){
System.out.println("线程停止");
t.stopThread();
break;
}
}
t.join();
}
}
操作系统往往将变量缓存下来,故用volatile 修饰,使得读取flag时强制从其内存地址读取最新值。
线程休眠sleep
sleep(int millisecond); 时间达到后线程进入就绪状态,可用于模拟网络延时,倒计时等。每个对象都有一把锁,sleep不会释放锁.
线程礼让yield
礼让的线程先出来,再和其他线程竞争。线程礼让一定成功,但CPU依旧可能会调度它。
package RunnableInterface;
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
System.out.println(Thread.currentThread().getName()+"结束");
}
}
package RunnableInterface;
public class MyYeildRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"开始");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"结束");
}
}
package RunnableInterface;
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
MyYeildRunnable myYeildRunnable = new MyYeildRunnable();
new Thread(myYeildRunnable,"a").start();
new Thread(myRunnable,"b").start();
}
}
/*礼让成功
a开始
b开始
b结束
a结束
*/
join线程
合并线程,听不懂???就是插队执行,其他线程阻塞等我执行完你再接着。
package ThreadExtend;
public class MyThread extends Thread {
@Override
public void run() {
for(int i = 0; i< 1000;++i){
System.out.println("vip来了!" + i);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread t = new MyThread();
t.start();
for(int i = 0;i<100;++i){
if(i == 50){
System.out.println("aaa:" + t.getState());
t.join();
System.out.println("bbb:" + t.getState());
}
System.out.println("main" + i);
}
}
}
线程优先级
事实上没什么卵用
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable,"a");
Thread t2 = new Thread(myRunnable,"b");
Thread t3 = new Thread(myRunnable,"c");
Thread t4 = new Thread(myRunnable,"d");
t1.setPriority(4);
t2.setPriority(8);
t3.setPriority(2);
t4.setPriority(10);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
守护线程
什么是守护线程?
java中线程分为:1. 用户线程, 2. 守护线程。 守护线程通常后台执行,为用户线程服务。JVM的垃圾回收线程是典型的守护线程,在程序执行时它一直保持执行,但当所有线程结束,它就可以结束了。
package RunnableInterface;
class DaemonThred implements Runnable{
@Override
public void run() {
int i = 0;
while(true){
try {
Thread.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("人生路漫漫,活着的第" + i++ +"天");
}
}
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int i = 1;i <= 365;++i){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("今天是一年中第" +i + "天");
}
}
}
package RunnableInterface;
public class Main {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
DaemonThred daemonThred = new DaemonThred();
Thread t = new Thread(daemonThred);
t.setDaemon(true); //设置为守护线程
t.start();
new Thread(myRunnable).start();
}
}
标签:java,Thread,void,基础,线程,println,new,多线程,public From: https://www.cnblogs.com/shmilyt/p/17054569.html