Java多线程-1

1 创建多线程

创建多线程一共有两种方式。一个是继承Thread类,另一个是实现Runable接口。其原理是一样的,本质上都是实现了Runable接口。因为Thread类就是实现了Runable接口。

1 实现Runable接口

1
2
3
4
5
6
public class thread1 implements Runnable{
@Override
public void run() {
//线程中将要执行操作
}
}

2 继承Thread类

1
2
3
4
5
6
public class thread1 extends Thread{
@Override
public void run() {
//线程中将要执行操作
}
}

2 启动线程

看下面,分别为两种不同创建线程方式的启动方法。可以看出在主方法中,最好都用了start方法启动了该线程。但是线程对象的创建方式却不同,Runable接口的是向Thread中注入了参数也就是下面的类t,而Thread类却直接创建了对象t。这是因为,Thread类已经实现了Runable接口,其实本质都是实现了Runable接口。

为啥线程开启不是用run方法,而是start方法呢?这是因为,run方法其实就是一个定义在Runab接口的普通方法,如果直接调用run方法,就不能直接开启线程,直接在main方法的主线程中执行。start方法其本质是调用了底层start0方法来开启线程。

1 Runable

1
2
3
4
5
6
7
8
9
10
11
12
13
public class thread {
public static void main(String[] args) {
t t = new t();
Thread thread = new Thread(t);
thread.start();
}
}
class t implements Runnable{
@Override
public void run() {
//线程执行的操作
}
}

2 Thread

1
2
3
4
5
6
7
8
9
10
11
12
public class thread {
public static void main(String[] args) {
t t = new t();
t.start();
}
}
class t extends Thread{
@Override
public void run() {
//线程执行的操作
}
}

3 小案例

在银行中存放了10000元。有三个人分别取出里面的钱,一次取五百直到取完为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class thread {
public static void main(String[] args) {
t t = new t();
Thread thread1 = new Thread(t);
Thread thread2 = new Thread(t);
Thread thread3 = new Thread(t);
thread1.start();
thread2.start();
thread3.start();
}
}
class t implements Runnable{
private static int money = 10000;
@Override
public void run() {
while (money >= 0){
money = money-500;
//Thread.currentThread().getName()获取当前线程的名字
System.out.println(Thread.currentThread().getName() + "取走500还剩" + money );
}
}
}

但是在运行的结果中,我们看到有些顺序反了,按理说不应该出现啊。而且还出现了,银行余额为负数,这不应该啊。其实这就是线程不安全,多个线程同时访问一个资源造成的。在后面我们会解决他的。

img

4 线程常用方法

1 线程名字

主线程的名字为main,后面的线程命为Thread-0,Thread-1…..

获取线程名字

1
2
3
public final String getName() {
return name;
}

设置线程名字

1
2
3
4
5
6
7
8
9
10
public final synchronized void setName(String name) {
checkAccess();
if (name == null) {
throw new NullPointerException("name cannot be null");
}
this.name = name;
if (threadStatus != 0) {
setNativeName(name);
}
}

2 线程优先级

线程的默认优先级为5,最高为10,最低为1。

img

获取当前优先级

1
2
3
public final int getPriority() {
return priority;
}

设置优先级

1
2
3
4
5
6
7
8
9
10
11
12
13
public final void setPriority(int newPriority) {
ThreadGroup g;
checkAccess();
if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) {
throw new IllegalArgumentException();
}
if((g = getThreadGroup()) != null) {
if (newPriority > g.getMaxPriority()) {
newPriority = g.getMaxPriority();
}
setPriority0(priority = newPriority);
}
}

3 其他

sleep(long n)

让线程休眠,单位为毫秒

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void sleep(long millis, int nanos)
throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException(
"nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++;
}
sleep(millis);
}

join()

让当前线程立刻执行,直到执行完毕才能执行其他线程。

1
2
3
public final void join() throws InterruptedException {
join(0);
}

yield()

让出当前线程的cpu,当时不一定让出。

1
public static native void yield();

5 守护线程

守护线程是指为其他线程服务的线程。在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出。因此JVM退出时,不必关心守护线程是否已结束。最经典的就是java的垃圾回收线程。

设置守护线程一定得在开起线程之前。

1
2
3
Thread t = new Thread(t);
t.setDaemon(true);
t.start();

6 线程的六种状态

状态 解释
NEW 尚未启动的线程状态,即线程创建,还未调用start方法
RUNNABLE 就绪状态(调用start,等待调度)+正在运行
WAITING 等待状态的线程正在等待另一线程执行特定的操作(如notify)
TIME_WAITING 具有指定等待时间的等待状态
BLOCKED 等待监视器锁时,陷入阻塞状态
TERMINATED 线程完成执行,终止状态

下面为图示

img

其中RUNABLE状态又分为就绪状态和运行状态,所有有的书本上说线程有七种状态。

线程的主要路径为new—->runnable—>terminated;而其他的三个状态都是在围绕着runnable。

7 synchronized

线程同步机制是一套用于协调线程间的数据访问及活动的机制,该机制用于保障线程安全以及实现这些线程的共同目标

synchronized便常用于线程同步。有两种用法

修饰部分代码

修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;

1
2
3
synchronized (object o) {
//todo
}

修饰一个方法

  • 修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
  • 修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
1
2
3
4
public synchronized void method()
{
// todo
}
  • 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  • 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制

8 案例完善

只需要要上把锁就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class thread {
public static void main(String[] args) {
t t = new t();
Thread thread1 = new Thread(t);
Thread thread2 = new Thread(t);
Thread thread3 = new Thread(t);
thread1.start();
thread2.start();
thread3.start();

System.out.println(thread1.getState());
System.out.println(thread2.getState());
System.out.println(thread3.getState());
}
}

class t implements Runnable {
private static int money = 10000;
private Object o = new Object();

@Override
public void run() {
synchronized (o) {
while (money > 0) {
money = money - 500;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//Thread.currentThread().getName()获取当前线程的名字
System.out.println(Thread.currentThread().getName() + "取走500还剩" + money);
}
}
}
}

如果有什么,可以通过下面的邮箱和我联系!!!

img