多线程编程的目的,就是"最大限度地利用CPU资源",当某一线程的处理不需要占用CPU而只和I/O等资源打交道时,让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程编程的最终目的。
Java多线程的优点就在于取消了主循环/轮询机制。一个线程可以暂停而不影响程序的其他部分。例如,当一个线程从网络读取数据或等待用户输入时产生的空闲时间可以被利用到其他地方。多线程允许活的循环在每一帧间隙中沉睡一秒而不暂停整个系统。
public class MyThread extends Thread {
public void run() {
… …
}
}
MyThread t = new MyThread();
t. start();
package JavaBase.multithread;
public class ThreadTest {
public static void main(String[] args) {
//声明线程对象
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
//线程启动后就不受控制了,执行是随机的。
thread1.start();
thread2.start();
//如果调用run方法,就不会以线程的方式运行了,会把它当作一个类来运行。
}
}
//继承Thread,并重写run方法,绝对不要重写start方法
class Thread1 extends Thread{
@Override
public void run(){
for (int i = 0; i < 100; i++) {
System.out.println("hello world:" + i);
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("welcome:" + i);
}
}
}
结果是:
welcome:0
welcome:1
welcome:2
welcome:3
welcome:4
hello world:0
welcome:5
hello world:1
welcome:6
hello world:2
welcome:7
hello world:3
welcome:8
hello world:4
welcome:9
welcome:10
welcome:11
welcome:12
welcome:13
welcome:14
welcome:15
welcome:16
hello world:5
welcome:17
welcome:18
welcome:19
hello world:6
hello world:7
hello world:8
welcome:20
welcome:21
welcome:22
welcome:23
welcome:24
welcome:25
welcome:26
welcome:27
welcome:28
welcome:29
welcome:30
welcome:31
welcome:32
welcome:33
welcome:34
welcome:35
welcome:36
welcome:37
welcome:38
welcome:39
welcome:40
welcome:41
welcome:42
hello world:9
welcome:43
welcome:44
welcome:45
welcome:46
welcome:47
hello world:10
welcome:48
welcome:49
welcome:50
welcome:51
welcome:52
welcome:53
welcome:54
welcome:55
welcome:56
welcome:57
welcome:58
welcome:59
hello world:11
welcome:60
welcome:61
welcome:62
welcome:63
welcome:64
welcome:65
welcome:66
welcome:67
welcome:68
welcome:69
hello world:12
hello world:13
hello world:14
hello world:15
hello world:16
hello world:17
hello world:18
welcome:70
hello world:19
hello world:20
hello world:21
hello world:22
welcome:71
hello world:23
hello world:24
hello world:25
hello world:26
hello world:27
hello world:28
hello world:29
hello world:30
hello world:31
hello world:32
hello world:33
hello world:34
hello world:35
hello world:36
hello world:37
hello world:38
hello world:39
welcome:72
hello world:40
hello world:41
hello world:42
hello world:43
hello world:44
hello world:45
hello world:46
hello world:47
hello world:48
hello world:49
hello world:50
hello world:51
hello world:52
welcome:73
hello world:53
hello world:54
hello world:55
hello world:56
welcome:74
welcome:75
welcome:76
welcome:77
welcome:78
welcome:79
welcome:80
welcome:81
hello world:57
welcome:82
welcome:83
welcome:84
welcome:85
hello world:58
welcome:86
hello world:59
hello world:60
hello world:61
hello world:62
hello world:63
hello world:64
hello world:65
hello world:66
hello world:67
hello world:68
hello world:69
hello world:70
hello world:71
hello world:72
hello world:73
hello world:74
hello world:75
hello world:76
hello world:77
hello world:78
hello world:79
hello world:80
hello world:81
hello world:82
hello world:83
hello world:84
hello world:85
hello world:86
hello world:87
hello world:88
hello world:89
hello world:90
welcome:87
hello world:91
hello world:92
hello world:93
hello world:94
hello world:95
hello world:96
hello world:97
hello world:98
hello world:99
welcome:88
welcome:89
welcome:90
welcome:91
welcome:92
welcome:93
welcome:94
welcome:95
welcome:96
welcome:97
welcome:98
welcome:99
将我们希望线程执行的代码放到 run 方法中,然后通过 start 方法来启动线程, start方法首先为线程的执行准备好系统资源,然后再去调用 run 方法。 当某个类继承了Thread 类之后,该类就叫做一个线程类。
1. Thread 类也实现了 Runnable 接口,因此实现了 Runnable
2. 当生成一个线程对象时,如果没有为其设定名字,那么线形式: Thread-number,该 number 将是自动增加的,并被(因为它是 static 的成员变量)。
3. 当使用第一种方式来生成线程对象时,我们需要重写 run方法此时什么事情也不做。
4. 当使用第二种方式来生成线程对象时,我们需要实现 Runnable 接口的 run 方法,然后使用 new Thread(new MyThread())(假如 MyThread 已经实现了 Runnable 接口) 来生成线程对象,这时的线程对象的 run 方法就会调用 MyThread 类的 run 方法,这样我们自己编写的 run 方法就执行了。
class MyRunner implements Runnable {
public void run() {
…
}
}
MyRunner r = new MyRunner();
Thread t = new Thread( ThreadGroup group, Runnable target, String name);
例如: Thread t = new Thread( r, "aa");
package JavaBase.multithread;
public class ThreadTest2 {
public static void main(String[] args) {
/*Thread thread = new Thread(new MyThread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("hello:" + i);
}
}
});
thread.start();*/
Thread thread = new Thread(new MyThread());
thread.start();
Thread thread1 = new Thread(new MyThread2());
thread1.start();
}
}
class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("hello:" + i);
}
}
}
class MyThread2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("welocme:" + i);
}
}
}
结果是:
hello:0
hello:1
hello:2
hello:3
hello:4
hello:5
hello:6
hello:7
hello:8
hello:9
hello:10
hello:11
hello:12
hello:13
hello:14
hello:15
hello:16
hello:17
hello:18
hello:19
hello:20
hello:21
hello:22
hello:23
hello:24
hello:25
hello:26
hello:27
hello:28
hello:29
hello:30
hello:31
hello:32
welocme:0
welocme:1
welocme:2
welocme:3
welocme:4
welocme:5
welocme:6
welocme:7
welocme:8
welocme:9
welocme:10
hello:33
welocme:11
welocme:12
welocme:13
welocme:14
welocme:15
welocme:16
welocme:17
welocme:18
hello:34
hello:35
hello:36
hello:37
hello:38
hello:39
hello:40
hello:41
hello:42
hello:43
hello:44
welocme:19
welocme:20
hello:45
hello:46
hello:47
hello:48
hello:49
hello:50
hello:51
hello:52
hello:53
hello:54
hello:55
hello:56
hello:57
hello:58
hello:59
hello:60
welocme:21
welocme:22
welocme:23
welocme:24
hello:61
hello:62
hello:63
hello:64
hello:65
hello:66
hello:67
hello:68
hello:69
hello:70
hello:71
hello:72
hello:73
hello:74
hello:75
hello:76
hello:77
welocme:25
hello:78
hello:79
hello:80
hello:81
hello:82
hello:83
hello:84
hello:85
hello:86
welocme:26
welocme:27
hello:87
hello:88
hello:89
hello:90
welocme:28
welocme:29
welocme:30
hello:91
welocme:31
welocme:32
welocme:33
welocme:34
welocme:35
welocme:36
welocme:37
welocme:38
welocme:39
welocme:40
welocme:41
welocme:42
welocme:43
welocme:44
welocme:45
welocme:46
welocme:47
welocme:48
welocme:49
welocme:50
welocme:51
welocme:52
welocme:53
welocme:54
welocme:55
welocme:56
welocme:57
welocme:58
welocme:59
hello:92
hello:93
hello:94
hello:95
hello:96
hello:97
hello:98
hello:99
welocme:60
welocme:61
welocme:62
welocme:63
welocme:64
welocme:65
welocme:66
welocme:67
welocme:68
welocme:69
welocme:70
welocme:71
welocme:72
welocme:73
welocme:74
welocme:75
welocme:76
welocme:77
welocme:78
welocme:79
welocme:80
welocme:81
welocme:82
welocme:83
welocme:84
welocme:85
welocme:86
welocme:87
welocme:88
welocme:89
welocme:90
welocme:91
welocme:92
welocme:93
welocme:94
welocme:95
welocme:96
welocme:97
welocme:98
welocme:99
总结:
public class MyThread implements Runnable{
private boolean flag=true;
public void run()
{ while (flag)
{…}
}
public void stopRunning()
{ flag=false;}
}
public class ControlThread
{ private Runnable r=new MyThread();
private Thread t=new Thread(r);
public void startThread()
{ t.start(); }
publi void stopThread()
{ r.stopRunning();}
}
线程的生命周期: 一个线程从创建到消亡的过程。
线程的生命周期可分为四个状态:
当用new操作符创建一个新的线程对象时,该线程处于创建状态。
处于创建状态的线程只是一个空的线程对象,系统不为它分配资源
执行线程的start()方法将为线程分配必须的系统资源,安排其运行,并调用线程体—run()方法,这样就使得该线程处于可行( Runnable )状态。
这一状态并不是运行中状态(Running ),因为线程也许实际上并未真正运行。
当发生下列事件时,处于运行状态的线程会转入到不可运行状态。
• 调用了sleep()方法,传入的参数是毫秒;
• 线程调用wait方法等待特定条件的满足
• 线程输入/输出阻塞
• 处于睡眠状态的线程在指定的时间过去后
• 如果线程在等待某一条件,另一个对象必须通过notify()或notifyAll()方法通知等待线程条件的改变
• 如果线程是因为输入/输出阻塞,等待输入/输出完成
当线程的run方法执行结束后,该线程自然消亡。(线程死了不能复生,也就是说消亡后不能再start()了)
package JavaBase.multithread;
public class ThreadTest3 {
public static void main(String[] args) {
Runnable runnable = new HelloThread();
Thread thread = new Thread(runnable);
Thread thread1 = new Thread(runnable);
thread.start();
thread1.start();
}
}
class HelloThread implements Runnable{
//如果我们使用成员变量i,会输出50个,因为一个对象共用一个成员变量
int i;
@Override
public void run() {
//如果我们使用局部变量i,会输出2个50,因为局部变量是每个线程一个,互不影响
// int i = 0;
while (true) {
System.out.println("number:" + i++);
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
if (50 == i) {
break;
}
}
}
}
在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源。必须对这种潜在资源冲突进行预防。
解决方法:在线程使用一个资源时为其加锁即可。访问资源的第一个线程为其加上锁以后,其他线程便不能再使用那个资源,除非被解锁。
package JavaBase.multithread;
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
Thread thread1 = new MoneyThread(bank);//柜台
Thread thread2 = new MoneyThread(bank);//提款机
thread1.start();
thread2.start();
}
}
class Bank{
private static int money = 1000;
public synchronized int getMoney(int number) {
if (number < 0) {
return -1;
} else if (number > money) {
return -2;
} else if (money < 0) {
return -3;
} else {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money -= number;
System.out.println("left money:" + money);
return number;
}
}
}
class MoneyThread extends Thread{
private Bank bank;
public MoneyThread(Bank bank) {
this.bank = bank;
}
@Override
public void run() {
System.out.println(bank.getMoney(800));
}
}
结果是:
left money:200
800
-2
package JavaBase.multithread;
public class ThreadTest4 {
public static void main(String[] args) {
Example example = new Example();
Thread thread1 = new TheThread(example);
example = new Example();
Thread thread2 = new TheThread2(example);
thread1.start();
thread2.start();
}
}
class Example{
public synchronized void execute(){
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello:" + i);
}
}
public synchronized void execute2() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("world:" + i);
}
}
}
class TheThread extends Thread{
private Example example;
public TheThread(Example example) {
this.example = example;
}
@Override
public void run() {
this.example.execute();
}
}
class TheThread2 extends Thread{
private Example example;
public TheThread2(Example example) {
this.example = example;
}
@Override
public void run() {
this.example.execute2();
}
}
结果是:
world:0
hello:0
hello:1
world:1
hello:2
world:2
hello:3
hello:4
world:3
world:4
hello:5
world:5
world:6
hello:6
hello:7
world:7
world:8
world:9
world:10
hello:8
world:11
hello:9
hello:10
hello:11
hello:12
world:12
world:13
hello:13
world:14
world:15
hello:14
hello:15
world:16
hello:16
hello:17
world:17
hello:18
world:18
hello:19
world:19
如果一个对象有多个 synchronized 方法,某一时刻某个线程已经进入到了某个synchronized 方法,那么在该方法没有执行完毕前,其他线程是无法访问该对象的任何 synchronized 方法的。
package JavaBase.multithread;
public class ThreadTest4 {
public static void main(String[] args) {
Example example = new Example();
Thread thread1 = new TheThread(example);
example = new Example();
Thread thread2 = new TheThread2(example);
thread1.start();
thread2.start();
}
}
class Example{
//如果只有一个static也是乱序的,因为他锁的是class对象,并没有锁这个对象本身
public synchronized static void execute(){
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello:" + i);
}
}
public synchronized static void execute2() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("world:" + i);
}
}
}
class TheThread extends Thread{
private Example example;
public TheThread(Example example) {
this.example = example;
}
@Override
public void run() {
this.example.execute();
}
}
class TheThread2 extends Thread{
private Example example;
public TheThread2(Example example) {
this.example = example;
}
@Override
public void run() {
this.example.execute2();
}
}
结果是:
hello:0
hello:1
hello:2
hello:3
hello:4
hello:5
hello:6
hello:7
hello:8
hello:9
hello:10
hello:11
hello:12
hello:13
hello:14
hello:15
hello:16
hello:17
hello:18
hello:19
world:0
world:1
world:2
world:3
world:4
world:5
world:6
world:7
world:8
world:9
world:10
world:11
world:12
world:13
world:14
world:15
world:16
world:17
world:18
world:19
如果某个 synchronized 方法是 static 的,那么当线程访问该方法时,它锁的并不是synchronized 方法所在的对象,而是 synchronized 方法所在的对象所对应的 Class 对象,因为 Java 中无论一个类有多少个对象,这些对象会对应唯一一个 Class 对象,因此当线程分别访问同一个类的两个对象的两个 static, synchronized 方法时,他们的执行顺序也是顺序的,也就是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
package JavaBase.multithread;
import java.io.ObjectInputStream;
public class ThreadTest5 {
public static void main(String[] args) {
Example2 example2 = new Example2();
Thread thread1 = new TheThread3(example2);
Thread thread2 = new TheThread4(example2);
thread1.start();
thread2.start();
}
}
class Example2{
private Object object = new Object();
public void execute(){
//synchronized块,传一个任意的对象,会将这个对象锁上
synchronized (this) {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello:" + i);
}
}
}
public void execute2() {
synchronized (this) {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long) (Math.random() * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("world:" + i);
}
}
}
}
class TheThread3 extends Thread{
private Example2 example;
public TheThread3(Example2 example) {
this.example = example;
}
@Override
public void run() {
this.example.execute();
}
}
class TheThread4 extends Thread{
private Example2 example;
public TheThread4(Example2 example) {
this.example = example;
}
@Override
public void run() {
this.example.execute2();
}
}
结果是:
hello:0
hello:1
hello:2
hello:3
hello:4
hello:5
hello:6
hello:7
hello:8
hello:9
hello:10
hello:11
hello:12
hello:13
hello:14
hello:15
hello:16
hello:17
hello:18
hello:19
world:0
world:1
world:2
world:3
world:4
world:5
world:6
world:7
world:8
world:9
world:10
world:11
world:12
world:13
world:14
world:15
world:16
world:17
world:18
world:19
synchronized 方法是一种粗粒度的并发控制,某一时刻,只能有一个线程执行该synchronized 方法:synchronized 块则是一种细粒度的并发控制,只会将块中的代码同步,位于方法内、 synchronized 块之外的代码是可以被多个线程同时访问到的。
public class MyStack {
int idx = 0;
char [] data = new char[ 6];
public synchronized void push( char c) {
data[ idx] = c;
idx++;
}
public synchronized char pop() {
idx--;
return data[ idx];
}
说明:
• 当synchronized方法执行完或发生异常时,会自动释放锁。
• 被synchronized保护的数据应该是私有(private)的。
线程间的相互作用:
• wait and notify
• The pools:
– Wait pool
– Lock pool
生产者和消费者问题。
死锁(deadlock)
wait 与 notify 方法都是定义在 Object 类中,而且是 final 的,因此会被所有的 Java类所继承并且无法重写。这两个方法要求在调用时线程应该已经获得了对象的锁,因此对这两个方法的调用需要放在 synchronized 方法或块当中。 当线程执行了 wait方法时,它会释放掉对象的锁。
另一个会导致线程暂停的方法就是 Thread 类的 sleep 方法,它会导致线程睡眠指定的毫秒数, 但线程在睡眠的过程中是不会释放掉对象的锁的。
例子 一个类里有1个整形变量,一个方法+1,一个方法-1,一个线程+1一个线程-1,声明2个+线程2个-线程,最后实现0和1的交替,我们通过这个例子,来熟悉wait和notify方法。
package JavaBase.multithread;
public class Sample {
private int number;
public synchronized void increase(){
//我用while而不用if,主要是因为,我们再wait的过程中不知道其他程序会进行什么操作。
while (0 != number) {
try {
//进入wait就会释放锁,别的线程就可以获取到对象的锁了
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
//随机调用正在wait的程序
notify();
}
public synchronized void decrease(){
while (0 == number){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
增加1
package JavaBase.multithread;
public class IncreaseThread extends Thread{
private Sample sample;
public IncreaseThread(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000) );
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.increase();
}
}
}
减少1
package JavaBase.multithread;
public class DecreaseThread extends Thread{
private Sample sample;
public DecreaseThread(Sample sample) {
this.sample = sample;
}
@Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep((long)(Math.random() * 1000) );
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.decrease();
}
}
}
package JavaBase.multithread;
public class MainTest {
public static void main(String[] args) {
Sample sample = new Sample();
Thread thread1 = new IncreaseThread(sample);
Thread thread2 = new DecreaseThread(sample);
Thread thread3 = new IncreaseThread(sample);
Thread thread4 = new DecreaseThread(sample);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
结果是:
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
1
0
所有线程都隶属于一个线程组。那可以是一个默认线程组,亦可是一个创建线程时明确指定的组。
说明:
– 在创建之初,线程被限制到一个组里,而且不能改变到一个不同的组。
– 若创建多个线程而不指定一个组,它们就会与创建它的线程属于同一个组。