文章转载自:多线程学习笔记
线程与进程
进程的概念
进程是操作系统结构的基础;是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上执行的过程,它是系统进行资源分配和调度的一个独立单位。
——百度百科
*简单地说,进程可以理解为正在运行的程序,完全可以将运行在内存中的exe文件理解成进程,进程是受操作系统管理的基本运行单元。*如通过 Ctrl + Alt + .的快捷键 ->启动任务管理器 -> 进程,就可以看到进程,基本上在运行中的exe程序都可以看成进程。
线程的概念
**线程可以理解成是在进程中独立运行的子任务。**比如,QQ.exe运行时就有很多的子任务在同时运行。再如,在使用word编辑文档时,可以一边听音乐一边下载电影一边给好友传输资料等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,编辑文档、听音乐、下载电影、发送图片等功能都有对应的线程在后台摸摸地运行。
下图是单任务与多任务运行的方式,从图中可以看出:在单任务运行环境中,后面的任务必须在前面的任务运行完之后才能进行,也就是说单任务的特点是排队执行,也就是同步。而在多任务环境中,不同的任务可以同时进行,即使用多线程后可以在同一时间内运行更多不同种类的任务。由此可以得出:但任务环境的缺点是,CPU利用率很低。多线程技术的优点是异步执行多个任务,大大地提高CPU利用率。
注意:多线程是异步的,所以千万不要把eclipse中代码的顺序当成线程执行的顺序,线程被调用的时机是随机的。
多线程的使用
一个进程在运行时至少会有一个线程在运行,比如调用public static void main(String[] args)方法时,JVM就会创建一个相应的线程在后台摸摸地执行。 实现多线程编程的方式主要有两种:继承Thread类和实现Runnable接口。
线程安全和数据共享的两个示例:
package org.sym.threadsafe;
/**
* 本程序使用synchronized关键字修饰方法,保证多线程在对类对象的同一个变量进行修改时,能够按顺序获取该同步锁,保证值修改的一致性
* @author sym
* @since 2018.3.11 21:46
*
*/
class MyThread01 extends Thread{
int count = 5;
/* (non-Javadoc)
* @see java.lang.Thread#run()
*/
@Override
synchronized public void run(){
//要想实现多个线程对同一个变量count进行同步数据共享(修改的结果不重复且依次递减),需要在方法前加上关键字synchronized
count--;
//此示例中不要使用for循环,因为使用同步后其他线程就得不到运行的机会了。可以一直由一个线程进行减法运算
System.out.println("由 " + Thread.currentThread().getName() + " 计算, count=" + count);
//虽然使用synchronized关键字修饰方法,可以保证对其中变量修改的一致性,但并不能保证线程调用顺序按照线程实例化的顺序执行
}
}
public class SynchronizedThread01 {
public static void main(String[] args){
MyThread01 mythread = new MyThread01();
Thread a = new Thread(mythread, "A");
//原型为Thread(Runnable target, String name),由于Thread类实现了Runnable,所以在向该类中传入Thread对象时,相当于Thread => Runnable向上转型
Thread b = new Thread(mythread, "B");
Thread c = new Thread(mythread, "C");
Thread d = new Thread(mythread, "D");
Thread e = new Thread(mythread, "E");
a.start(); //启动线程
b.start();
c.start();
d.start();
e.start();
}
}
其中一次的运行结果如下。从结果中可以看到:虽然使用关键字synchronized修饰方法,可以保证方法中变量修改的一致性,但是由于线程被调用的时机是随机的,所以使用多线程技术时,代码的运行结果与代码的执行顺序或调用顺序是无关的(本例中从前面的线程名的顺序与调用的线程顺序不一致可以看出)。
另一个线程安全的实例:
package org.sym.threadsafe;
/**
* 本程序使用synchronized关键字修饰方法,表示要想执行该同步方法必须先要拿到这把锁,
* 这样当有多个线程对该类的同一个对象中的同一个变量进行操作时,就不会出现只修改后不同步的“非线程安全”现象
* @author sym
* @version v1.0
* @since 2018.3.11 21:26
*
*/
//本类模拟成一个Servlet组件
class LoginServlet {
private static String usernameRef;
private static String passwordRef;
synchronized public static void doPost(String username, String password){//使用synchronized关键字可以保证多线程修改变量的一致性
try{
usernameRef = username;
if(username.equals("a")){
Thread.sleep(1000); //休眠1s
}
passwordRef = password;
System.out.println("username = " + usernameRef + " password = " + password);
//虽然使用synchronized关键字修饰方法,可以保证对其中变量修改的一致性,但并不能保证线程调用顺序按照线程实例化的顺序执行
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
class ALogin extends Thread{
@Override
public void run(){
LoginServlet.doPost("a", "aa"); //静态方法可以直接通过其类名调用,而不需要使用实例对象调用
}
}
class BLogin extends Thread{
@Override
public void run(){
LoginServlet.doPost("b", "bb");
}
}
public class SynchronizedThread02 {
public static void main(String[] args){
ALogin a = new ALogin();
a.start();
BLogin b = new BLogin();
b.start();
}
}
其中一次运行的结果:
注意:虽然使用synchronized关键字修饰方法,可以保证对其中变量修改的一致性,但并不能保证线程调用顺序按照线程实例化的顺序执行