线程基础

本文主要介绍线程的基础知识,相应的一些方法。

进程和线程区别:一个进程就是一个程序,当运行一个程序时,计算机会为这个程序开启一个进程。线程是用来真正执行程序的,一个进程最少有一个线程。补充:linux环境:一个进程最多可开1000个线程。windows环境:一个进程最多可开2000个线程。操作系统添加线程上线是因为线程切换需要耗费时间、占用内存,线程太多会将计算机资源耗尽导致死机。
CPU时间片轮换机制:又称RR调度,计算机底层会多次切换线程。
并行和并发:并行是指同一时间执行多个任务,并发是指同一时间能交替执行不同的任务。比如排队取票,并行是指如果有两个窗口,会排两队,这两队会同时进行取票操作。并发是指有一个窗口,然后拍了两队,这两队的人员交替的在这个窗口取票。二者的区别:一个是同时执行,一个是交替执行。

线程启动方式:两种方式启动线程:extends Thread 或 implements Runnable(将实现Runnable的方法交给Thread执行).(Thread.class 源码中注释: There are two ways to create a new thread of execution.)

public class CreateThread {

    public static void main(String[] args) {
        //调用线程
        Thread MyThread1 = new MyThread1();
        MyThread1.start();
        MyThread2 MyThread2 = new MyThread2();
        new Thread(MyThread2).start();
    }

    /*
     *创建线程方式1:继承Thread
     */
    private static class MyThread1 extends Thread{
        @Override
        public void run() {
            super.run();
            System.out.println("重写run(),开始写线程1的逻辑");
        }
    }

    /*
     * 创建线程方式2:实现Runnable
     */
    private static class MyThread2 implements Runnable{

        public void run() {
            System.out.println("重写run(),开始写线程2的逻辑");
        }
    }
}

Thread和Runnable的区别:Thread是对线程的抽象,Runnable是对任务的抽象。Thread 才是Java 里对线程的唯一抽象,Runnable 只是对任务(业务逻辑)的抽象。Thread 可以接受任意一个Runnable 的实例并执行。
线程终止和中断:线程终止:线程运行完或报异常线程会终止。
线程中断:在线程未执行完之前使线程停止执行并不会继续执行。
中断方法(stop()、interrupt()、isInterrupted()、isInterrupted())
stop():Thread类中的stop(),线程停止的方法,不过jdk已经标记为不建议使用的方法,因为stop()方法会立即停止线程,不考虑资源是否释放(例如占有锁时,锁不会被释放)。
interrupt():将线程标记为中断,但不会自己去中断线程,需要在程序中手动去判断该标记是否是被中断的,如果是,则停止线程,没被中断则继续执行。若不在程序中手动判断,则即便调用了intreeupt()也不会停止线程,所有interrupt()方法不会自己去中断线程,更不会立即中断线程,只是进行中断线程的标记,通知线程可以中断。
isInterrupted():获取线程是否中断的标记,默认是true,当调用interrupt()方法后,修改为false,即线程不会继续执行。
Thread.isInterrupted():static标记的isInterrupted(),可以获取线程是否被中断的标记,此时和非静态的isInterrupted()功能一样,但该方法会在获取标记后将标记改为true。该方法在实际中用的不多,面试时出现的几率较大。

public class EndThread {
	
	private static class UseThread extends Thread{

		public UseThread(String name) {
			super(name);
		}

		@Override
		public void run() {
			String threadName = Thread.currentThread().getName();
			System.out.println(threadName+" interrrupt flag ="+isInterrupted());
			while(!isInterrupted()){
//				while(!Thread.interrupted()){
				//while(true){
				System.out.println(threadName+" is running");
				System.out.println(threadName+"inner interrrupt flag ="
						+isInterrupted());
			}
			System.out.println(threadName+" interrrupt flag ="+isInterrupted());
		}
	}

	public static void main(String[] args) throws InterruptedException {
		Thread endThread = new UseThread("endThread");
		endThread.start();
		Thread.sleep(20);
		endThread.interrupt();//中断线程,其实设置线程的标识位true
	}
}

在这里插入图片描述
在这里插入图片描述

sleep()和中断线程的关系:通过源码可知sleep()方法会抛出InterruptedException(线程中断异常)。该异常会把isInterrupted()置位true。所以如果在中断线程后调用sleep()方法(调用中断线程和调用sleep方法不在同一个线程中,否则不会出现这种情况),需要手动在sleep方法的异常处理中在调用一个中断线程(interrupt()),否则线程不会中断。优点:留给了程序员手动干预的时间。如果InterruptedException异常不修改标志位,就和Thread类中的stop()方法一样了,直接中断了线程,如果此时占有资源(例如锁),资源并不会释放。

/**
 *类说明:阻塞方法中抛出InterruptedException异常后,如果需要继续中断,需要手动再中断一次
 */
public class HasInterrputException {
	
	private static class UseThread extends Thread{
		
		public UseThread(String name) {
			super(name);
		}
		
