--------------------------------------------------------------------------------------------------------------------------------仅供个人学习总结
进程是操作系统进行资源分配和调度的基本单元。
线程是在进程内执行运算的最小单位。
多线程是异步的,不要把代码的顺序当成线程执行的顺序,线程被调用的时机是随机的。
实现多线程编程的方式主要有两种,一是继承Thread类,另一种是实现Runnable接口。
上图为 Thread类的部分源码,可看出Thread类实现了Runnable接口。这两种方式创建的线程在工作时的性质是一样的,没有本质的区别。Java语言的特点是单继承,所以为了支持多继承,完全可以实现Runnable接口的方式,一边继承一边实现。
创建MyThread类并继承Thread类,重写run方法
package multithread.thread;
public class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
System.out.println("运行了MyThread的 run方法");
}
}
创建运行类Test
package multithread.thread;
public class Test {
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
System.out.println("运行结束");
}
}
运行结果
从结果可看出,MyThread类的run方法执行较晚,这也说明在使用多线程技术时,代码的运行结果和代码的执行顺序或调用顺序是无关的。
Thread类中的start()方法通知“线程规划器”此线程已经准备就绪,等待调用线程对象的run()方法。这个过程其实就是让系统安排一个时间来调用 Thread 中的 run() 方法,也就是使线程得到运行,多线程是异步的。线程在代码中启动的顺序不是start()方法被调用的顺序。
创建MyRunnable类实现Runnable接口,重写run方法
package multithread.runnable;
public class MyRunnable implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("运行中");
}
}
创建运行类Test
package multithread.runnable;
public class Test {
public static void main(String[] args) {
MyRunnable myRunnable=new MyRunnable();
Thread thread=new Thread(myRunnable);
thread.start();
System.out.println("运行结束");
}
}
运行结果
还可以这样创建运行
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("运行run方法");
}
}).start();
自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。
自定义线程类
public class MyThread extends Thread{
private int count=5;
public MyThread(String name){
super();
this.setName(name);
}
@Override
public void run() {
// TODO Auto-generated method stub
super.run();
while(count>0){
count--;
System.out.println("由 "+this.currentThread().getName()+" 计算 ,count="+count);
}
}
}
创建运行类
public class Test {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread A=new MyThread("A");
MyThread B=new MyThread("B");
MyThread C=new MyThread("C");
A.start();
B.start();
C.start();
}
}
运行结果
有结果可知,每个线程拥有自己的count变量,自己减少自己的count变量的值。这样的情况就是变量不共享。
共享数据的情况就是多个线程可以访问同一个变量。
为了避免产生“非线程安全”问题,对run方法添加了synchronized同步关键字
自定义线程类
public class MyThread2 extends Thread{
private int count=5;
@Override
synchronized public void run() {
super.run();
count--;
System.out.println("由 "+this.currentThread().getName()+" 计算 ,count="+count);
}
}
创建测试类
public class Test2 {
public static void main(String[] args) {
MyThread2 myThread2=new MyThread2();
Thread a=new Thread(myThread2,"A");
Thread b=new Thread(myThread2,"B");
Thread c=new Thread(myThread2,"C");
Thread d=new Thread(myThread2,"D");
Thread e=new Thread(myThread2,"E");
a.start();
b.start();
c.start();
d.start();
e.start();
}
}
运行结果
synchronized关键字可以在任意对象及方法上加锁,而加锁的这段代码称为“互斥区”或“临界区”。
当一个线程执行同步方法里面的代码时,线程首先尝试去拿这把锁,如果能够拿到这把锁,那么线程就可以执行synchronized里面的代码。如果不能拿到这把锁,那么这个线程就会不断的尝试拿这把锁,直到能够拿到为止,而且是有多个线程同时去争抢这把锁。
非线程安全主要是指多个线程对同一个对象中的同一个实例变量进行操作时会出现值被更改、值不同步的情况,进而影响程序的执行流程。
推荐《Java多线程编程核心技术》高红岩 著