Java多线程

什么是线程

进程是指可执行程序放在计算机存储器的一个指令序列,他是一个动态执行的过程。
线程是比进程还要小的单位,一个进程包含多个线程,线程可以看做一个子程序

Thread类和Runnable接口介绍

《Thread类常用构造方法,位于java.lang包》
构造方法
Thread()
创建一个线程对象
Thread(String name)
创建一个具有指定名称的线程对象
Thread(Runnable target)
创建一个基于Runnable接口实现类的线程对象
Thread(Runnable target, String name)
创建一个基于Runnable接口实现类,并且具有指定民称的线程对象
《Thread类常用方法》
public void run():线程相关的代码写在该方法中,一般需要重写
public void start():启动线程的方法
public static void sleep(long m):线程休眠m毫秒的方法
public void join():优先执行调用join()方法的线程
《Runnable接口》
只有一个方法run();
Runnable是Java中用以实现线程的接口
任何实现线程功能的类都必须实现该接口

通过Thread类创建线程(上)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.imooc.thread;
class MyThread extends Thread{
public void run(){
System.out.println(getName()+"该线程正在执行!");
}
}
public class ThreadTest {

public static void main(String[] args) {
//System.out.println("主线程1");
MyThread mt=new MyThread();
mt.start();//启动线程
mt.start();
//System.out.println("主线程2");

}

}

1、一个java源文件中,可以并存多个独立的类(非内部类),但是只能有一个public类,且public类类名与文件名相同。
2、启动线程:
MyThread mt = new MyThread();
mt.start();
注:一个线程只能启动一次,start()只能调用一次。多次调用会产生IllegalThreadStateException异常。
3、主方法本身也会产生一个线程。其和其他线程运行顺序是随机的。

通过Thread类创建线程(下)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.imooc.thread1;
class MyThread extends Thread{
public MyThread(String name){
super(name);
}
public void run(){
for(int i=1;i<=10;i++){
System.out.println(getName()+"正在运行"+i);
}
}
}
public class ThreadTest {

public static void main(String[] args) {
MyThread mt1=new MyThread("线程1");
MyThread mt2=new MyThread("线程2");
mt1.start();
mt2.start();
}

}

通过此例子可以看出,线程获得CPU的使用权是随机的

实现Runnable接口创建线程

通过实现Runnable接口的方式来实现线程:(现实工作中用的更多)
存在这个方式的原因:
1:如果一个类继承了其他类,就没办法继承Thread类了。只有实现runnable来创建线程;
2:thread中有很多的方法,不打算继承除了run方法外的其他方法,那么只实现Runnable后重写run方法就行了。
使用方式:在一个类中实现接口,然后根据需求重写run方法。
在调用时,需要先实例化实现run方法的类a,再把a做参数实例化Thread类b。然后用b.start()来启动线程。
这里的Runnable对象可以被多个线程公用,即多个Thread实例化都可以调用同一个对象作为参数。
注:run方法中很大概率使用Thread.currentThread().getName()来获取线程名。(因为Runnable接口中只有一个run方法);

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
package com.imooc.runnable;

class PrintRunnable implements Runnable {
int i = 1;
@Override
public void run() {

while (i <= 10)
System.out.println(Thread.currentThread().getName() + "正在运行" + (i++));
}

}

public class Test {

public static void main(String[] args) {
PrintRunnable pr = new PrintRunnable();
Thread t1 = new Thread(pr);
t1.start();
//PrintRunnable pr1 = new PrintRunnable();
Thread t2 = new Thread(pr);
t2.start();

}

}

线程的状态和生命周期

sleep方法的使用

sleep:
是thread的方法:public static void sleep(long millis)
作用是让正在执行的线程休眠指定的毫秒数,之后重新变为可运行状态。参数是休眠的时间,单位是毫秒。
在run方法中调用时,使用Thread.sleep(xxx);
调用sleep()方法,需要进行异常处理。调用try catch。

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
package com.imooc.sleep;