		@Override
		public void run() {
			while(!isInterrupted()) {
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					System.out.println(Thread.currentThread().getName() +" in InterruptedException interrupt flag is " +isInterrupted());
					//需再手动中断一次,InterruptedException这样设计是给资源释放留点时间
					interrupt();
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + " I am extends Thread.");
			}
			System.out.println(Thread.currentThread().getName() +" interrupt flag is "+isInterrupted());
		}
	}
	public static void main(String[] args) throws InterruptedException {
		Thread endThread = new UseThread("HasInterrputEx");
		endThread.start();
		Thread.sleep(500);
		endThread.interrupt();
	}
}

**切记:处于死锁状态的线程不会被中断。
获取当前线程的方法Thread.currentThread() :当使用继承Thread创建线程时可以获取到当前线程的实例,继而获取当前线程,当使用实现Runnable方法时无法获取当前线程的实例,此时就可以通过该方法获取当前线程。

***start()和run()***::我们通过new Thread()其实只是new 出一个Thread 的实例,还没有与操作系统中真正的线程挂起钩来。只有执行了start()方法后,才实现了真正意义上的启动线程。
start()方法让一个线程进入就绪队列等待分配cpu,分到cpu 后才调用实现的run()方法,start()方法不能重复调用,如果重复调用会抛出异常()。而run() 方法是业务逻辑实现的地方,本质上和任意一个类的任意一个成员方法并没有任何区别,可以重复执行,也可以被单独调用。
在这里插入图片描述

线程其他方法:
yield():让出CPU的执行权,从运行状态变为就绪状态,但不会释放资源。所有调用yield()方法的线程都有可能再次被CPU选中进行执行。因为yield()方法不会释放资源,所以可以把资源都释放掉之后在调用该方法。
join():线程A正在执行,调用线程B的join()方法,此时线程A会让出CPU的执行权,让线程B执行完后,CPU才会继续执行线程A。类似于压栈出栈。
join()考点:如何让线程顺序的执行? 使用join方法。

线程优先级:线程优先级只是给CPU的时间片轮转机制设置一个轮转到改线程的时间,线程并不一定按照这个优先级去执行,所以这个优先级的意义不大。

守护线程:用户线程执行完后,守护线程会自动关闭(守护线程可用来做内存管理等)。一般业务开发不需要设置守护线程,在写底层框架时可能会用到守护线程。Daemon(守护)线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java 虚拟机中不存在非Daemon 线程的时候,Java 虚拟机将会退出。可以通过调用Thread.setDaemon(true)将线程设置为Daemon 线程。我们一般用不上,比如垃圾回收线程就是Daemon 线程。Daemon 线程被用作完成支持性工作,但是在Java 虚拟机退出时Daemon 线程中的finally 块并不一定会执行。在构建Daemon 线程时,不能依靠finally 块中的内容来确保执行关闭或清理资源的逻辑。

synchronized(锁的是对象):是一个内置锁。可以锁代码块、对象、和类。
锁代码块:将synchronized作用于一段代码块上。
对象锁:对象锁是用于对象实例方法,或者一个对象实例上。
类锁:用于类的静态方法或者一个类的class 对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class 对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,类锁其实锁的是每个类的对应的class 对象。类锁和对象锁之间也是互不干扰的。
synchronized错误的加锁:加锁的对象不是同一个对象,这样加锁是没有效果的。

package concurrent.syn;

/**
 * synchronized加锁
 */
public class SysTest {

    public static void main(String[] args) {
        MyThread myThread = new MyThread(1);
        for (int j = 0; j <5 ; j++) {
            new Thread(myThread).start();
        }
    }
    private static class MyThread implements Runnable{
        private Integer i;
        private Object obj  = new Object();

        public MyThread(Integer a){
            this.i=a;
        }

        @Override
        public void run() {

            /*method1:线程安全*/
            //synchronized (obj){
            /*method2:线程不安全*/
//            synchronized (new Object()){
            /*method3:线程不安全*/
            synchronized (i){ //i++内部实现是new Integer(i),和method1一个问题
                //获取当前线程
                Thread thread = Thread.currentThread();
                i++;
                System.out.println("线程名称="+thread.getName()+"----线程打印值"+i);

                try {
                    Thread.sleep(1000);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
            }

        }
    }
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
volatile:轻量的同步机制,只保证数据的可见性,但并不隔离线程,适用于一写多度的情况。

ThreadLocal:

ThreadLocal 和 Synchonized 都用于解决多线程并发訪问。可是 ThreadLocal 与 synchronized 有本质的差别。synchronized 是利用锁的机制,使变量或代码块 在某一时该仅仅能被一个线程訪问。而 ThreadLocal 为每个线程都提供了变量的 副本,使得每个线程在某一时间訪问到的并非同一个对象,这样就隔离了多个线 程对数据的数据共享。

threadLocal详见:https://mp.csdn.net/mdeditor/95492233#

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值