java-单线程基础

进程和线程

  • 进程是动态的程序,特点就是动态。
  • 线程是比进程更小的单位,一个进程可以有一个或多个线程,但是进程最少一个线程。
  • 多线程之间可以分享资源,但是进程之间不会分享资源。
  • 进程是系统资源分配的最小单位,而线程是CPU任务调度。也就是说CPU就是会对多个线程进行分配时间块。

线程的组成部分

  • 时间块:线程必须要得到CPU分配的时间片才可以执行。
  • 共享数据:堆数据可以共享,栈是每个线程独立
  • 线程的逻辑代码

线程的实现方法

方法一:继承Thread类,重写run方法。
缺点就是,java里面是单继承,如果继承了Thread类,那么就不能继承其他类了。

public class MyThread extends Thread{
    
    //继承Thread类。
	
	@Override
	public void run() {
    
    //重写run方法
		try {
    
    
			Thread.sleep(3); //睡眠1毫秒
		}catch(InterruptedException e){
    
    
			e.printStackTrace();
		}
		//得到当前进程的名字,currentThread表示当前进程
		String threadName = Thread.currentThread().getName(); 
		
		for(int i = 0 ; i < 100 ; i++) {
    
     //循环输出看看输出当前进程的名字和执行次数
			System.out.println(threadName + "==" + i);
		}
	}
}

main方法里面

public static void main(String[] args) {
    
    
		
		MyThread my1 = new MyThread();
		my1.start(); //.start()进入是就绪状态,还没获得cpu调度时间,即获得时间块
		
		MyThread my2 = new MyThread();
		my2.start(); 
	}

得到的运行结果那么每次就会都不一样,因为两个对象需要抢位置来获得时间块执行。

方法二:实现Runnable接口,重现run()方法
因为接口,可以实现多个,所以这个优点就是接口的优点实现多继承

public class MyRunnable  implements Runnable{
    
    

	@Override
	public void run() {
    
    
	}
}
public static void main(String[] args) {
    
    
		
		MyRunnable run1 = new MyRunnable(); //就是比继承Thread类多一行代码
		Thread thread1 = new Thread(run1); //这里将对象作为参数传递到进程中
		thread1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即时间块
		//获得当前线程的名称。
		String name = Thread.currentThread().getName();
		for(int i = 0 ; i < 100 ; i++) {
    
    
			
			System.out.println(name + "==" + i);
			try {
    
    
				my1.join();
			} catch (InterruptedException e) {
    
    
				e.printStackTrace();
			}
		}
	}

}

注意在main方法里面,一定有一个主线程是main,一定有一个主线程是main,一定有一个主线程是main。

从下图的运行结果来看,我没有创建一个线程叫main,一个下面叫Thread-0就是thread1这个对象,在main方法中一定有main线程在这里插入图片描述

线程的状态

这里采用了百度里面的图片,应该来说很贴切。
在这里插入图片描述
初始状态-new关键字
就是创建一个进程对象,进入初始状态。

Thread thread = new Thread;

就绪状态READY-对象.start()

thread.start(); //start并没有进入running 运行中这个状态,只是下一步就是运行中这个状态,这点很重要

运行中状态running-
从上图中可以看到,系统调用,所以这里并没有代码来实现。

等待状态waiting
三种方式实现。

  1. Thread.sleep(毫秒数);
  2. 对象.join(); *//这个会将该对象强行加入到运行状态中,这个有点霸道,但是很重要一点,就绪状态到运行中状态这个谁也说不清,有的时候得到的结果可能不是我们想要的
  3. 对象.wait(); 这个我还没有理解透彻,因为这里需要加上锁。

下面这个例子是Thread.sleep();

public class TestThread {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		MyThread my1 = new MyThread();
		my1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即执行时间
		
		MyThread my2 = new MyThread();
		my2.start();
	}

}

public class MyThread extends Thread{
    
    
	
	@Override
	public void run() {
    
    
		try {
    
    
			Thread.sleep(3); //睡眠1毫秒
		}catch(InterruptedException e){
    
    
			e.printStackTrace();
		}
		String threadName = Thread.currentThread().getName(); 
		//得到当前进程的名字,currentThread表示当前进程
		for(int i = 0 ; i < 100 ; i++) {
    
    
			System.out.println(threadName + "==" + i);
		}
	}
}

在这里插入图片描述
阻塞状态

结束状态
也就是线程运行结束。程序结束的话,那么就是main线程结束了。

sleep();yield();join();

这三个方法比较常见吧。
Thread.sleep(毫秒数);表示让当前线程进入等待状态(单位:毫秒),休眠时间到后,会自动进入就绪状态,但是又可以强占时间块。这个有可能产生异常
Thread.yield();表示强行退出时间块,从runnable状态转变成就绪状态,又可以强占时间块。这个不可能产生异常。
对象.join();方法表示对象加入到runnable状态,正在运行的那个对象等待我运行完,你再来。有点强势。

