Java笔记-多线程

多线程

1.1.程序

程序:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象

1.2.进程

进程:是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期

  • 程序是静态的,进程是动态的
  • 进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域

1.3.线程

进程可进一步细化为线程,是一个程序内部的一条执行路径。

  • 若一个进程同一时间并行执行多个线程,就是支持多线程的。
  • 线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小 。
  • 一个进程中的多个线程共享相同的内存单元/内存地址空间,它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高效。
    在这里插入图片描述

2.线程的使用

2.1.继承Thread类

构造器
➢Thread():创建新的Thread对象
➢Thread(String threadname):创建线程并指定线程实例名
➢Thread(Runnable target):指定创建线程的目标对象,它实现了Runnable接口中的run方法
➢Thread(Runnable target, String name):创建新的Thread对象

//线程类,继承Thread类来实现,重写父类run方法
public class MyThread extends Thread {

	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " MyThread " + i);
		}
	}
}
public class MyThreadTest {

	public static void main(String[] args) {
		System.out.println("主线程开始。。。");
		
		Thread t1 = new MyThread();
		t1.setName("t1");
		t1.start();
		
		Thread t2 = new MyThread();
		t2.setName("t2");
		t2.start();
		
		//只要有一个线程存活,就会等待
		while(Thread.activeCount()>1) {
			Thread.yield();
		}
		
		System.out.println("主线程结束。。。");
	}
}

定义子类继承Thread类。
子类中重写Thread类中的run方法。
创建Thread子类对象,即创建了线程对象。
调用线程对象start方法:启动线程,调用run方法。

2.2.实现Runnable接口

  1. 定义子类,实现Runnable接口。
  2. 子类中重写Runnable接口中的run方法。
  3. 通过Thread类含参构造器创建线程对象。
  4. 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
  5. 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。
public class MyRunnable implements Runnable {
	
	@Override
	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName() + " MyRunnable " + i);
		}
	}

}
public class MyRunnableTest {

	public static void main(String[] args) {
		System.out.println("主线程开始");
		
		//启动子线程1
		new Thread(new MyRunnable(),"r1").start();
		//启动子线程2
		new Thread(new MyRunnable(),"r2").start();
		
		//只要有一个线程存活,就会等待
		while(Thread.activeCount()>1) {
			Thread.yield();
		}
		
		System.out.println("主线程结束");
	}

}

2.3.实现Callable接口

与使用Runnable相比,Callable功能更强大些
➢相比run()方法,可以有返回值
➢方法可以抛出异常
➢支持泛型的返回值
➢需要借助FutureTask类, 比如获取返回结果

Future接口
➢可以对具体Runnable、Callable任 务的执行结果进行取消、查询是否完成、获取结果等。
➢FutrueTask是Futrue接 口的唯一的实现类
➢FutureTask 同时实现了Runnable,Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

	@Override
	public String call() throws Exception {
		System.out.println("请求接口。。。");
		return "MyCallable call()";
	}

}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class MyCallableTest {

	public static void main(String[] args) throws InterruptedException, ExecutionException {
		FutureTask ft = new FutureTask(new MyCallable());
		new Thread(ft).start();
		//是否退出
		System.out.println("ft.isCancelled() "   + ft.isCancelled());
		//是否完成
		System.out.println("ft.isDone() "   + ft.isDone());
		//获取返回值
		System.out.println(ft.get());
		//线程退出
		ft.cancel(true);
	}

}

3.线程同步

线程同步用来解决线程安全、线程冲突问题。
问题的原因:
当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以参与执行。

3.1.Synchronized的使用方法

public class SyncAcount {
	private int num = 100;
	private boolean flag = true;

	// 不安全的方法
	public void add(int n) {
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		num = num + n;
	}

	public void sub(int n) {
		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		num = num - n;
	}

	// 安全的方法,有同步锁
	public synchronized void syncAdd(int n) throws InterruptedException {
		Thread.sleep(50);

		while (!flag) {
			// 标志位不为真则等待
			this.wait();
		}
		num = num + n;
		flag = false;
		// 唤醒等待的线程
		this.notify();
		System.out.println("syncAdd " + n);
	}

	// 安全的方法,有同步锁
	public synchronized void syncSub(int n) throws InterruptedException {
		Thread.sleep(50);
		while (flag) {
			// 标志位为真则等待
			this.wait();
		}

		num = num - n;
		flag = true;
		// 唤醒等待的线程
		this.notify();
		System.out.println("syncSub " + n);

	}

	public int getNum() {
		return num;
	}
}

public class SyncAcountTest {

	public static void main(String[] args) {
		SyncAcount sa = new SyncAcount();
		for (int i = 0; i < 5; i++) {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
					//不安全的调用方法
//					sa.add(10);
//					sa.sub(10);
					
					//安全的调用方式
					try {
						sa.syncAdd(10);
						sa.syncSub(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}).start();
		}
		
		while(Thread.activeCount()>1) {
			Thread.yield();
		}
		
		System.out.println(sa.getNum());
	}

}

3.2.ReentrantLock使用方法

●从JDK 5.0开始,Java提供了更强大的线程同步机制,通过显式定义同步锁对象来实现同步。同步锁使用Lock对象充当。
●java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源之前应先获得Lock对象。
●ReentrantLock 类实现了Lock ,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用的是ReentrantLock,可以显式加锁、释放锁。


import java.util.concurrent.locks.ReentrantLock;

public class LockTest {
	public static void main(String[] args) {
		Lock2 1 = new Lock2();
		for(int i=0;i < 10;i++){
			new Thread(new Runnable() {
				@Override
				public void run() {
					1.add(10);
				},"Thread"+i).start();
		//主线程让出CPU时间
		while (Thread.activeCount() > 1) {
			Thread.yield();
		}
		System.out.println(1.acount);
	}

	static class Lock2 {
		private int acount = 100;
		private final ReentrantLock lock = new ReentrantLock();
		
		public void add(int num) {
			try {
				Thread.sleep(20);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			lock.lock();
			try{
				acount = acount + num;
				System.out.println(Thread.currentThread().getName() +":"+ acount);
			} finally{
				lock.unlock();
			}
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值