线程补充
- 线程是操作系统能够进行运算调度的最小单位,是进程中的实际运作单位
线程和进程的区别
1.每个进程都有独立的代码和数据空间(进程上下文),进程间的切换会有较大的开销 2.线程可以当成轻量级的进程,每个线程有独立的工作内存,切换开销小 3.多进程:在操作系统中同时运行多个程序 4.多线程:在同一程序中有多个顺序流(功能)同时执行(多核cpu) 5.单线程:同一时间点只能有一个进程在执行(采用时间片轮转,让多个线程轮流执行。因为cpu执行速度快--假多线程)
线程理解
每个分支都叫一个线程,main叫主分支(主线程)
机器上的一个.class文件,机器上的一个.exe文件,这个叫做一个进程。程序的执行过程都是这样的:首先把程序的代码放到内存的代码区里面
代码放到代码区后并没有马上开始执行,但这时候说明了一个进程准备开始,进程已经产生了,但还没有开始执行,这就是进程,所以进程其实是一个静态的概念,它本身就不能动
平常所说的进程的执行指的是进程里面主线程开始执行了,也就是main()方法开始执行了。进程是一个静态的概念,在我们机器里面实际上运行的都是线程程序的执行流程:
1.操作系统将分块的程序运输到内存对应的区间,过程中建立一个对应的表方便寻址
2.当程序跑起来后根据局部性规律(当调用一块代码后,周围的代码也会被调用),一点点的调入内存中
3.寻址过程中使用虚拟地址分配,就可以运行那些占较大内存的程序java中的线程
java.lang.Thread 类的实例就是一个线程但是它需要调用java.lang.Runnable接口来执行, 由于线程类本身就是调用的Runnable接口 所以你可以继承java.lang.Thread 类或者直接调用Runnable接口(推荐)来重写run()方法实现线程
- 线程的创建和启动
继承Thread并重写run方法
class MyThread extends Thread { public void run(){ //线程体 } }
实现Runnable接口
//定义一个线程类 public class ThreadTest implements Runnable{ public void run() { // TODO Auto-generated method stub 线程体 } }
public class TestThread1{ public static void main(String args[]){ ThreadTest tt = new ThreadTest(); tt.run();//就会执行run()方法再执行main方法 Thread t = new Thread(tt);//启动一个新的线程需要Thread对象 t.start();//启动新线程与主线程一起执行(交替执行) for(int i=0;i<10;i++){ System.out.println("maintheod:"+i); } } } public static Thread currentThread()//获取当前线程的引用
start与run
start()方法被用来启动新创建的线程,而且start()内部调用了run()方法, 当调用run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start()方法才会启动新线程
线程状态转移
阻塞状态(等待状态)
- 创建一个新线程start准备好后进入就绪态(标上优先级别),进入cpu执行(线程调度器决定哪个线程进入运行态)
- 时间片用完线程执行完后线程终止/遇到死锁后kill掉/时间片用完没有执行完的线程进入等待态
线程使用
sleep(毫秒):休眠当前线程,进入等待状态,当休眠时间到解除等待状态,进入就绪状态等待cpu运行(睡眠时不会释放锁) yield()让出cpu给其他线程 join()合并当前线程,让线程按照当前代码写的顺序执行 wait():与synchronized关键字一起使用,进程进入等待状态,当notify/notifyall调用后解除等待态,wait调用后会释放锁,所以拿到锁后才可以运行 setPriority()设置线程优先级别
线程同步
对象互斥锁(synchronized)--保证共享数据操作的完整,这时就只有一个线程可以访问该对象/方法
synchronized(this) { 共享(同步)数据块 } synchronized public void xx(){ 共享(同步)方法 }
线程同步出现的问题
死锁:
1.在同步数据块内调用了其他同步代码-----减少同步锁定 2.指定获取锁的顺序:比如规定,只有获得A锁的线程才有资格获取B锁
synchronized
1.synchronized进行加锁操作,synchronized的锁机制会根据线程竞争情况在运行时会有偏向锁(单一线程)、轻量锁(多个线程访问synchronized区域)、对象锁(重量锁,多个线程存在竞争的情况)、自旋锁等。该关键字是一个几种锁的封装。 2.进入时,执行monitorenter(显式同步)将计数器+1,释放锁时计数器-1 3.当一个线程判断到计数器为0时,则当前锁空闲,可以占用;反之,当前线程进入等待状态
volatile
1.volatile关键字是与Java的内存模型有关(主内存和工作内存) 2.volatile可以直接与主内存产生交互,进行读写操作,保证可见性 2.volatile能确保可见性:可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。 3.volatile禁止进行指令重排序。 4.volatile变量可以保证下一个读取操作会在前一个写操作之后发生
线程池
java.util.concurrent.ThreadPoolExecutor类就是一个线程池。
当前线程池大小:表示线程池中实际工作者线程的数量
最大线程池大小(maxinumPoolSize):表示线程池中允许存在的工作者线程的数量上限
核心线程大小(corePoolSize ):表示一个不大于最大线程池大小的工作者线程数量上限
线程code
按顺序打印0~9--使用wait让未达到条件的线程进入等待态
public class ThreadTest { private static final int endNum = 10; public static void main(String[] args) { //按需求新建多个线程,让每个线程实现对应的功能 TTest[] test = new TTest[10]; for(int i=endNum-1;i>=0;i--) { test[i] = new TTest(i); test[i].start(); } } }
public class TTest extends Thread { private static final int endNum = 10; static int orderNum = 0; //对象锁,只有获取该锁才能继续操作 static Object object = new Object(); private int printNum; public TTest(int printNum) { this.printNum = printNum; } @Override public void run() { synchronized (object) { //用户判断程序是否执行完毕 while(orderNum < endNum) { if(orderNum == printNum) { System.out.println(printNum); orderNum ++; if(orderNum ==10) { System.out.println("打印完成"); } object.notifyAll(); }else { try { //每个线程都对应一个参数,根据参数判断是否应该输出,如果不符合,当前线程就进入等待态,唤醒后再执行 object.wait(); } catch (Exception e) { System.out.println("线程:"+printNum+"被打断了"); e.printStackTrace(); } } } } } }
ref:
java基础学习总结——线程(一)
你真的了解volatile关键字吗?
Java并发编程:volatile关键字解析
评论已关闭