public class TestThread {
    
    

	public static void main(String[] args) {
    
    
		// TODO Auto-generated method stub
		MyThread my1 = new MyThread();
		my1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即执行时间
		
		MyThread my2 = new MyThread();
		my2.start(); 
		
		//main线程执行
		String main = Thread.currentThread().getName();
		for(int i = 0 ; i < 100 ; i++) {
    
    
			
			try {
    
    //这个让main线程进入等待状态,那么new的其他对象那么就会先运行。
				Thread.sleep(2);
			} catch (InterruptedException e) {
    
    
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		System.out.println(main + "==" + i);
		}
	}

}
public class MyThread extends Thread{
    
    
	
	@Override
	public void run() {
    
    
		try {
    
    
			
			Thread.sleep(1);
		}catch(InterruptedException e){
    
    
			e.printStackTrace();
		}
		String threadName = Thread.currentThread().getName(); 
		//得到当前进程的名字,currentThread表示当前进程
		for(int i = 0 ; i < 100 ; i++) {
    
    
			System.out.println(threadName + "==" + i);
		}
	}
}

那么就会先运行my1,my2这两个线程,然后运行main,当然结果有的时候也不是我门像的那样。正常。
Thread.yield();

public class TestThread {
    
    

	public static void main(String[] args) throws InterruptedException {
    
    
		// TODO Auto-generated method stub
		MyThread my1 = new MyThread();
		my1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即执行时间
		
		MyThread my2 = new MyThread();
		my2.start(); 
		
		//main线程执行
		String main = Thread.currentThread().getName();
		for(int i = 0 ; i < 100 ; i++) {
    
    
			
			if(i == 80) {
    
    
				Thread.yield();
			}
		System.out.println(main + "==" + i);
		}
	}
}
public class MyThread extends Thread{
    
    
	
	@Override
	public void run() {
    
    
		try {
    
    
			
			Thread.sleep(1);
		}catch(InterruptedException e){
    
    
			e.printStackTrace();
		}
		String threadName = Thread.currentThread().getName(); 
		//得到当前进程的名字,currentThread表示当前进程
		for(int i = 0 ; i < 100 ; i++) {
    
    
			System.out.println(threadName + "==" + i);
		}
	}
}

在这里插入图片描述

到了79的时候,main线程就进入了就绪状态,又要去抢占时间块。

public class TestThread {
    
    

	public static void main(String[] args) throws InterruptedException {
    
    
		// TODO Auto-generated method stub
		MyThread my1 = new MyThread();
		my1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即执行时间
		
		MyThread my2 = new MyThread();
		my2.start(); 
		
		//main线程执行
		String main = Thread.currentThread().getName();
		for(int i = 0 ; i < 100 ; i++) {
    
    
			
			if(i == 80) {
    
    
				//Thread.yield();
				my2.join();//那么my2一定能会先执行完毕,除非一种情况,my1,amin之前就已经执行完毕了,没有其他线程进行强占资源。
				my1.join();
			}
		System.out.println(main + "==" + i);
		}
	}
}

改变线程优先级。

方法:对象.setPriority(num); num默认情况是5,num表示的是优先级,num最大只能为10那么优先级就会最大但是也可能出现不是最先进入运行中的状态,num最小只能为1
.`

public class TestThread {
    
    

	public static void main(String[] args) throws InterruptedException {
    
    
		// TODO Auto-generated method stub
		MyThread my1 = new MyThread();
		my1.start(); //启动进入是就绪状态,还没获得cpu调度时间,即执行时间
		
		MyThread my2 = new MyThread();
		my2.start(); 
		
		my2.setPriority(6); //改变优先级也不一定先执行。
		my1.setPriority(9);
		//main线程执行
		String main = Thread.currentThread().getName();
		for(int i = 0 ; i < 100 ; i++) {
    
    
		System.out.println(main + "==" + i);
		}
	}
}

`

线程安全问题

通过加锁的方式来解决,给唯一的对象加锁,要满足两个条件嘛,唯一的以及对象。如果给static方法加锁,那么就是给this加锁;如果給普通方法加锁,锁的就是一般的对象。
加锁是指多个线程同时对一个共享数据进行修改时,先获得修改权限(获取锁)的,会进行加锁(独占),其他的线程会等待(阻塞)当前线程修改完毕后再进行操作。

synchronized加锁就是这个关键字,下面是个普通方法说明了锁this
方法就是
public synchronized void add(){

}

下面这个例子是锁常量,对象,额,这个也不一定对。反正就是要锁不变东西,或者说是对象都要调用的东西。
synchronized (object ,常量){
}

死锁

写程序千万不要造成死锁,因为,这样就会出现很严重的问题。

猜你喜欢

转载自blog.csdn.net/toomemetoo/article/details/112623826
今日推荐