class MyThread implements Runnable{

@Override
public void run() {
for(int i=1;i<=30;i++){
System.out.println(Thread.currentThread().getName()+"执行第"+i+"次!");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
public class SleepDemo {

public static void main(String[] args) {
MyThread mt=new MyThread();
Thread t=new Thread(mt);
t.start();
Thread t1=new Thread(mt);
t1.start();
}

}

编程练习1

利用线程输出“a~z”的26个字母(横向输出),要求每隔一秒钟输出一个字母

效果图:

(每隔一秒钟输出一个字母)

任务要求:

  1. 创建实现类Letter,它实现Runnable接口

    定义一个char类型的数组letter[ ]来存放26个字母

方法:1)创建无参构造方法对数组中元素进行循环赋值。

​ 2)重写run( )方法,再建立一个循环,循环中实现每隔一秒打印输出一个字母。

​ 2、创建测试类,创建Letter类的对象,再通过Letter类的对象创建线程类的对象,然后启动线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package me.feihong.thread;

class Letter implements Runnable{
char[] letter=new char[26];
public Letter() {
for(int i=0;i<letter.length;i++) {
letter[i]=(char)(97+i);
}
}
@Override
public void run() {
for(int i=0;i<letter.length;i++) {
System.out.print(letter[i]+" ");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

}
1
2
3
4
5
6
7
8
9
10
11
12
package me.feihong.thread;

public class words {

public static void main(String[] args) {
// TODO Auto-generated method stub
Letter le=new Letter();
Thread t1=new Thread(le);
t1.start();
}

}

join方法的使用

join方法的第二种用法,当输入的毫秒时间结束时,使用join()方法的线程不管是否运行结束,CPU继续执行其他线程

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
package com.imooc.join;

class MyThread extends Thread{
public void run(){
for(int i=1;i<=500;i++)
System.out.println(getName()+"正在执行"+i+"次!");
}
}
public class JoinDemo {

public static void main(String[] args) {
MyThread mt=new MyThread();
mt.start();
try {
mt.join(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for(int i=1;i<=20;i++){
System.out.println("主线程运行第"+i+"次!");
}
System.out.println("主线程运行结束!");
}

}

线程的优先级

Java为线程提供了10个优先级
优先级可以用整数1-10表示,超过范围会抛出异常。
主线程默认优先级为5,数字越大优先级别越高
优先级常量
MAX_PRIORITY : 线程的最高优先级10
MIN_PRIORITY : 线程的最低优先级1
NORM_PRIORITY : 线程的默认优先级5

优先级相关的方法
public int getPriority() 获取线程优先级的方法
public void setPriority(int newPriority) 设置线程优先级的方法

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
package com.imooc.priority;

class MyThread extends Thread{
private String name;
public MyThread(String name){
this.name=name;
}
public void run(){
for(int i=1;i<=50;i++){
System.out.println("线程"+name+"正在运行"+i);
}
}
}
public class PriorityDemo {

public static void main(String[] args) {
//获取主线程的优先级
int mainPriority=Thread.currentThread().getPriority();
//System.out.println("主线程的优先级为:"+mainPriority);
MyThread mt1=new MyThread("线程1");
MyThread mt2=new MyThread("线程2");
//mt1.setPriority(10);
mt1.setPriority(Thread.MAX_PRIORITY);
mt2.setPriority(Thread.MIN_PRIORITY);
mt2.start();
mt1.start();
//System.out.println("线程1的优先级为:"+mt1.getPriority());
}

}

线程同步

多线程的运行问题
1.各个线程是通过竞争cpu时间而获得运行机会的
2.各线程什么时候得到cpu时间,占用多久,是不可预测的
3.一个正在运行着的线程在什么地方被暂停是不确定的
synchronized关键字,可以用在:成员方法,静态方法,语句块,将其锁定,确保共享对象在同一时刻只能被一个线程访问,这种机制称为线程的同步

银行存取款案例:

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package com.imooc.bank;

public class Bank {
private String account;// 账号
private int balance;// 账户余额

public Bank(String account, int balance) {
this.account = account;
this.balance = balance;
}

public String getAccount() {
return account;
}

public void setAccount(String account) {
this.account = account;
}

public int getBalance() {
return balance;
}

public void setBalance(int balance) {
this.balance = balance;
}

@Override
public String toString() {
return "Bank [账号:" + account + ", 余额:" + balance + "]";
}

// 存款
public synchronized void saveAccount() {

// 获取当前的账号余额
int balance = getBalance();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改余额,存100元
balance += 100;
// 修改账户余额
setBalance(balance);
// 输出存款后的账户余额
System.out.println("存款后的账户余额为:" + balance);
}

public void drawAccount() {
synchronized (this) {
// 在不同的位置处添加sleep方法

// 获得当前的帐户余额
int balance = getBalance();
// 修改余额,取200
balance = balance - 200;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 修改帐户余额
setBalance(balance);
System.out.println("取款后的帐户余额:" + balance);
}

}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
package com.imooc.bank;
//取款
public class DrawAccount implements Runnable{
Bank bank;
public DrawAccount(Bank bank){
this.bank=bank;
}
@Override
public void run() {
bank.drawAccount();
}

}
1
2
3
4
5
6
7
8
9
10
11
package com.imooc.bank;
//存款
public class SaveAccount implements Runnable{
Bank bank;
public SaveAccount(Bank bank){
this.bank=bank;
}
public void run(){
bank.saveAccount();
}
}
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
package com.imooc.bank;

public class Test {

public static void main(String[] args) {
// 创建帐户,给定余额为1000
Bank bank=new Bank("1001",1000);
//创建线程对象
SaveAccount sa=new SaveAccount(bank);
DrawAccount da=new DrawAccount(bank);
Thread save=new Thread(sa);
Thread draw=new Thread(da);
save.start();
draw.start();
try {

draw.join();
save.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(bank);
}

}

线程间通信

线程见通信:
三个Object类的方法:(都被继承了,可直接使用)
wait()方法:中断方法的执行,使线程处于等待状态:(阻塞);
notify()方法:唤醒处于等待的某一个线程,使其进入可运行状态;
notifyAll()方法:唤醒所有的处于等待的线程,进入可运行状态;一般使用notifyAll()方法比较多。
wait方法需要进行异常处理;

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.imooc.queue;

public class Consumer implements Runnable{
Queue queue;
Consumer(Queue queue){
this.queue=queue;
}

@Override
public void run() {
while(true){
queue.get();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.imooc.queue;

public class Producer implements Runnable{
Queue queue;
Producer(Queue queue){
this.queue=queue;
}

@Override
public void run() {
int i=0;
while(true){
queue.set(i++);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

}

}
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
37
package com.imooc.queue;

public class Queue {
private int n;
boolean flag=false;

public synchronized int get() {
if(!flag){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("消费:"+n);
flag=false;//消费完毕,容器中没有数据
notifyAll();
return n;
}

public synchronized void set(int n) {
if(flag){
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("生产:"+n);
this.n = n;
flag=true;//生产完毕,容器中已经有数据
notifyAll();
}

}
1
2
3
4
5
6
7
8
9
10
11
package com.imooc.queue;

public class Test {

public static void main(String[] args) {
Queue queue=new Queue();
new Thread(new Producer(queue)).start();
new Thread(new Consumer(queue)).start();
}

}
-------------本文结束感谢您的阅读-------------