什么是程序,进程和线程?
程序:是一组指令的集合(如我们写的代码),它静态存储于诸如磁盘之类的存储器里。
当一个程序被操作系统执行时,它就会被载入内存空间,并在逻辑上产生一个独立的实例,这就是进程。
进程:是资源(CPU、内存等)分配的基本单位,它是程序执行时的一个实例,即运行中的程序。
通常由程序、数据和进程控制块(Process Control Block,PCB)组成的,PCB时进程存在的唯一标志。
线程:线程是程序执行时的最小单位,它是进程的一个执行流,是CPU调度和分派的基本单位,一个进程可以由很多个线程组成,线程间共享进程的所有资源,每个线程有自己的堆栈和局部变量。
线程由CPU独立调度执行,在多CPU环境下就允许多个线程同时运行。同样多线程也可以实现并发操作,每个请求分配一个线程来处理。
Java中的多线程
其实在Java中的多线程是模拟出来的,实际上是并发执行的,真正的多线程是指多个CPU,即多核。如果是模拟出来的多线程,即一个CPU的情况下,在同一个时间点,CPU只能执行一个代码,由于切换的很快,所以就有同时执行的错觉。
并发: 多个任务交替执行,多任务之间还是串行的。
容易与 并发 混淆在一起的概念-------并行
并行: 多个任务同时执行。
多线程实现方式
1、继承Thread类,重写run方法
public class MyThread extends Thread{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
public static void main(String[] args) {
new MyThread().start(); /* 线程启动方式,调用线程类的 start() 方法 */
}
}
运行结果
下面两句代码有什么区别?
public class MyThread extends Thread{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+": "+1);
}
public static void main(String[] args) {
/** 这两句 **/
new MyThread().start();
new MyThread().run();
}
}
运行结果
start() : 它的作用是启动一个新线程,新线程会执行相应的run()方法。start()不能被重复调用。
而一启动线程,线程会自动执行线程里面的 run() 方法
run() : run()就和普通的成员方法一样,可以被重复调用。单独调用run()的话,会在当前线程中执行run(),而并不会启动新线程。
为什么呢?
当你启动线程,使用start(),系统会把run()方法当成线程执行体来处理,这是正常的,也是正确的情况。但是,当你启动线程时,调用run()方法,系统run()方法会立即执行,但是这时候系统会把run()方法当成普通的方法,线程对象也当成一个普通对象。
所以出现一个是main线程调用,一个是新的线程调用。由于主线程的优先级比其他线程高,所以比较容易抢占资源,优先运行。
2、实现Runnable接口,重写run方法
启动线程,使用静态代理
- 创建真实角色
- 创建代理角色 +真实角色引用
- 调用.start() 启动线程
package test;
public class MyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (i % 2 == 0){
System.out.println(Thread.currentThread().getName()+": "+i);
}
}
}
public static void main(String[] args) {
Runnable pro = new MyThread();
new Thread(pro).start();
}
}
3、使用Callable和Future创建线程 重写call方法(了解)
public class MyThread implements Callable<String> {
private String str;
public MyThread(String str){
this.str = str;
}
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "call: "+str;
}
public static void main(String[] args) throws ExecutionException,InterruptedException {
Callable callable = new MyThread("hello world");
FutureTask<String> task = new FutureTask(callable);
long beginTime = System.currentTimeMillis();
new Thread(task).start();
//获取call()方法的返回值
String result = task.get();
System.out.println("task result = "+result);
System.out.println("use time="+(System.currentTimeMillis()-beginTime));
}
}
运行结果
常见的线程方法
- currentThread()
返回当前正在执行的线程对象 - getName()
获取线程名称 - setName(String name)
设置线程名称 - isAlive()
判断线程当前状态,若执行状态则返回true,反之,返回false
public static void main(String[] args) throws InterruptedException {
//实现Runnable接口
MyThread t1 = new MyThread();
Thread proxy = new Thread(t1,"线程t1");
proxy.setName("Thread1");
System.out.println(proxy.getName());
System.out.println(Thread.currentThread().getName()); //main
proxy.start();
System.out.println("启动后的状态:"+proxy.isAlive()); //true
Thread.sleep(200);
t1.stop();
Thread.sleep(100);
System.out.println("停止后的状态:"+proxy.isAlive()); //false
}
-
setPriority() & getPriority()
setPriority(int newPriority):设置线程的优先级;
getPriority():获取线程的优先级;
什么是线程优先级?
线程优先级是指优先级越高,越有可能先执行,但只是建议先执行,具体什么时候执行由系统决定。
在Thread类中定义了三种静态成员变量:
- public final static int MIN_PRIORITY = 1;
- public final static int NORM_PRIORITY = 5;
- public final static int MAX_PRIORITY = 10;
也就是线程优先级的取值范围是1-10;
public class Prio
{
public static void main(String[] args)
{
MyThread t1 = new MyThread();
Thread p1 = new Thread(t1,"线程t1");
MyThread t2 = new MyThread();
Thread p2 = new Thread(t2,"线程t2");
p1.setPriority(Thread.MIN_PRIORITY); //设置优先级 1
p2.setPriority(Thread.MAX_PRIORITY); //设置优先级 10
System.out.println(Thread.currentThread().getPriority()); //主线程main优先级为5
}
}
- sleep() 线程休眠 不会释放锁
Thread.sleep(millisec) 方法会休眠当前正在执行的线程,其他线程不受影响,millisec 单位为毫秒。
sleep() 可能会抛出 InterruptedException,因为当前线程sleep的时候,有可能被停止,这时就会抛出 InterruptedException。
线程休眠会立即交出CPU,但是不会释放锁。
public void run() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
- join() 线程强制加入 会释放锁
线程强制加入,强制加入的线程运行完毕后才会让出资源。
如:主线程(或者某个线程)中若调用了子线程(或者是另外一个线程)的join()方法就必须得等该子线程run()方法结束,主线程才能继续执行
join()方法只是对Object提供的wait()做了一层包装而已。( join在内部使用wait()方法进行等待 ),执行wait(long)方法后,当前线程的锁会被释放
,其他线程就可以调用此线程中的同步方法了。
public class JoinDemo {
public static void main(String[] args) {
TestSleep s = new TestSleep();
Thread t1 = new Thread(s,"线程t1"); //新建
t1.start(); //就绪
//cpu调度 运行
for (int i=0; i<10; i++) {
if(6 == i) {
try {
t1.join(); //main 阻塞
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() +" " +i);
}
}
}
class TestSleep implements Runnable{
public void run(){
for(int i = 0; i < 6; i++){
System.out.println(Thread.currentThread().getName() +" i : "+ i +" Join...");
}
}
}
输出结果:
-
yield() 线程让步 不会释放锁
线程让步是指 暂停执行当前的线程对象,并执行其他线程,yield()方法会让当前线程交出CPU(不一定立即交出CPU),不会释放锁。
yield()方法无法控制具体交出CPU的时间,并且yield()方法只能让拥有相同优先级的线程有获取CPU的机会。暂时让一会不是一直让,只让一会就不让了,具体调度要看CPU。
不会释放锁。
public class YieldDemo {
public static void main(String[] args) {
TestSleep1 s = new TestSleep1();
Thread t1 = new Thread(s,"线程t1"); //新建
t1.start(); //就绪
//cpu调度 运行
for (int i=0; i<10; i++)
{
if(i == 4)
{
System.out.println("开始礼让:");
Thread.yield();
}
System.out.println(Thread.currentThread().getName() +" " +i);
}
}
}
class TestSleep1 implements Runnable {
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " i : " + i + " Yield...");
}
}
}