一.MyThread继承Thread父类
使用父类方法构造器。MyThread 继承 Thread执行父类Thread(String name)构造器,为当前线程设置名字了
public class MyThread extends Thread{
public MyThread(String name){
super(name); //1.执行父类Thread(String name)构造器,为当前线程设置名字了
}
@Override
public void run() {
//2.currentThread() 哪个线程执行它,它就会得到哪个线程对象。
Thread t = Thread.currentThread();
for (int i = 1; i <= 3; i++) {
//3.getName() 获取线程名称
System.out.println(t.getName() + "输出:" + i);
}
}
}
父类Thread(String name)构造器
1.线程与进程的区别:
二.创建线程的三种方式
1.extends Thread
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Value"+i);
}
}
public static void main(String[] args) {
MyThread t1=new MyThread();
t1.start();
}
}
2.实现implements Runnable 接口
通过implements Runnable接口,使线程的执行逻辑封装在实现了run()
方法的类中。以后,创建Thread类实例时,可将实现了Runnable接口的类的实例,即mr,传递给Thread的构造方法。
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("Value"+i);
}
}
public static void main(String[] args) {
MyRunnable mr=new MyRunnable();
Thread t1=new Thread(mr);
t1.start();
}
}
3.implements Callable 接口
Callable接口实际上是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更加强大的功能。
Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能。
Callable的call方法分可以抛出异常,而Runnable的run方法不能抛出异常。
Thread和Runnable的异同点
Thread 和 Runnable 的相同点:都是“多线程的实现方式”。
Thread 和 Runnable 的不同点:
Thread 是类,而Runnable是接口;如果有其他类需要继承的话肯定是用Runnable接口了。Thread本身是实现了Runnable接口的类。我们知道“一个类只能有一个父类,但是却能实现多个接口”,因此Runnable具有更好的扩展性。
此外,Runnable还可以用于“资源的共享”。即,多个线程都是基于某一个Runnable对象建立的,它们会共享Runnable对象上的资源。
Thread、Runnable、Callable三者特点\异同:
Thread:继承类实现,简单地定义一个线程,而不用担心单继承的限制,并且不需要共享数据。
Runnable:推荐。重写run()方法。接口实现,若需要定义线程的同时扩展其他类的功能,可在多个线程之间共享数据。可以定义一个类A实现Runnable接口;然后,通过new Thread(new A())等方式新建线程。
Callable:接口实现,重写call()方法。可以在任务结束的时候提供一个返回值,call方法可以抛出异常。
public class MyCallable implements Callable<Integer> {
@Override
public Integer call() {
int sum=0;
for (int i = 0; i < 10; i++) {
sum+=i;
}
return sum;
}
public static void main(String[] args) {
Callable<Integer>mc=new MyCallable();
FutureTask<Integer>ft=new FutureTask<>(mc);
Thread t1=new Thread(ft);
t1.start();
try {
Integer result=ft.get();
System.out.println("Sum from Callable"+result);
} catch (Exception e) {
e.printStackTrace();
}
}
}
三:线程安全
线程安全问题指的是,多个线程同时操作同一个共享资源的时候,可能会出现业务安全问题。
1.同步代码块
1.建议把共享资源作为锁对象, 不要将随便无关的对象当做锁对象
2.对于实例方法,建议使用this作为锁对象
3.对于静态方法,建议把类的字节码(类名.class)当做锁对象
//锁对象:必须是一个唯一的对象(同一个地址)
synchronized(锁对象){
//...访问共享数据的代码...
}
// 小明 小红线程同时过来的
public void drawMoney(double money) {
// 先搞清楚是谁来取钱?
String name = Thread.currentThread().getName();
// 1、判断余额是否足够
// this正好代表共享资源!
synchronized (this) {
if(this.money >= money){
System.out.println(name + "来取钱" + money + "成功!");
this.money -= money;
System.out.println(name + "来取钱后,余额剩余:" + this.money);
}else {
System.out.println(name + "来取钱:余额不足~");
}
}
}
2.同步方法
其实同步方法,就是把整个方法给锁住,一个线程调用这个方法,另一个线程调用的时候就执行不了,只有等上一个线程调用结束,下一个线程调用才能继续执行。
使用synchronized
修饰方法,确保在同一时间只有一个线程能够访问该方法。这种方式适用于整个方法需要被同步的情况
// 同步方法
public synchronized void increment() {
count++;
}
3.Lock锁
1.首先在成员变量位子,需要创建一个Lock接口的实现类对象(这个对象就是锁对象)
private final Lock lk = new ReentrantLock();
2.在需要上锁的地方加入下面的代码
lk.lock(); // 加锁
//...中间是被锁住的代码...
lk.unlock(); // 解锁
四:线程池的使用
五.死锁
死锁问题:
1: 某一个线程一直持有锁 不解锁 2: 两个线程之间互相持有对方需要的锁 两个线程都持有了一把锁 接着都需要再持有一把锁(也是对方目前持有的锁)才可以 执行任务
解决:
1: 不写死锁(拿到一把锁 不获取另一把锁) 2: 获取一把锁之后 尝试获取第二把锁,失败的话 立马放弃第一把锁,再重新自旋 获取第一把锁
public class TA extends Thread{
@Override
public void run() {
for (int i = 0; i < 3; i++) {
synchronized (Main.object1){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("TA - 持有 O1 Lock");
synchronized (Main.object2){
System.out.println("TA - 持有 O2 Lock");
Main.num++;
}
}
}
System.out.println("TA- end");
}
}
class RA implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (Main.object2){
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("RA - 持有 O2 Lock");
synchronized (Main.object1){
System.out.println("RA - 持有 O1 Lock");
Main.lock.lock();
Main.num++;
Main.lock.unlock();
}
}
}
System.out.println("RA- end");
}
}
class Main{
static int num=0;
static Object object1=new Object();
static Object object2=new Object();
static Lock lock=new ReentrantLock();
public static void main(String[] args) {
TA ta=new TA();
RA ra=new RA();
ta.start();
Thread t1=new Thread(ra);
t1.start();
try {
ta.join();
t1.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(num);
}
}