线程与进程

1 线程:进程中负责程序执行的执行单元
线程本身依靠程序进行运行
线程是程序中的顺序控制流,只能使用分配给程序的资源和环境

2 进程:执行中的程序
一个进程至少包含一个线程

3 单线程:程序中只存在一个线程,实际上主方法就是一个主线程

4 多线程:在一个程序中运行多个任务
目的是更好地使用CPU资源

线程的实现

继承Thread类

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyThread thread1 = new MyThread("thread1");
        thread1.start();
        MyThread thread2 = new MyThread("thread2");
        thread2.run();
    }
}

class MyThread extends Thread{
    private String name;

    public MyThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println("name:"+name+" 子线程ID:"+Thread.currentThread().getId());
    }
}

实现Runnable接口

public class Test {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        MyRunnable runnable = new MyRunnable();
        Thread thread = new Thread(runnable);
        thread.start();
    }
} 
class MyRunnable implements Runnable{
    public MyRunnable() {
    }

    @Override
    public void run() {
        System.out.println("子线程ID:"+Thread.currentThread().getId());
    }
}

静态方法

1.currentThread()

public class Test2 {
    public static void main(String[] args)  {
        System.out.println("主线程ID:"+Thread.currentThread().getId());
        System.out.println("主线程名字:"+Thread.currentThread().getName());
    }
}

2.sleep()

public class Test2 {

    private int i = 10;
    private Object object = new Object();

    public static void main(String[] args) throws IOException {
        Test2 test = new Test2();
        MyThread thread1 = test.new MyThread();
        MyThread thread2 = test.new MyThread();
        thread1.start();
        thread2.start();
    }

    class MyThread extends Thread{
        @Override
        public void run() {
            synchronized (object) {
                i++;
                System.out.println(Thread.currentThread().getName()+"===> i:"+i);
                try {
                    System.out.println("线程"+Thread.currentThread().getName()+"进入睡眠状态");
                    Thread.currentThread().sleep(5000);
                } catch (InterruptedException e) {
                    // TODO: handle exception
                }
                System.out.println("线程"+Thread.currentThread().getName()+"睡眠结束");
                i++;
                System.out.println("i:"+i);
            }
        }
    }
}
Thread-0===> i:11
线程Thread-0进入睡眠状态
//这里会等待3000毫秒
线程Thread-0睡眠结束
i:12

Thread-1===> i:13
线程Thread-1进入睡眠状态
//这里会等待3000毫秒
线程Thread-1睡眠结束
i:14

3.yield

  • 调用yield方法会让当前线程交出CPU权限,让CPU去执行其他的线程
  • 但是yield不能控制具体的交出CPU的时间,另外,yield方法只能让拥有相同优先级的线程有获取CPU执行时间的机会。
  • 注意,调用yield方法并不会让线程进入阻塞状态,而是让线程重回就绪状态,它只需要等待重新获取CPU执行时间,这一点是和sleep方法不一样的。
public class MyThread  extends Thread{
    @Override
    public void run() {
        long beginTime=System.currentTimeMillis();
        int count=0;
        for (int i=0;i<50000000;i++){
            count=count+(i+1);
            //Thread.yield();
        }
        long endTime=System.currentTimeMillis();
        System.out.println("用时:"+(endTime-beginTime)+" 毫秒!");
    }
}

public class Run {
    public static void main(String[] args) {
        MyThread t= new MyThread();
        t.start();
    }
}
注释======>用时:23毫秒!
取消注释==>用时:4890 毫秒!

对象方法

start()

start()用来启动一个线程,当调用start方法后,系统才会开启一个新的线程来执行用户定义的子任务,在这个过程中,会为相应的线程分配需要的资源。

run()

run()方法是不需要用户来调用的,当通过start方法启动一个线程之后,当线程获得了CPU执行时间,便进入run方法体去执行具体的任务。注意,继承Thread类必须重写run方法,在run方法中定义具体要执行的任务。

getId()

public class Run{
    public static void main(String[] args) {
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName());
        System.out.println(thread.getId());
    }
}
main
1

isAlive()

方法isAlive()的作用是测试线程是否偶处于活动状态。什么是活动状态呢?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。

