1:进程和线程的概念
进程是受操作系统管理的基本运行单元,可以把进程列表中的.exe程序理解成一个进程。
线程是进程中独立运行的子任务,比如,QQ.exe在运行的时候会有很多的子任务在运行,比如,下载文件线程,传输数据线程,发送表情线程,这些都可以看成是QQ.exe进程的子任务。
线程是异步的,不是同步的,不用按照排队顺序执行,而是根据CPU安排。
2:使用多线程
2.1:继承Thread类;
Thread类中的start方法是通知线程规划器,表示线程已经准备好了,等待调用线程对象的run()方法,让系统安排时间来调用Thread中的run()方法,具有异步的效果。如果使用thread.run()方法,那么就不是异步执行了,直接调用run方法是由main主线程来调用run()方法,这样就必须前面的代码执行完才能执行后面的代码。
2.2:实现Runnable接口;
因为java不支持多继承,所以可以采用实现Runnable接口的方法得到一个线程。在Thread类的构造方法中Thread(Runnable target) 和Thread(Runnable target,String name)可以传递Runnable接口。
public class MyRunnable implements Runnable{
public void run(){
system.out.println("创建");
}
}
public class Run(){
public static void main(String[] args){
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);//传递一个Runnable接口对象,得到一个Thread对象
thread.start();
System.out.println("success");
}
}
另外,Thread类本身自己也实现了Runnable接口,这样说明Thread类可以传入一个Thread类的对象。这样就可以把一个Thread对象中的run方法交给其他线程调用。
2.3:实例变量和线程安全
共享数据的情况下,当多个线程访问同一个变量的时候,这时候可能会出现“非线程安全”。例如
这里面都是对同一个Thread对象进行调用,调用的是同一个run方法。产生的结果是
说明A和B同时对count进行了处理,产生了“非线程安全”,这种时候如果是对同一个变量进行访问的话,我们就要使多个线程之间进行同步。
使多个线程同步的方法:在run方法前加入synchronized关键字,使多个线程在执行run方法的时候,以排队的方式进行处理。synchronized可以在任意对象即方法上加锁,当一个线程要执行同步方法的代码时,先看这个方法有没有上锁,如果上锁了,说明其他线程正在调用这个方法,那么他只能等这个线程调用完它才能去拿到这个锁,去调用run方法。
特别注意:System.out.println()中的println方法中使用了synchronized关键字,是同步执行的。 要留意i–和println方法的联合使用时也会出现“非线程安全”,比如:
System.out.println("i =" + (i--));
其中i–是在println方法前执行的。
3:currentThread()方法
此方法可以返回代码段正在被哪个线程调用的信息。
下面注意一个特别重要的地方,this和Thread.currentThread的差异,看下面具体的代码。
public class Mythread extends Thread {
public Mythread() {
System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
}
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(this.getName());
}
public static void main(String[] args) {
Mythread mythread = new Mythread();
Thread t1 = new Thread(mythread);
t1.setName("A");
t1.start();
}
}
得到的运行结果为:
因此,MyThread的构造函数是由main线程执行的,run方法是由A线程执行的。
为什么运行结果两个都是Thread-0呢?
因为代码 Thread t1 = new Thread(mythread);这里执行的是把mythread给了Thread的构造函数,得到一个Thread对象。引用了mythread,执行的是mythread里面的run方法。所以run方法中的this还是mythread对象,它并没有复写getName方法,所以调用的是父类的getName方法,返回的依旧是Thread-0。
我们来验证一下,如果它不是引用mythread对象,this.getName会不会是它自己线程的名字?
public static void main(String[] args) {
Mythread t1 = new Mythread();
t1.setName("A");
t1.start();
得到的结果是:
很明显,返回的是它自己线程的名字。所以验证了上面的一点,如果将线程对象以构造参数的方式传递给Thread对象,那么this.getName返回的是引用的那个线程的名字。
4:isAlive(),sleep()方法,getid()方法
isAlive()方法的作用是测试线程是否处于活动状态,即线程已经启动尚未终止。
在使用isAlive方法时,如果线程对象是以参数的方式传递给Thread对象,跟上面的this和Thread.currentThread一样,存在差异。this指的是引用的那个线程。
sleep()方法:是在指定的毫秒数内让当前“正在执行的线程”(暂停执行),这个“正在执行的线程是this.currentThread()”返回的线程。这里就不会是引用的那个线程了,而是执行这个方法的线程。
getid():得到取得线程的唯一标识。
5:停止线程
5.1:停止不了的线程
使用interrupt()方法来停止线程,但是interrupt方法并不是立即停止运行,而仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。使用了interrupt方法后面的代码依旧会执行。
5.2:判断线程是否是停止状态
1)this.interruptd()方法:测试当前线程是否已经中断,执行后具有将状态标志清除,置为false的功能。
2)this.isInterruptd()方法:测试线程是否已经中断,测试的是线程Thread对象是否已经中断状态,不清除状态。
这两个方法的区别:this.interruptd()方法:测试当前线程,当前线程指的是运行this.interrupted()方法的线程。看代码:
public class MyThread2 extends Thread {
public void run() {
super.run();
for(int i = 0; i < 10; i++) {
System.out.println("i = " + i);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread2 mythread2 = new MyThread2();
mythread2.start();
Thread.sleep(1000);
mythread2.interrupt();
System.out.println(mythread2.interrupted());
System.out.println(mythread2.interrupted());
}
}
结果:
这里返回的都是false,很明显证明了上面的结论,这里使用的是interruptd()方法,判断的是当前线程,即执行这个方法的线程,这个线程是main线程,没有中断,并且第二次调用的时候,清除了之前的状态,即把其置为未中断状态,仍然返回了false。
this.isInterrupted()方法:测试的是Thread对象,不清除状态。
public class MyThread2 extends Thread {
public void run() {
super.run();
for(int i = 0; i < 5; i++) {
System.out.println("i = " + i);
}
}
public static void main(String[] args) throws InterruptedException {
MyThread2 mythread2 = new MyThread2();
mythread2.start();
// Thread.sleep(1000);
mythread2.interrupt();
System.out.println(mythread2.isInterrupted());
System.out.println(mythread2.isInterrupted());
}
}
运行结果:
5.3:能停止的线程;
1)异常法:前面我们学了如何判断当前线程是否中断,如果停止状态,那么我们就不去执行后面的代码。的确理论上我们可以这样做,但是事实中结果并不是这样,看代码:
public class Run {
public static void main(String[] args) {
ThreadTestThird thread = new ThreadTestThird();
thread.start();
thread.interrupt();
}
}
public class ThreadTestThird extends Thread {
public void run() {
super.run();
for(int i = 0; i < 10; i++) {
if(this.interrupted()) {
System.out.println("end");
break;
}
System.out.println("i=" + i);
}
System.out.println("还是运行了!");
运行结果:
本应该在停止了线程后,不会再执行后面的代码,但是还是执行了后面的代码。这个时候我们可以使用抛出异常的办法来停止后面代码的执行。
public class ThreadTestThird extends Thread {
public void run() {
super.run();
try {
for(int i = 0; i < 10; i++) {
if(this.interrupted()) {
System.out.println("end");
throw new InterruptedException();
}
System.out.println("i=" + i);
}
System.out.println("还是运行了!");
} catch (InterruptedException e) {
System.out.println("进catch了,不会执行下面的代码了");
e.printStackTrace();
}
运行结果:
2):在沉睡中停止
如果线程在sleep()状态下停止线程,会产生什么样的效果;
public class Run3 {
public static void main(String[] args) throws InterruptedException {
ThreadSleep ts = new ThreadSleep();
ts.start();
Thread.sleep(200);
ts.interrupt();
System.out.println("end");
}
}
public class ThreadSleep extends Thread {
public void run() {
super.run();
try {
System.out.println("run begin");
Thread.sleep(20000);
System.out.println("run end");
} catch (InterruptedException e) {
System.out.println("在沉睡中被停止!进入catch" + this.isInterrupted());
e.printStackTrace();
}
}
}
运行结果:
根据结果可以得到,如果在sleep状态下停止一个线程,会进入catch语句,并且清除停止状态值,使其变为false。
反之,如果先interrupt停止线程再进入sleep状态依然会进入catch语句,不执行sleep后面的语句。
3) 使用stop()方法暴力停止(但是此方法已经过期作废);
如果强制让线程停止则有可能使一些清理性的工作得不到完成;另外就是对锁定的对象进行了“解锁 ”,会导致数据不同步。不建议使用stop()方法。
4)使用return停止线程
使用return+interrupt()方法可以实现停止线程的效果
public class ThreadReturn extends Thread {
public void run() {
while(true) {
if(this.isInterrupted()) {
System.out.println("end!");
return ;
}
System.out.println("运行ing");
}
}
public static void main(String[] args) throws InterruptedException {
ThreadReturn tr = new ThreadReturn();
tr.start();
Thread.sleep(2000);
tr.interrupt();
}
}
运行结果:
还是建议使用“抛异常”的方法来实现线程的停止,异常流能更好的控制程序的运行流程,不至于使用很多个的return,造成污染。
6:暂停线程
suspend方法:暂停线程;resume:恢复线程(这两个方法也已经作废了,原因如下)
suspend与resume方法的缺点-----独占;即如果使用不恰当,极易造成公共的同步对象的独占,使其他线程无法访问公共同步对象。
例如:如果这个方法是上了锁的,两个线程都要调用这个公共方法,必须要前一个执行完后面的才能执行,比如println方法中就上了锁。看一个代码例子。
public class ThreadSuspend extends Thread {
int i = 0;
public void run() {
while(true) {
i++;
System.out.println(i);
}
}
public static void main(String[] args) {
try {
ThreadSuspend ts = new ThreadSuspend();
ts.start();
Thread.sleep(1000);
ts.suspend();
System.out.println("end");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
这里是不会输出main主线程中的那个“end”,因为println方法就有一个锁,当你把其中的一个线程暂停了的时候,main主线程就无法去访问println方法,也就不能输出“end”了。
另外一个缺点-----数据不同步
看例子:
public class MyObject {
private String username = "1";
private String password = "11";
public void SetValue(String u,String p) {
this.username = u;
if(Thread.currentThread().getName().equals("a")) {
Thread.currentThread().suspend();
}
this.password = p;
}
public void print() {
System.out.println(username + " " +password);
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
final MyObject my = new MyObject();
//final MyObject test = new MyObject();
Thread thread1 = new Thread() {
public void run() {
my.SetValue("a", "aa");
};
};
thread1.setName("a");
thread1.start();
Thread.sleep(200);
Thread thread2 = new Thread() {
public void run() {
my.print();
};
};
thread2.start();
}
}
运行结果:
数据不同步,原因是两个线程都是对同一个对象进行访问,而之前的那个线程赋予了对象的username属性后就暂停了,而password又没有改变,线程2进去后读取了线程1赋予的值,使数据产生了不同步。本应该得到的是a,aa。
7:线程的优先级
1)线程的优先级具有继承性,例如A线程启动B线程,则B线程的优先级与A的是一样的。
2)优先级具有规则性,可以使用setPriority()方法去设置线程的优先级,一般来说,高优先级的线程总是先执行完,谁先执行完与代码的调用顺序是无关的。可以看个例子:
public class MyThread2 extends Thread {
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println("Thread2走了哈");
}
}
}
public class MyThread extends Thread {
public void run() {
for(int i = 0; i < 5; i++) {
System.out.println("Thread1走了哈");
}
}
}
public class Run {
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 5; i++) {
MyThread2 thread2 = new MyThread2();
thread2.setPriority(1);
thread2.start();
//Thread.sleep(5000);
MyThread thread1 = new MyThread();
thread1.setPriority(10);
thread1.start();
}
}
}
运行结果:
先执行完的是thread2。说明谁先执行完和代码的调用顺序没有关系。
3)优先级具有随机性,不一定每次都是优先级高的先执行完。
8:守护线程
在java中有两种线程,一种是用户线程,一种是守护(Daemon )线程。守护线程是相当于幼儿园的保姆,当幼儿园的小朋友都回家了,也就不需要保姆了。当线程中没有了非守护线程,守护线程也就不自动销毁了。最经典的就是GC(垃圾回收器)的应用。
看个例子:
public class Run2 {
public static void main(String[] args) {
try {
TestThread3 thread3 = new TestThread3();
//MyThread2 thread2 = new MyThread2();
//thread2.setDaemon(true);
thread3.setDaemon(true);//把它定义为守护线程
thread3.start();
Thread.sleep(5000);
System.out.println("end");
//thread2.start();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public class TestThread3 extends Thread {
int i = 0;
public void run() {
try {
while(true) {
i++;
System.out.println(i);
Thread.sleep(1000);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果:
当主线程全部都执行完了以后,守护线程thread3也就强制退出,不会再执行了。只要线程中还有非守护线程在执行,守护线程也会执行。