Java基础——多线程1

多线程的三组概念

程序和进程
  1. 程序:在磁盘中存储的一些数据和逻辑的集合,在存储内存中存储。程序不执行,就不会分配内存空间等系统资源,是一个静止的状态。
  2. 进程:正在执行的程序就是进程,表示程序的一次运行。进程是在运行内存中执行,进程也会有其他系统资源的分配。
进程和线程
  1. 进程:正在执行的程序。
  2. 线程:进程中的一条独立的执行路径就是线程。
    特点:
    (1)进程是由线程组成的
    (2)一个进程中至少有一条线程
    (3)如果一个进程中有多条线程,那么当前进程就是一个多线程进程
    (4)线程没有独立的资源分配,进程共用一个进程中的资源
并行和并发

1、并行:在某一个时刻,多个任务同时在执行。 计算机想要实现并行,就需要多个cpu
2、并发:在某一个时刻,多个任务同时发起,不要求同时执行,在某一个时间段内将多个任务都执行一遍。

多线程的第一种实现方式

  1. 方式:继承方式
  2. 步骤:
    1、自定义一个线程类型
    2、继承Thread类型
    3、重写父类中的run方法表示一个具体的任务
    4、在方法中创建一个线程类对象
    5、启动线程
  3. 特点:
    1、新线程的启动需要借助主线程,启动之后两条线程互不影响
    2、一旦启动新线程,当前进程中有多条线程,因为只有一个cpu,所有cpu只能在多条线程之间来回切换,去谁那就执行谁。
    3、启动线程需要使用start方法,而不是调用run方法
public class Demo01 {

	public static void main(String[] args) {
		MyClass m = new MyClass();
		m.start();
		for(int i = 1;i<=100;i++) {
			System.out.println("主线程执行方法"+i);
		}
	}
}
class MyClass extends Thread{
	@Override
	public void run() {
		for (int i = 1; i <=100 ; i++) {
			System.out.println(i+"新线程执行");
		}
	}
}

多线程的第二种实现方式

  1. 方式:实现方式
  2. 步骤:
    1、定义一个类型,表示一个任务类
    2、实现Runnable接口
    3、重写接口中的run方法表示需要执行的任务
    4、在方法中创建一个任务类对象,表示一个具体任务
    5、创建一个Thread类的线程对象,将任务对象提交给线程对象
    6、启动线程
public class Demo02 {

	public static void main(String[] args) {
		MyTask m = new MyTask();
		Thread t = new Thread(m);
		t.start();
		for (int i = 1; i <=100 ; i++) {
			System.out.println("主线程执行"+i);
		}
	}
}
class MyTask implements Runnable{
	@Override
	public void run() {
		for (int i = 1; i <=100 ; i++) {
			System.out.println(i+"新线程执行");
		}
	}
}
两种方式的比较
  1. 代码复杂程度:
    继承的方式比较简单
    实现的方式比较复杂
  2. 灵活性:
    继承的方式:类中的任务只能由当前对象执行,当前对象也只能执行类中定义的任务
    实现的方式:同一个任务可以交给不同的线程对象执行,同一个线程对象可以接收不同的任务。
  3. 扩展性:
    继承的方式不能再继承其他父类
    实现的方式可以继续继承其他父类
多线程的第三种创建方式
  1. 实现Callable接口
  2. 步骤:
    (1)定义一个类实现Callable接口
    (2)在类中重写call()方法
    (3)创建自定义类的对象
    (4)创建Future的实现类FutureTask对象,把自定义类对象作为构造方法的参数传递
    (5)创建Thread类的对象,把FutureTask对象作为构造方法的参数传递
    (6)启动线程
    (7)再调用get方法,就可以获取线程结束之后的结果。
  3. 注意事项:
    (1)实现类重写call,可以返回数据
    比前两种多一个返回值
    (2)要获取返回的数据,需要在启动线程之后获取