public class Test {
    public static void main(String[] args) throws InterruptedException {
        MyThread thread2 = new MyThread("_2");
        System.out.println(thread2.isAlive());
        thread2.start();
        Thread.sleep(1000);
        System.out.println(thread2.isAlive());
    }
}

class MyThread extends Thread{
    private String name;

    public MyThread(String name){
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.isAlive());
    }
}
false
true
false

join()

在很多情况下,主线程创建并启动了线程,如果子线程中药进行大量耗时运算,主线程往往将早于子线程结束之前结束。这时,如果主线程想等待子线程执行完成之后再结束,比如子线程处理一个数据,主线程要取得这个数据中的值,就要用到join()方法了。方法join()的作用是等待线程对象销毁。

public class Thread4 extends Thread{

    public Thread4(String name) {
        super(name);
    }
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + "  " + i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 启动子进程
        new Thread4("new thread").start();

        for (int i = 0; i < 10; i++) {
            if (i == 5) {
                Thread4 th = new Thread4("joined thread");
                th.start();
                th.join();
            }
            System.out.println(Thread.currentThread().getName() + "  " + i);
        }

    }
}
有join方法:
main  0
main  1
main  2
main  3
main  4
new thread  0
new thread  1
new thread  2
new thread  3
new thread  4
joined thread  0
joined thread  1
joined thread  2
joined thread  3
joined thread  4
main  5
main  6
main  7
main  8
main  9

没有join方法:
main  0
main  1
main  2
main  3
main  4
main  5
main  6
main  7
main  8
main  9
new thread  0
joined thread  0
joined thread  1
joined thread  2
joined thread  3
joined thread  4
new thread  1
new thread  2
new thread  3
new thread  4

getName和setName()

用来得到或者设置线程名称。

getPriority和setPriority()

用来获取和设置线程优先级。

setDaemon和isDaemon()

用来设置线程是否成为守护线程和判断线程是否是守护线程。

停止线程

停止线程是在多线程开发时很重要的技术点,掌握此技术可以对线程的停止进行有效的处理。

停止一个线程可以使用Thread.stop()方法,但最好不用它。该方法是不安全的,已被弃用。
在Java中有以下3种方法可以终止正在运行的线程:

  • 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止
  • 使用stop方法强行终止线程,但是不推荐使用这个方法,因为stop和suspend及resume一样,都是作废过期的方法,使用他们可能产生不可预料的结果。
  • 使用interrupt方法中断线程,但这个不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。

暂停线程

interrupt()方法

线程的优先级

JDK中使用3个常量来预置定义优先级的值,代码如下:

public final static int MIN_PRIORITY = 1;
public final static int NORM_PRIORITY = 5;
public final static int MAX_PRIORITY = 10;

线程优先级特性:

  • 继承性
    比如A线程启动B线程,则B线程的优先级与A是一样的。
  • 规则性
    高优先级的线程总是大部分先执行完,但不代表高优先级线程全部先执行完。
  • 随机性
    优先级较高的线程不一定每一次都先执行完。

守护线程

在Java线程中有两种线程,一种是User Thread(用户线程),另一种是Daemon Thread(守护线程)。
Daemon的作用是为其他线程的运行提供服务,比如说GC线程。其实User Thread线程和Daemon Thread守护线程本质上来说去没啥区别的,唯一的区别之处就在虚拟机的离开:如果User Thread全部撤离,那么Daemon Thread也就没啥线程好服务的了,所以虚拟机也就退出了。

守护线程并非虚拟机内部可以提供,用户也可以自行的设定守护线程,方法:public final void setDaemon(boolean on) ;但是有几点需要注意:

  • thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。 (备注:这点与守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别)
  • 在Daemon线程中产生的新线程也是Daemon的。 (这一点又是有着本质的区别了:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是“父进程挂掉,init收养,然后文件0,1,2都是/dev/null,当前目录到/”)
  • 不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为在Daemon Thread还没来的及进行操作时,虚拟机可能已经退出了。

同步与死锁

  1. 同步代码块
    在代码块上加上”synchronized”关键字,则此代码块就称为同步代码块
  2. 同步代码块格式
synchronized(同步对象){
 需要同步的代码块;
}
  1. 同步方法
    除了代码块可以同步,方法也是可以同步的

  2. 方法同步格式

    synchronized void 方法名称(){}
分类: Java

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注