线程
概述
线程与进程:进程是一个应用程序执行一次的过程,windows系统下可以查看应用程序进程,在一次进程执行中可以分为多个线程的执行。多进程使用情况:i.需要同时执行多个任务。ii.实现需要等待的任务。iii.需要后台运行的程序。创建多线程的方式:i.继承Thread类。ii.实现Runnable接口。两种方式都需要重写run方法。多线程同时运行时会抢占cpu资源,输出时可能导致结果混乱。
线程的两种实现方法
代码实现
package com
.leeyu
.testthread
;
public class TestThread1 {
public static void main(String
[] args
) {
Thread thread1
=new Thread01();
Thread thread2
=new Thread(new Thread02());
thread1
.start();
thread2
.start();
for(int i
=0;i
<100;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
}
}
class Thread01 extends Thread{
@Override
public void run() {
for(int i
=0;i
<100;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
super.run();
}
}
class Thread02 implements Runnable{
@Override
public void run() {
for(int i
=0;i
<100;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
}
}
结果
----------------------
main
:0
main
:1 main
:2 main
:3 main
:4 main
:5
main
:6 main
:7 main
:8 main
:9 main
:10
main
:11 main
:12 main
:13 main
:14 main
:15
main
:16 main
:17 main
:18 main
:19 main
:20
main
:21 main
:22 main
:23 main
:24 main
:25
main
:26 main
:27 main
:28 main
:29 main
:30
main
:31 main
:32 main
:33 main
:34 main
:35
main
:36 main
:37 main
:38 main
:39 Thread
-1:0 Thread
-0:0
main
:40
main
:41 Thread
-1:1
Thread
-0:1 Thread
-0:2 Thread
-0:3 Thread
-0:4 Thread
-0:5
Thread
-0:6 Thread
-0:7 Thread
-0:8 Thread
-0:9 Thread
-1:2 Thread
-1:3 Thread
-1:4 main
:42 main
:43 Thread
-1:5
Thread
-1:6 Thread
-1:7 Thread
-1:8 Thread
-1:9 Thread
-1:10
Thread
-1:11 Thread
-1:12 Thread
-1:13 Thread
-1:14 Thread
-0:10
Thread
-1:15
main
:44 Thread
-1:16 Thread
-1:17 Thread
-0:11 Thread
-0:12 Thread
-1:18 main
:45 Thread
-1:19
main
:46 main
:47 main
:48 Thread
-0:13 Thread
-0:14 Thread
-0:15
main
:49 main
:50 Thread
-1:20
Thread
-0:16 Thread
-0:17 main
:51 Thread
-1:21 main
:52 Thread
-0:18 main
:53 Thread
-1:22 Thread
-1:23 main
:54 main
:55
Thread
-0:19 Thread
-0:20
Thread
-0:21 Thread
-0:22 main
:56 main
:57 main
:58 main
:59 main
:60
main
:61 main
:62 main
:63 Thread
-1:24 Thread
-1:25 main
:64 main
:65
main
:66 main
:67 main
:68 main
:69 main
:70
main
:71 main
:72 main
:73 main
:74 main
:75
main
:76 main
:77 main
:78 main
:79 main
:80 Thread
-0:23
Thread
-1:26 Thread
-1:27 Thread
-1:28 Thread
-1:29 Thread
-1:30
Thread
-1:31 Thread
-1:32 main
:81 main
:82 main
:83 Thread
-0:24 Thread
-0:25
Thread
-0:26 Thread
-0:27 Thread
-0:28 Thread
-0:29 Thread
-0:30
Thread
-0:31 Thread
-0:32 Thread
-0:33 main
:84 main
:85
main
:86 main
:87 main
:88 main
:89 main
:90
main
:91 Thread
-1:33 main
:92 main
:93 main
:94 main
:95
main
:96 main
:97 main
:98 main
:99 Thread
-0:34 Thread
-0:35 Thread
-1:34
Thread
-0:36 Thread
-0:37 Thread
-0:38 Thread
-0:39 Thread
-0:40
Thread
-0:41 Thread
-0:42 Thread
-0:43 Thread
-0:44 Thread
-0:45
Thread
-0:46 Thread
-0:47 Thread
-0:48 Thread
-0:49 Thread
-0:50
Thread
-0:51 Thread
-0:52 Thread
-0:53 Thread
-0:54 Thread
-0:55
Thread
-0:56 Thread
-0:57 Thread
-0:58 Thread
-0:59 Thread
-0:60
Thread
-0:61 Thread
-0:62 Thread
-0:63 Thread
-0:64 Thread
-0:65
Thread
-0:66 Thread
-0:67 Thread
-0:68 Thread
-0:69 Thread
-0:70
Thread
-0:71 Thread
-0:72 Thread
-0:73 Thread
-0:74 Thread
-0:75
Thread
-0:76 Thread
-0:77 Thread
-0:78 Thread
-0:79 Thread
-0:80
Thread
-0:81 Thread
-0:82 Thread
-0:83 Thread
-0:84 Thread
-0:85
Thread
-0:86 Thread
-0:87 Thread
-0:88 Thread
-0:89 Thread
-0:90
Thread
-0:91 Thread
-0:92 Thread
-0:93 Thread
-0:94 Thread
-0:95
Thread
-0:96 Thread
-0:97 Thread
-0:98 Thread
-0:99 Thread
-1:35
Thread
-1:36 Thread
-1:37 Thread
-1:38 Thread
-1:39 Thread
-1:40
Thread
-1:41 Thread
-1:42 Thread
-1:43 Thread
-1:44 Thread
-1:45
Thread
-1:46 Thread
-1:47 Thread
-1:48 Thread
-1:49 Thread
-1:50
Thread
-1:51 Thread
-1:52 Thread
-1:53 Thread
-1:54 Thread
-1:55
Thread
-1:56 Thread
-1:57 Thread
-1:58 Thread
-1:59 Thread
-1:60
Thread
-1:61 Thread
-1:62 Thread
-1:63 Thread
-1:64 Thread
-1:65
Thread
-1:66 Thread
-1:67 Thread
-1:68 Thread
-1:69 Thread
-1:70
Thread
-1:71 Thread
-1:72 Thread
-1:73 Thread
-1:74 Thread
-1:75
Thread
-1:76 Thread
-1:77 Thread
-1:78 Thread
-1:79 Thread
-1:80
Thread
-1:81 Thread
-1:82 Thread
-1:83 Thread
-1:84 Thread
-1:85
Thread
-1:86 Thread
-1:87 Thread
-1:88 Thread
-1:89 Thread
-1:90
Thread
-1:91 Thread
-1:92 Thread
-1:93 Thread
-1:94 Thread
-1:95
Thread
-1:96 Thread
-1:97 Thread
-1:98 Thread
-1:99
可以看出,线程执行顺序和main方法中调用顺序并不一致。
线程常用方法
1.获取线程id和名称:Thread.currentThread().getId();和Thread.currentThread().getName();2.优先级:获取:Thread.currentThread().getPriority();设置: Thread.currentThread().setPriority(10);3.线程状态:NEW:表示新建但是没有调用的状态; TERMINATED:表示运行结束之后的状态; RUNNABLE:表示正在执行run方法的状态; BLOCKED,WAITING,TIMED_WAITING:都表示阻塞状态。4.sleep方法,可以让线程休眠指定时间
package com
.leeyu
.testthread
;
import java
.util
.Date
;
public class TestThread2 {
public static void main(String
[] args
) {
Thread thread1
=new Thread(
new Runnable() {
public void run() {
Long start
=System
.currentTimeMillis();
try {
Thread
.sleep(3000);
} catch (InterruptedException e
) {
e
.printStackTrace();
}
Long end
=System
.currentTimeMillis();
Thread
.currentThread().setName("thread1");
for(int i
=1;i
<=10;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
System
.out
.println(end
-start
);
}
});
thread1
.start();
}
}
结果
-------------------------------
thread1
:1
thread1
:2
thread1
:3
thread1
:4
thread1
:5
thread1
:6
thread1
:7
thread1
:8
thread1
:9
thread1
:10
3005
为什么打印出来的时间是3005(ms)不是3000整,可能是cpu利用了5ms的时间执行了其他程序的线程,之后才空余出来。
5.join方法,可以让外部线程等待当前线程执行完毕之后在执行。(需要注意,join方法需要插入到run方法内,实际的执行方法之前。)
package com
.leeyu
.testthread
;
public class TestThread2 {
public static void main(String
[] args
) {
Thread thread1
=new Thread(
new Runnable() {
public void run() {
Thread
.currentThread().setName("thread1");
for(int i
=1;i
<=10;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
}
});
Thread thread2
=new Thread(
new Runnable(){
@Override
public void run() {
Thread
.currentThread().setName("thread2");
try {
thread1
.join();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
for(int i
=1;i
<=10;i
++) {
System
.out
.println(Thread
.currentThread().getName()+":"+i
);
}
}
});
thread2
.start();
thread1
.start();
}
}
结果
------------
thread1
:1
thread1
:2
thread1
:3
thread1
:4
thread1
:5
thread1
:6
thread1
:7
thread1
:8
thread1
:9
thread1
:10
thread2
:1
thread2
:2
thread2
:3
thread2
:4
thread2
:5
thread2
:6
thread2
:7
thread2
:8
thread2
:9
thread2
:10
多线程可能导致的问题
由于线程之间是共享数据的(这个特性的存在使得线程处理能力好),但是当不同线程尝试修改同一个数据时可能导致结果错误。当对于数据的操作不是原子操作时使用多线程修改就可能出错.
package com
.leeyu
.testthread
;
public class TestMutiThread {
private static int i
=0;
private static Runnable
getThread() {
return new Runnable(){
@Override
public void run() {
for(int num
=0;num
<5;num
++) {
i
++;
System
.out
.println("i=:"+i
);
}
}
};
}
public static void main(String
[] args
) {
Thread
[] mutiThread
=new Thread[3];
for(int n
=0;n
<mutiThread
.length
;n
++) {
mutiThread
[n
]=new Thread(getThread());
}
for(Thread n
:mutiThread
) {
n
.start();
}
}
}
结果
----------------
i
=:1
i
=:2
i
=:1
i
=:4
i
=:3
i
=:7
i
=:8
i
=:9
i
=:6
i
=:10
i
=:11
i
=:5
i
=:12
i
=:13
i
=:14
对于例中的代码来说,i++就是非原子操作。实现这个操作需要先获取i的值,然后计算i+1的值,最后赋给i,到此才算完成操作。这就导致当一个线程获取到i的值为1时另外一个线程也可以获取到值为1的i的值(当另外一个线程获取值得操作在第一个线程计算返回值之前完成的时候就会出现这种问题)。还可能因为数据内存的可见性问题导致结果错误,当访问一个数据时,可能直接从寄存器或者缓存中,也可能从内存中获取。修改变量时,也可能先改到缓存中,之后再同步到内存中。这就导致数据不同步。
synchronized修饰符
修饰类的实例方法,静态方法,代码块。加入synchronized修饰符的代码块就变成了原子操作。sychronized修饰的是同一对象(不是同一个类)中的方法,对于多个变量可以同时访问不同变量的相同的synchronized方法。修饰普通方法:得到的是对象锁。
package com
.leeyu
.testthread
;
public class TestSynchronized {
public static void main(String
[] args
) {
Thread5 thread5
= new Thread5();
Thread thread1
=new Thread(thread5
);
Thread thread2
=new Thread(thread5
);
thread1
.start();
thread2
.start();
}
}
class Thread5 implements Runnable{
static int i
=0;
private synchronized void increase() {
i
++;
System
.out
.println(Thread
.currentThread().getName()+"-i:"+i
);
}
@Override
public void run() {
for(int n
=0;n
<10;n
++) {
increase();
}
}
}
结果
----------------
Thread
-0-i
:1
Thread
-0-i
:2
Thread
-0-i
:3
Thread
-0-i
:4
Thread
-0-i
:5
Thread
-0-i
:6
Thread
-0-i
:7
Thread
-0-i
:8
Thread
-0-i
:9
Thread
-0-i
:10
Thread
-1-i
:11
Thread
-1-i
:12
Thread
-1-i
:13
Thread
-1-i
:14
Thread
-1-i
:15
Thread
-1-i
:16
Thread
-1-i
:17
Thread
-1-i
:18
Thread
-1-i
:19
Thread
-1-i
:20
修饰静态方法:注意和上方代码的区别(传入Thread构造器参数不同,下面代码是两个实例),但是由于synchronized修饰的是静态方法(属于类),所以得到的类锁。
package com
.leeyu
.testthread
;
public class TestSynchronized {
public static void main(String
[] args
) {
Thread thread1
=new Thread(new Thread5());
Thread thread2
=new Thread(new Thread5());
thread1
.start();
thread2
.start();
}
}
class Thread5 implements Runnable{
static int i
=0;
private synchronized static void increase() {
i
++;
System
.out
.println(Thread
.currentThread().getName()+"-i:"+i
);
}
@Override
public void run() {
for(int n
=0;n
<10;n
++) {
increase();
}
}
}
结果
---------------
Thread
-0-i
:1
Thread
-0-i
:2
Thread
-0-i
:3
Thread
-0-i
:4
Thread
-0-i
:5
Thread
-0-i
:6
Thread
-0-i
:7
Thread
-0-i
:8
Thread
-0-i
:9
Thread
-0-i
:10
Thread
-1-i
:11
Thread
-1-i
:12
Thread
-1-i
:13
Thread
-1-i
:14
Thread
-1-i
:15
Thread
-1-i
:16
Thread
-1-i
:17
Thread
-1-i
:18
Thread
-1-i
:19
Thread
-1-i
:20
修饰代码块:使用synchronized(){}来完成,会使不同线程之间争夺()中对象的锁。
package com
.leeyu
.testthread
;
public class TestSynchronized2 {
public static void main(String
[] args
) {
Thread6 thread
=new Thread6();
Thread thread1
=new Thread(thread
);
Thread thread2
=new Thread(thread
);
thread1
.start();
thread2
.start();
}
}
class Thread6 implements Runnable{
static int i
;
@Override
public void run() {
for(int n
=0;n
<10;n
++) {
synchronized(this){
i
++;
System
.out
.println(Thread
.currentThread().getName()+"I:"+i
);
}
}
}
}
结果
--------------------------------------
Thread
-0I
:1
Thread
-0I
:2
Thread
-0I
:3
Thread
-0I
:4
Thread
-0I
:5
Thread
-0I
:6
Thread
-0I
:7
Thread
-0I
:8
Thread
-0I
:9
Thread
-0I
:10
Thread
-1I
:11
Thread
-1I
:12
Thread
-1I
:13
Thread
-1I
:14
Thread
-1I
:15
Thread
-1I
:16
Thread
-1I
:17
Thread
-1I
:18
Thread
-1I
:19
Thread
-1I
:20
wait/notify控制线程
wait与notify需要配合synchronized使用,wait会释放当前线程占用的锁,notify通知处于WAITING状态的对象继续执行,参与争夺对象锁。(并不会立即释放锁,会等待synchronized代码执行完毕)使用wait和notify完成一个简单的生产消费模型。
package com
.leeyu
.testthread
;
import java
.util
.ArrayList
;
import java
.util
.List
;
public class TestThreadWN {
public static void main(String
[] args
) {
List
<String> list
=new ArrayList();
Thread producter
=new Thread(new Thread7(list
));
Thread consumer
=new Thread(new Thread8(list
));
producter
.start();
consumer
.start();
}
}
class Thread7 implements Runnable{
private List
<String> list
;
public Thread7(List
<String> list
) {
this.list
=list
;
}
@Override
public void run() {
synchronized(list
) {
for(int i
=0;i
<3;i
++) {
while(list
.size()<10) {
list
.add("str");
}
list
.notify();
System
.out
.println("producter notify");
System
.out
.println(list
.size());
try {
System
.out
.println("producter wait");
list
.wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
}
}
class Thread8 implements Runnable{
private List
<String> list
;
public Thread8(List
<String> list
) {
this.list
=list
;
}
@Override
public void run() {
synchronized(list
) {
for(int n
=0;n
<3;n
++) {
while(list
.size()>0) {
list
.remove(list
.size()-1);
}
list
.notify();
System
.out
.println("consumer notify");
System
.out
.println(list
.size());
try {
System
.out
.println("consumer wait");
list
.wait();
} catch (InterruptedException e
) {
e
.printStackTrace();
}
}
}
}
}
结果
--------------------
producter notify
10
producter wait
consumer notify
0
consumer wait
producter notify
10
producter wait
consumer notify
0
consumer wait
producter notify
10
producter wait
consumer notify
0
consumer wait