1.1、继承Thread类(重点)
public class MyThread extends Thread{
@Override
public void run() {
}
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
}
}
- 通过继承Thread类,然后重写run方法实现多线程
- 通过在main方法中调用实现了Thread类的对象的start方法
- 线程不一定立即执行,而是有CPU调度
- 不建议使用,避免oop单继承的局限性
1.2、实现Runnable接口(重点,推荐)
public class MyThread1 implements Runnable{
@Override
public void run() {
}
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
new Thread(myThread1).start();
}
}
- 通过实现Runnable接口,然后重写接口的run方法
- 通过在main方法中new一个Thread对象,然后将实现了Runnable接口的类的对象当做参数传入Thread构造方法,再调用Thread对象的start方法
- 本质上来说这两种方式都是一样的,因为Thread类也实现了Runnable方法,而继承Thread类就相当于实现了Runnable接口
- 推荐使用,方便同一个对象被多个线程调用
1.3、实现Callable接口(了解)
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class MyCallable implements Callable {
private int flag=1;
@Override
public String call() throws Exception {
Thread.sleep(10);
return "你好"+flag++;
}
public static void main(String[] args) {
MyCallable myCallable=new MyCallable();
Future future=null;
List> list=new ArrayList<>();
ExecutorService service= Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) {
future=service.submit(myCallable);
list.add(future);
}
try {
for (int i = 0; i < 10; i++) {
String str1=list.get(i).get();
System.out.println(str1);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdownNow();
}
}
- 通过实现Callable接口,重写call方,该方法与run方法不同,run方法没有返回值而call有
- 重写call方法时需要抛出异常
- 创建实现了Callabl接口的类的对象
- 通过ExecutorService service=Executors.newFixedThreadPool(10)方法创建执行服务,参数为线程的个数
- 通过service.submit(myCallable)提交执行,并返回一个Future<>类型的结果,参数为实现了Callabl接口的类的对象
- 通过Future<>对象的get方法获取返回的结果
-
最后通过ExecutorService 的对象的shutdownNow()方法关闭服务
-
Thread.currentThread().getName()–拿到当前线程的名字,Thread.currentThread()表示当前线程
public void run() {
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticket--+"张票");
}
- Thread.sleep(int)–线程休眠指定的时间,单位毫秒
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
注:
– 使用该方法可以模拟网络延时,放大问题以便让我们观察到
– 使用该方法还可以模拟倒计时
– 该方法不会释放锁
3. new Thread(myThread1,”小明”)–构造函数,第二个参数为线程的名字
public static void main(String[] args) {
MyThread1 myThread1=new MyThread1();
new Thread(myThread1,"小明").start();
new Thread(myThread1,"小红").start();
new Thread(myThread1,"小东").start();
}
- join()–相当于插队,必须该线程执行完才能张执行其他的线程
public class Yield implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i==0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("插队线程======="+i);
}
}
public static void main(String[] args) {
Yield yield1=new Yield();
Thread thread1=new Thread(yield1);
thread1.start();
for (int i = 0; i < 500; i++) {
if(i==200){
try {
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("main线程======"+i);
}
}
}
- yield()–暂停当前线程,并执行其他线程,也叫做礼让线程
public class Yield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();
System.out.println(Thread.currentThread().getName()+"线程结束执行");
}
public static void main(String[] args) {
Yield yield1=new Yield();
new Thread(yield1,"a").start();
new Thread(yield1,"b").start();
}
}
注:
– 礼让不一定成功,相当于重新竞争
6. interrupt()–中断线程,不建议使用这种方式
7. isAlive()–测试线程是否处于活动状态
8. getStates()–得到线程的状态
Yield yield1=new Yield();
Thread thread1=new Thread(yield1);
System.out.println(thread1.getState());
thread1.start();
System.out.println(thread1.getState());
for (int i = 0; i < 10; i++) {
System.out.println(thread1.getState());
if(i==200){
try {
thread1.join();
System.out.println(thread1.getState());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(thread1.getState());
System.out.println("main线程======"+i);
}
}
- [
NEW
]
尚未启动的线程处于此状态。 - [
RUNNABLE
]
在Java虚拟机中执行的线程处于此状态。 - [
BLOCKED
]
被阻塞等待监视器锁定的线程处于此状态。 - [
WAITING
]
正在等待另一个线程执行特定动作的线程处于此状态。 - [
TIMED_WAITING
]
正在等待另一个线程执行动作达到指定等待时间的线程处于此状态。 - [
TERMINATED
]
已退出的线程处于此状态。 - 线程停止方法:
- 建议让线程正常终止,利用次数,不建议死循环
- 建议使用一个标志位,去设置一个flag
- 不要使用stop(),destory()等过时或者jdk不建议的方法
- 线程的优先级
- getPriority()–得到线程的优先级
- setPriority(int)–设置线程的优先级 注:
- 主线程的优先级不能更改,使用默认的优先级5
- 先设置优先级再启动
- 优先级大只是被调用的概率大,不一定优先级高的就先被调用
- 优先级范围为1~10
- 守护线程:线程分为用户线程和守护线程,JVM必须等待用户线程执行完成,而不必等待守护线程进行完毕,通过setDaemon(true)设置线程为守护线程,默认为false
5.1、了解线程同步
问题:当多个线程访问一个资源的时候,就会出现线程不安全的问题,导致资源的变化出现不一致!!
解决:通过锁和队列的机制解决
具体:
- 每一个对象都有一个锁,线程必须拿到该对象的锁才可以对该资源进行操作
- 当有多个线程访问同一个对象时,就会形成一个队列
- 当线程访问该对象的时候,如果该对象有锁,那么线程拿到对象的锁
- 后续线程按照一定的顺序去访问这个对象,访问的时候会去检查该对象是否有锁,如果没有则说明上一个线程还没有操作完毕,也就是还没有释放该对象的锁,即该等待的线程还不能操作该对象
弊端:
- 虽然解决了安全的问题,但是使用锁以及排队会降低性能
- 如果出现优先级高的线程等待优先级低的线程,那么可能会出现优先级倒置的情况
5.2、代码模拟
5.3、Lock锁
5.4、死锁
产生情况:当有多个线程相互拿着对方资源,并且需要获得对方的资源才能释放锁的时候,就会产生死锁,因为都需要彼此的资源来释放自己的资源,导致资源一直不能被释放进而程序卡死,出现死锁的情况!!!
死锁的四个必要条件
解决死锁的4种基本方法
Original: https://www.cnblogs.com/xiaoye-Blog/p/16608220.html
Author: 小也取不到名字
Title: 多线程基础知识!!!
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/590014/
转载文章受原作者版权保护。转载请注明原作者出处!