public class Demo03 {
	public static void main(String[] args) throws InterruptedException, ExecutionException {
		MyCallable m = new MyCallable();
		FutureTask<String> fu = new FutureTask<String>(m);
		Thread t = new Thread(fu);
		t.start();
		String s = fu.get();
		System.out.println(s);	
	}
}
class MyCallable implements Callable<String>{
	@Override
	public String call() throws Exception {
		for (int i = 1; i <=99 ; i++) {
			System.out.println("我朝她走了"+i+"步");
		}
		return "她向我走了1步";
	}
}

Thread类

获取线程名称
  1. getName() :
    获取当前线程名称,如果没有起名,系统会默认起一个名字 Thread-X
  2. 注意事项:
    -可以直接在线程类中直接调用此方法获取当前对象线程名称
    -在任务类中不能直接调用此方法,因为这个方法属于线程类的方法
public class Demo04 {

	public static void main(String[] args) {
		Thread t = new Thread();
		System.out.println(t.getName());//Thread-0
		XiXi x = new XiXi();
		x.start();//Thread-2
	}
}
class XiXi extends Thread{
	@Override
	public void run() {
		System.out.println(getName());
	}	
}
设置线程名称
  1. setName(String name) :给线程对象设置名称
  2. Thread(String name) :通过构造给线程命名
  3. Thread(Runnable target, String name) :在接收一个任务的同时给线程命名
    注意事项:
    如果创建一个自定义类的对象,通过构造给属性赋值,需要调用父类的构造方法
public class Demo04 {

	public static void main(String[] args) {
		//使用set方法给线程设置名称
		Thread t = new Thread();
		t.setName("一号");
		System.out.println(t.getName());
		//使用有参构造命名
		Thread t2 = new Thread("二号");
		System.out.println(t2.getName());
		
		Runnable r = new Runnable() {		
			@Override
			public void run() {
				System.out.println("新任务!");
			}
		};
		//有任务对象时,给线程命名
		Thread t3 = new Thread(r,"三号");
		System.out.println(t3.getName());
	}
}
class XiXi extends Thread{
}
获取当前线程对象
  1. 概念:
    如果有一段代码想要执行,肯定在某个方法内执行。某个方法想要执行,肯定是通过一条线程来执行。每一段代码都有一个对应的线程执行。获取正在执行这段代码的线程对象。
  2. 方法:
    currentThread() :是一个静态方法,返回的是执行这段代码的线程对象
public class Demo05 {

	public static void main(String[] args) {
		HeiHei h = new HeiHei();
		Thread t1 = new Thread(h,"一号");
		Thread t2 = new Thread(h,"二号");
		t1.start();
		t2.start();
	}

}
class HeiHei implements Runnable{
	@Override
	public void run() {
		System.out.println("需要做的事");
		//哪个线程执行这段代码,就返回哪个线程的对象
		Thread t = Thread.currentThread();
		System.out.println(t.getName());
	}	
}
//打印结果:需要做的事
//一号
//需要做的事
//二号
线程休眠
  1. Thread.sleep(long time):让执行这段代码的线程休息一会
    time:表示线程休息的时间,单位ms
  2. 特点:
    无论哪个线程执行这个方法,该线程都会主动休息time毫秒
  3. 注意事项:
    -在使用的时候会出现一个异常:线程中断异常
    -在普通方法中,可以对这个异常进行声明
    -在run方法中,只能捕获,不能声明,因为run方法是重写的父类的,父类中没有声明异常子类在重写的时候也不能有任何异常的声明。
