二十八.多线程
文章目录
28.1线程的三种实现方式
- 继承Thread类的方式进行实现
- 实现Runnable接口的方式进行实现
- 利用Callable接口和Future接口方式实现
28.1.1 第一种
- 继承Thread类的方式进行实现
1.自己定义一个类继承Thread
2.重写run方法,编写线程执行体
3.创建子类的对象,并用start()方法启动线程
public class Mythread extends Thread{
@Override
public void run() {
//线程要执行代码
for (int i = 0; i < 3; i++) {
System.out.println(getName()+"你好");
}
}
}
==================================
public class MythreadTest {
public static void main(String[] args) {
Mythread t1 = new Mythread();
Mythread t2 = new Mythread();
t1.setName("线程1:");
t2.setName("线程2:");
t1.start();
t2.start();
}
}
28.1.2 第二种
-
实现Runnable接口的方式进行实现
1.自己定义一个类实现Runnable接口
2.重写里面的run方法
3.创建自己的类的对象
4.创建一个Thread类的对象,并用start()方法开启线程
public class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// 获取到当前线程的对象
Thread t = Thread.currentThread();
System.out.println(t.getName()+"你好a");
}
}
}
===================================================
public class MyRunTest {
public static void main(String[] args) {
// 创建字节定义类的对象,表示要执行的任务
///即,创建线程要执行的参数对象
MyRun m1 = new MyRun();
// 创建线程对象
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
//给线程设置名字
t1.setName("线程1:");
t2.setName("线程2:");
//开启线程
t1.start();
t2.start();
}
}
28.1.3 第三种
- 利用Callable接口和Future接口方式实现
特点:可以获取到多线程运行的结果
1. 创建一个类MyCallable实现Callable接口
2. 重写call (是有返回值的,表示多线程运行的结果)
3. 创建MyCallable的对象(表示多线程要执行的任务)
4. 创建FutureTask的对象(作用管理多线程运行的结果)
5. 创建Thread类的对象,并启动(表示线程)
import java.util.concurrent.Callable;
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 3; i++) {
sum =sum +i;
}
return sum;
}
}
===================================================
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class MyCallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 创建MyCallable的对象(表示多线程要执行的任务)
MyCallable mc = new MyCallable();
// 创建FutureTask的对象(作用管理多线程运行的结果)
FutureTask<Integer> ft = new FutureTask<>(mc);
// 创建线程对象
Thread t1 = new Thread(ft);
// 开启线程
t1.start();
// 获取多线程运行的结果
Integer res = ft.get();
System.out.println(res);
}
}
28.2 常见的成员方法
方法 | 说明 |
---|---|
String getName() | 返回此线程的名称 |
void setName(String name) | 设置线程的名字(构造方法也可以设置名字) |
static Thread currentThread() | 获取当前线程的对象 |
static void sleep(long time) | 让线程休眠指定的时间,单位为毫秒 |
setPriority(int newPriority) | 设置线程的优先级 |
final int getPriority() | 获取线程的优先级 |
final void setDaemon(boolean on) | 设置为守护线程 |
public static void yield() | 出让线程/礼让线程 |
public final void join() | 插入线程/插队线程 |
public class MyRun implements Runnable{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// 获取到当前线程的对象
Thread t = Thread.currentThread();
try {
//休眠多少时间
t.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(t.getName()+"你好a");
}
}
}
========================================
public class MyRunTest {
public static void main(String[] args) throws InterruptedException {
// 创建字节定义类的对象,表示要执行的任务
// 创建线程要执行的参数对象
MyRun m1 = new MyRun();
// 创建线程对象
Thread t1 = new Thread(m1);
Thread t2 = new Thread(m1);
Thread t3 = new Thread(m1,"线程3:");
//给线程设置名字
t1.setName("线程1:");
t2.setName("线程2:");
//获取优先级
System.out.println(t1.getPriority());//5
//设置优先级(默认是5)
t1.setPriority(10);//可以先让t1线程先执行完
// t1.sleep(5000);
t1.start();
t2.start();
t3.start();
}
}
//用构造方法设置线程名字
// 需要子类继承Thread,然后子类用super调用父类Thread的构造方法
public class Mythread extends Thread{
public Mythread() {
}
public Mythread(String name) {
super(name);
}
@Override
public void run() {
//线程要执行代码
for (int i = 0; i < 3; i++) {
System.out.println(getName()+"你好");
}
}
}
=============================================
public class MythreadTest {
public static void main(String[] args) {
Mythread t1 = new Mythread("线程1:");
Mythread t2 = new Mythread("线程2:");
// t1.setName("线程1:");
// t2.setName("线程2:");
t1.start();
t2.start();
}
}
28.3 守护线程
final void setDaemon(boolean on) 设置为守护线程
当非守护线程执行结束后,守护线程才陆续结束;守护线程并不会立即结束
public class Mythread1 extends Thread{
//非守护线程
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println(getName()+"better..."+i);
}
}
}
=============================================
public class Mythread2 extends Thread{
// 守护线程
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName()+"good..."+i);
}
}
}
=============================================
public class MythreadTest {
public static void main(String[] args) {
//创建线程对象
Mythread1 t1 = new Mythread1();
Mythread2 t2 = new Mythread2();
// 给线程设置名字
t1.setName("大树");
t2.setName("小草");
// 将线程2设置为守护线程
//即,当非守护线程1执行结束后,守护线程2才陆续结束,并不会立即结束
t2.setDaemon(true);
// 开启线程
t1.start();
t2.start();
}
}
28.4 出让线程
public static void yield() 出让线程/礼让线程
当某线程1执行结束后,会让当前CPU的执行权,此时多个线程包括线程1会重新抢夺CPU的执行权,让运行的结果尽可能的均匀
public class Mythread1 extends Thread{
public Mythread1() {}
public Mythread1(String name) { super(name);}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()+"...");
//设置为出让线程/礼让线程
//表示出让当前CPU的执行权
Thread.yield();
}
}
}
=============================================
public class MyThreadTest {
public static void main(String[] args) {
Mythread1 t1 = new Mythread1("线程1:");
Mythread1 t2 = new Mythread1("线程2:");
t1.start();
t2.start();
}
}
28.5 插入线程
public final void join()
插入线程/插队线程 表示插入当前线程之前
比如有线程1 和线程2, 在抢夺CPU的执行权,如果线程2抢到,线程2执行,如果将线程1设置为插入线程,则表示当线程1执行结束后,线程2 才会执行
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(getName()+"..."+i);
}
}
}
=============================================
public class MyThreaTest {
public static void main(String[] args) throws InterruptedException {
MyThread t1 = new MyThread();
t1.setName("线程1:");
t1.start();
//表示把t1这个线程,插入到当前线程之前。
//t1:线程1
//当前线程: main线程
t1.join();
// 执行在main线程当中
for (int i = 0; i < 10; i++) {
System.out.println("man线程...");
}
}
}
28.6 线程生命周期
(图片来自B站Java视频)
问:sleep方法会让线程睡眠,睡眠时间结束后,立马会执行下面的代码么?
答:不会,因为睡眠结束后,会进入就绪状态,进行CPU的抢夺,抢到CPU 的执行权后才会进入运行状态。
28.7 同步代码块
把操作共享数据的代码锁起来
格式
synchronized(锁){
操作共享数据的代码
}
共有100张券,而有3个销售出售,请设计一个程序模拟该出售过程
public class MyThread extends Thread {
// 表示这个类所有的对象,都共享ticket数据
static int ticket=0;//0-99
// 锁对象,一定是唯一的,即 表示obj被共享
static Object obj = new Object();
@Override
public void run() {
while (true) {
// 同步代码块
// obj 可换成 MyThread.class
synchronized (obj) {
if (ticket < 100) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
ticket++;
System.out.println(getName() + "出售第" + ticket + "个东西");
} else {
break;
}
}
}
}
// ending.....
}
==================================
public class MyThreadTest {
public static void main(String[] args) {
// 创建线程对象
MyThread t1= new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
// 给线程设置名字
t1.setName("A:");
t2.setName("B:");
t3.setName("C:");
// 开启线程
t1.start();
t2.start();
t3.start();
}
}
28.8 同步方法
把synchronized关键字加到方法上
格式
修饰符 synchronized 返回值类型 方法名(方法参数){
....
}
特点:
-
同步方法是锁住方法里的所有代码
-
锁对象不能自己指定
非静态 :this
静态: 当前类的字节码文件对象
需求:
共有100张券,而有3个销售出售,请设计一个程序模拟该出售过程
利用同步方法完成
(技巧:先写成同步代码块,再改成成同步方法)
public class MyRunnable implements Runnable{
// 此处static可以省略不写
//因为MyRunnable只创建了一次...
static int stick =0;//0-99
@Override
public void run() {
while (true){
if (method()) break;
}
}
//ctrl + alt +m ;抽取成方法
//方法是非静态的,所以锁对象是this,指的是mr,mr是唯一的
private synchronized boolean method() {
if (stick == 100){
return true;
}else {
stick++;
String name = Thread.currentThread().getName();
System.out.println(name+"出售第"+stick+"个东西");
}
return false;
}
}
==================================
public class MyRunnableTest {
public static void main(String[] args) {
//创建线程要执行的参数对象,即 任务
MyRunnable mr = new MyRunnable();
// 创建线程对象,(代表三个销售)
Thread t1 = new Thread(mr);
Thread t2 = new Thread(mr);
Thread t3 = new Thread(mr);
// 给线程设置名字
t1.setName("A:");
t2.setName("B:");
t3.setName("C:");
//开启线程
t1.start();
t2.start();
t3.start();
}
}
标签:Java,Thread,void,t1,线程,new,多线程,public
From: https://blog.csdn.net/weixin_54555405/article/details/141949321