标签:增加 影响 不同的 操作系统 exec 使用 情况 常用 优点
Thread
Java虚拟机允许应用程序同时执行多个线程。
每个线程都有优先级,具有较高优先级的线程优先于优先级较低的线程执行。
当在某个线程中运行的代码创建一个新的Thread
对象时,新线程的优先级最初设置为创建线程的优先级。
每个线程都有一个用于识别的名称,多个线程可能具有相同的名称。如果在创建线程时未指定名称,则会为其生成一个新名称。
继承Thread类,重写Thread类的run方法
public class Thread implements Runnable {
@Override
public void run() {
if (target != null) {
target.run();
}
}
}
//继承Thread,重写run方法的逻辑为:不再调用target.run()方法,而是调用子类自定义的[线程要执行的方法]
//所以Thread() / Thread(String name)的构造方式,target为null,执行的是子类MyThread的run()
//Thread类继承Runnable接口,实现run方法,target代表Runnable实例对象,即创建Thread对象时传递的Runnable类型的参数
//Thread(Runnable target)的构造方式,就不需要重写run方法,target.run()调用Runnable实现类的run逻辑
Thread类的常用方法
方法名 | 描述 |
---|---|
public final String getName()【final修饰方法,子类不允许重写】 | 获取当前线程的名称 |
public final int getPriority() | 获取当前线程的优先级 |
public final void setName(String name) | 设置当前线程的名称 |
public final void setPriority(int newPriority) | 设置当前线程优先级 |
public static native Thread currentThread(); | 获取当前执行的线程 |
public final static int MIN_PRIORITY = 1; | 最低优先级 |
public final static int NORM_PRIORITY = 5; | 默认优先级 |
public final static int MAX_PRIORITY = 10; | 最高优先级 |
package org.westos.demo;
/**
* @author lwj
* @date 2020/5/31 13:53
*/
public class MyTest {
public static void main(String[] args) {
System.out.println("主线程的main方法开始执行");
System.out.println(Thread.currentThread().getName());
//获取当前正在执行的线程对象Thread.currentThread()
//main
System.out.println(Thread.currentThread().getPriority());
//获取当前正在执行的线程的优先级
//5
//创建一个线程,1、继承Thread类,重写run方法
MyThread myThread = new MyThread();
myThread.setName("myThread-0");
//设置线程的名称
System.out.println(myThread.getName());
//获取线程的名称
//myThread-0
System.out.println(myThread.getPriority());
//获取线程的优先级
//5
myThread.setPriority(10);
//设置线程的优先级
System.out.println(myThread.getPriority());
//10
myThread.start();
//启动线程,同一线程不要重复启动
//创建另一个线程
MyThread myThread1 = new MyThread();
myThread1.setName("myThread-1");
System.out.println(myThread1.getName());
//myThread-1
System.out.println(myThread1.getPriority());
//5
myThread1.start();
/*
两个线程轮流被操作系统调度,cpu执行,具有随机性
*/
System.out.println("主线程的main方法执行完毕");
}
}
class MyThread extends Thread {
@Override
public void run() {
//run方法:该线程执行的代码
for (int i = 0; i < 100; i++) {
System.out.println("线程" + this.getName() + ":" + i);
}
}
}
线程并不一定立即执行,由操作系统进行调度。
package org.westos.demo;
/**
* @author lwj
* @date 2020/6/1 16:33
*/
public class MyTest {
public static void main(String[] args) {
MyThread myThread = new MyThread("myThread-0");
myThread.setPriority(Thread.MAX_PRIORITY);
myThread.start();
}
}
class MyThread extends Thread {
public MyThread() {}
public MyThread(String name) {
super(name);
//MyThread子类不能继承父类Thread的构造方法,但是可以使用super关键字调用父类的构造方法
}
@Override
public void run() {
System.out.println("线程" + this.getName() + this.getPriority());
//MyThread继承Thread,自动继承Thread类的getName()和getPriority()
//线程myThread-010
}
}
缺陷:单继承具有局限性。
实现Runnable接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
package org.westos.demo2;
/**
* 实现Runnable接口
* @author lwj
* @date 2020/6/1 20:01
*/
public class MyTest {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread thread = new Thread(myRunnable, "A线程");
//构造方法:Thread(Runnable runnableImpl)
//Thread(Runnable target, String name)
Thread thread1 = new Thread(myRunnable, "B线程");
//虽然是一个相同的任务(对象),但是两个线程在执行run方法时,都会有自己的内存空间来执行for循环,来存储变量i,所以两个变量i的内存地址不同,那么就是两次的0-19
thread.start();
//开启线程
thread1.start();
}
/*
B线程---0
B线程---1
B线程---2
B线程---3
B线程---4
A线程---0
A线程---1
A线程---2
B线程---5
B线程---6
B线程---7
B线程---8
B线程---9
A线程---3
A线程---4
A线程---5
A线程---6
A线程---7
A线程---8
A线程---9
每个线程都有自己的PC寄存器/程序计数器,还有自己的虚拟机栈内存,存储自己线程的方法调用
*/
}
class MyRunnable implements Runnable {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
优点:避免了单继承的局限性,方便同一个对象被多个线程使用。
实现Callable接口
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
相较于实现Runnable接口的方式,方法有返回值,并且可以抛出异常。
这种方式需要 FutureTask 实现类的支持(包装Callable),用于接收运算结果。FutureTask 是Runnable接口的实现类,Thread(Runnable target)来接收FutureTask实例。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
package org.westos.demo2;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现Callable接口
* @author lwj
* @date 2020/6/1 20:18
*/
public class MyTest2 {
public static void main(String[] args) {
FutureTask<Integer> futureTask = new FutureTask<>(new MyCallable(100));
Thread thread = new Thread(futureTask);
//Thread(Runnable target)
thread.start();
try {
Integer integer = futureTask.get();
//FutureTask<V>用于接收运算结果
System.out.println(integer);
//5050
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
class MyCallable implements Callable<Integer> {
private int num;
public MyCallable(int num) {
this.num = num;
}
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= num; i++) {
sum += i;
}
return sum;
}
}
Runnable和Callable的区别
Thread.sleep()是Thread类的一个静态方法,使当前线程休眠,进入阻塞状态(暂停执行),休眠结束后,由阻塞态进入就绪态;如果线程在睡眠状态被中断,将会抛出IterruptedException中断异常。
注意:在哪个线程里面调用sleep()方法就阻塞哪个线程。
package org.westos.demo;
/**
* 线程休眠Thread.sleep,让当前线程休眠
* @author lwj
* @date 2020/6/1 16:49
*/
public class MyTest2 {
public static void main(String[] args) throws InterruptedException {
System.out.println("主线程开始执行了");
Thread.sleep(2000);
//主线程休眠
System.out.println("主线程休眠后的代码");
MyThread3 myThread = new MyThread3("A线程");
MyThread3 myThread1 = new MyThread3("B线程");
myThread.start();
myThread1.start();
}
}
class MyThread3 extends Thread {
public MyThread3() {}
public MyThread3(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(1000);
//实现Runnable接口的方式创建线程,在run方法中如果存在异常,那么不能向外抛出,只能在内部try-catch
} catch (InterruptedException e) {
e.printStackTrace();
}
String name = Thread.currentThread().getName();
System.out.println(name + "---" + i);
}
}
}
在A线程中调用B线程的join()方法,那么A线程必须等待B线程执行完毕后,才可以继续执行。
package org.westos.demo;
/**
* join
* @author lwj
* @date 2020/6/1 18:52
*/
public class MyTest3 {
public static void main(String[] args) throws InterruptedException {
MyThread1 myThread = new MyThread1("刘备");
MyThread1 myThread1 = new MyThread1("关羽");
MyThread1 myThread2 = new MyThread1("张飞");
myThread.start();
myThread.join();
//此时进程中只有main线程和myThread线程,在main线程中调用myThread.join()方法,那么直到myThread线程执行完毕后,main线程才会继续执行
myThread1.start();
myThread1.join();
//此时只有main线程和myThread1线程,当myThread1线程执行完毕后,main线程才会继续执行
myThread2.start();
myThread2.join();
//同理,当myThread2线程执行完毕后,main线程才会继续执行
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
class MyThread1 extends Thread {
public MyThread1() {}
public MyThread1(String name) {
super(name);
}
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println(name + "---" + i);
}
}
}
案例2
package org.westos.demo;
/**
* @author lwj
* @date 2020/6/4 15:53
*/
public class JoinTest implements Runnable{
public static int a = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
a += 1;
}
}
public static void main(String[] args) {
Thread thread = new Thread(new JoinTest());
thread.start();
//线程启动,需要准备资源,等待CPU的调度,而且和main线程是处于并发执行的状态,所以存在一种情况,main线程执行完毕了,thread线程才开始执行
System.out.println(a);
//0
}
}
package org.westos.demo;
/**
* @author lwj
* @date 2020/6/4 15:53
*/
public class JoinTest implements Runnable{
public static int a = 0;
@Override
public void run() {
for (int i = 0; i < 5; i++) {
a += 1;
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new JoinTest());
thread.start();
thread.join();
//在main线程中调用thread.join方法,那么main线程必须等待thread线程执行完毕后才可以继续执行
System.out.println(a);
//5
}
}
线程让步
yield()的作用是让步。它能让当前线程由"运行状态"进入到"就绪状态",从而让其它具有相同优先级的等待线程获取执行权;但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权;也有可能是当前线程又进入到"运行状态"继续运行!
package org.westos.demo;
/**
* 线程礼让yield
* @author lwj
* @date 2020/6/4 16:07
*/
public class MyTest5 {
public static void main(String[] args) {
MyThread4 thread = new MyThread4("A线程");
MyThread4 thread1 = new MyThread4("B线程");
thread.start();
thread1.start();
}
/*
...
A线程---0
B线程---3
A线程---1
B线程---4
A线程---2
B线程---5
A线程---3
B线程---6
A线程---4
B线程---7
A线程---5
B线程---8
A线程---6
B线程---9
A线程---7
B线程---10
A线程---8
B线程---11
A线程---9
B线程---12
A线程---10
B线程---13
A线程---11
B线程---14
A线程---12
B线程---15
A线程---13
B线程---16
A线程---14
B线程---17
A线程---15
B线程---18
A线程---16
B线程---19
*/
}
class MyThread4 extends Thread {
public MyThread4() {}
public MyThread4(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
Thread.yield();
}
}
}
Java用户线程和守护线程
package org.westos.demo;
/**
* 守护线程
* @author lwj
* @date 2020/6/1 19:14
*/
public class MyTest4 {
public static void main(String[] args) {
//更改主线程的name
Thread.currentThread().setName("刘备");
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
MyThread2 myThread = new MyThread2("关羽");
MyThread2 myThread1 = new MyThread2("张飞");
myThread.setDaemon(true);
myThread1.setDaemon(true);
//设置为守护线程
//当主线程刘备消亡后,守护线程也要消亡
myThread.start();
myThread1.start();
System.out.println(Thread.currentThread().getName() + "线程执行完毕");
/*
刘备---0
刘备---1
刘备---2
刘备---3
刘备---4
刘备---5
刘备---6
刘备---7
刘备---8
刘备---9
刘备线程执行完毕
张飞---0
关羽---0
关羽---1
关羽---2
关羽---3
关羽---4
关羽---5
*/
}
}
class MyThread2 extends Thread {
public MyThread2() {}
public MyThread2(String name) {
super(name);
}
@Override
public void run() {
String name = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println(name + "---" + i);
}
}
}
创建守护线程
package org.westos.demo;
/**
* stop
* @author lwj
* @date 2020/6/4 16:29
*/
public class MyTest6 {
public static void main(String[] args) throws InterruptedException {
MyThread5 thread = new MyThread5("A线程");
thread.start();
//让主线程休眠2ms,给thread线程一些执行时间
Thread.sleep(2);
thread.stop();
//中断线程,已过时的方法
}
/*
A线程---0
...
A线程---90左右
*/
}
class MyThread5 extends Thread {
public MyThread5() {}
public MyThread5(String name) {
super(name);
}
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
interrupt()
:清除阻塞,打断线程的阻塞状态,当线程调用wait()、sleep(long mills)方法后,由运行态变为阻塞态。
package org.westos.demo2;
/**
* interrupt
* @author lwj
* @date 2020/6/4 16:48
*/
public class MyTest3 {
public static void main(String[] args) throws InterruptedException {
MyThread myThread = new MyThread();
myThread.setName("A线程");
myThread.start();
Thread.sleep(2);
//主线程休眠保证myThread已经启动
myThread.interrupt();
//清除myThread线程的阻塞状态
//触发了sleep方法的InterruptedException异常,进入catch块打印异常的堆栈信息
/*
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at org.westos.demo2.MyThread.run(MyTest3.java:26)
A线程---0
A线程---1
A线程---2
A线程---3
A线程---4
A线程---5
A线程---6
A线程---7
A线程---8
A线程---9
*/
}
}
class MyThread extends Thread {
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//手动让线程由运行态变为阻塞状态
for (int i = 0; i < 10; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
}
}
}
package org.westos.demo3;
/**
* 售票案例
* @author lwj
* @date 2020/6/4 17:16
*/
public class MyTest {
public static void main(String[] args) {
CellThread a = new CellThread("A");
CellThread b = new CellThread("B");
CellThread c = new CellThread("C");
a.start();
b.start();
c.start();
/*
在增加static修饰后,售票不是按照tickets--的顺序输出的,所以是需要在run方法里加同步,每个线程执行完run方法后再轮到下一个线程
*/
}
}
class CellThread extends Thread {
//private int tickets = 100;
private static int tickets = 100;
/*
B窗口出售了第100张票
C窗口出售了第100张票
C窗口出售了第99张票
C窗口出售了第98张票
C窗口出售了第97张票
C窗口出售了第96张票
A窗口出售了第100张票
第一次是由于在main方法中创建了三个线程,每个线程对象都拥有tickets属性为100,即三个窗口各售100张票
所以需要将tickets属性作为三个线程共享的变量,添加static修饰
*/
public CellThread() {}
public CellThread(String name) {
super(name);
}
@Override
public void run() {
while (true) {
if (tickets >= 1) {
//只有在余票大于等于1张时才可以卖票
System.out.println(Thread.currentThread().getName() + "窗口出售了第" + tickets-- + "张票");
} else {
break;
}
}
}
}
package org.westos.demo3;
/**
* 售票案例
* @author lwj
* @date 2020/6/4 19:57
*/
public class MyTest2 {
public static void main(String[] args) {
/*Thread a = new Thread(new CellRunnable(), "a");
Thread b = new Thread(new CellRunnable(), "b");
Thread c = new Thread(new CellRunnable(), "c");*/
/*
同样的问题,三个线程,分别执行三个不同的任务Runnable对象,那么分别都拥有tickets=100的属性
*/
CellRunnable cellRunnable = new CellRunnable();
Thread a = new Thread(cellRunnable, "a");
Thread b = new Thread(cellRunnable, "b");
Thread c = new Thread(cellRunnable, "c");
a.start();
b.start();
c.start();
/*
三个线程执行一个Runnable任务之后,那么只有一个对象,所以任何一个线程对tickets属性的修改都会影响其他线程
而当继承Thread的方式时,是因为有三个对象,所以tickets变量必须设置为静态,多个对象共享数据
*/
}
}
class CellRunnable implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets >= 1) {
System.out.println(Thread.currentThread().getName() + "窗口出售了第" + tickets-- + "张票");
} else {
break;
}
}
}
}
package org.westos.demo3;
/**
* 模拟实际售票场景
* @author lwj
* @date 2020/6/4 20:07
*/
public class MyTest3 {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread a = new Thread(myRunnable, "a");
Thread b = new Thread(myRunnable, "b");
Thread c = new Thread(myRunnable, "c");
a.start();
b.start();
c.start();
}
}
class MyRunnable implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets >= 1) {
try {
Thread.sleep(50);
//判断有票之后
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "窗口出售了第" + tickets-- + "张票");
} else {
break;
}
}
}
}
27、多线程(多线程的三种实现方式、Thread线程类的常见方法、线程安全问题)
标签:增加 影响 不同的 操作系统 exec 使用 情况 常用 优点
原文地址:https://www.cnblogs.com/shawnyue-08/p/13046427.html