public class Demo07 {
//倒数十秒
	public static void main(String[] args) {
		KeKe k = new KeKe();
		Thread t = new Thread(k);
		t.start();
	}

}
class KeKe implements Runnable{
	@Override
	public void run() {
		for (int i = 10; i >=1 ; i--) {
			System.out.println(i);
				try {
					//ctrl 1
					Thread.sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
	
}
守护线程
  1. 概念:保护其他线程能够正常运行的线程就是守护线程。
  2. 方法:
    -setDaemon(boolean on) :将当前线程对象设置为指定线程,参数为true为守护线程否则为非守护线程。
    -isDaemon():判断当前对象是否是一个守护线程
  3. 特点:
    1、随便定义一个线程都默认为非守护线程
    2、如果守护线程要守护的线程都结束了,守护线程也会跟着结束。
public class Demo08 {

	public static void main(String[] args) {
		Thread t = new Thread() {
			@Override
			public void run() {
				while(true) {
					System.out.println("新线程工作");
				}
			}
		};
		t.setDaemon(true);
		System.out.println(t.isDaemon());
		t.start();
		System.out.println("主方法结束");
		
	}

}
线程优先级
  1. 概念:每个线程在被cpu执行的时候都具有相同的优先级,被执行的机会一样大。可以通过某个方法让线程的优先级进行调整,优先级大的线程被执行的机会越大,优先级低的线程被执行的机会越小。
  2. 修改优先级的方法:
    setPriority(int newPriority) :设置线程优先级
    优先级的范围:1–10 1最小优先级 10最大优先级
    如果不设置优先级, 每个线程都是默认为5优先级
  3. 静态常量:
    static int MAX_PRIORITY
    线程可以拥有的最大优先级。 10
    static int MIN_PRIORITY
    线程可以拥有的最小优先级。 1
    static int NORM_PRIORITY
    分配给线程的默认优先级。 5
public static void main(String[] args) {
		Thread t = new Thread("一号一号") {
			@Override
			public void run() {
				for (int i = 1; i <=100 ; i++) {
					System.out.println(getName()+" "+i);
				}
			}	
		};
		Thread t1 = new Thread("二号") {
			@Override
			public void run() {
				for (int i = 1; i <=100 ; i++) {
					System.out.println(i+" "+getName());
				}
			}	
		};
		//将一号设为最低优先级,每次被cpu执行的机会低,不代表后执行
		t.setPriority(Thread.MIN_PRIORITY);//t.setPriority(1);
		//将二号设为最高优先级,每次被cpu执行的机会高,不代表先执行
		t1.setPriority(Thread.MAX_PRIORITY);//t1.setPriority(10);
		t.start();
		t1.start();
	}

线程安全问题

  1. 其中一个线程在操作某一个数据的时候,这个数据可能也在被另外一条线程操作,就造成多个线程同时在操操作同一个数据,碰到这种情况,此数据就有可能出现错误问题。
  2. 避免方式:
    在执行某一段代码的时候,不要去执行能影响当前代码的代码上去。
    保证代码的完整性,一致性
同步代码块
  1. 格式:
    synchronized(锁对象){
    需要保证完整性的代码。
    }
  2. 原理:
    cpu执行带着锁的线程时,该线程需要先获取锁对象,获取到锁对象之后cpu才能执行,并且该段代码不执行结束,该线程就不会释放锁对象,代码执行完之后才会释放锁对象。
  3. 注意事项:
    如果两段代码之间需要互不影响,两个代码段的锁对象必须是一个锁对象。
    一般使用本类的字节码对象
public class Demo10 {

	public static void main(String[] args) {
		Print p = new Print();
		Thread t = new Thread() {
			public void run() {
				while(true) {
					p.print1();
				}
			}	
		};
		Thread t2 = new Thread() {
			public void run() {
				while(true) {
					p.print2();
				}
			}	
		};
		t.start();
		t2.start();
	}

}
class Print{
	public void print1() {
		synchronized (Print.class) {
			System.out.print("全");
			System.out.print("世");
			System.out.println("界");
		}
	}
	public void print2() {
		synchronized (Print.class) {
			System.out.print("好像");
			System.out.print("只有");
			System.out.print("我");
			System.out.println("疲惫");
		}
	}
}
同步方法
  1. 概念:如果一个方法中的所有内容都需要保持同步,可以使用同步方法来代替同步代码块以达到简化代码的操作。
  2. 格式:
    修饰符 synchronized 返回值类型 方法名称(){
    }
  3. 如果这个方法是一个非静态方法,那么锁对象默认为当前对象。This
  4. 如果这个方法是一个静态方法,锁对象默认为当前类的字节码对象.class对象
class Print{
	public static synchronized void print1() {
			System.out.print("全");
			System.out.print("世");
			System.out.println("界");
	}
	public static synchronized void print2() {
		synchronized (Print.class) {
			System.out.print("好像");
			System.out.print("只有");
			System.out.print("我");
			System.out.println("疲惫");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值