进程与线程
进程: 指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程.
线程: 指进程中的一个执行任务(控制单元),是程序执行时的最小单位,是CPU调度和分派的基本单位,一个进程可以运行多个线程,多个线程可共享资源.
并行与并发
并行: 多个进程同时运行,发生在多CPU中,一个进程占一个CPU,所有进程同步运行,互相之间不抢夺资源
并发: 多个进程间隔运行,发生在单CPU中,每个进程切换运行,切换时间很短,所以看起来好像是同时在运行,互相之间抢夺资源
同步与异步
同步: 多个线程有序执行,前面的执行完了后面的补上,在前面线程运行时其他线程都在等待
异步: 多个线程同时进入就绪状态,等待CPU的统一调度,当线程进入运行状态时其他线程可以访问其他资源,把访问资源的空闲时间利用起来,能提高效率,也就是多线程机制
线程分类
应用线程: 前台线程,执行各种具体任务的线程,一个程序启动至少有一个应用线程(main线程)和一个守护线程(GC)
守护线程: 后台线程,随着前台线程的全部死亡,守护线程也会自动死亡
线程的创建和启动
继承方式创建
自定义类继承Thread
重写run方法,把线程任务封装进run方法中
创建自定义对象
自定义对象调用start()方法,开启线程
// 继承方式创建线程
public class MyThreadDemo extends Thread { // 继承Thread类创建线程
// 重写run方法,把线程任务封装进run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("吃了" + (i + 1) + "顿");
}
}
}
// 开启线程
public class MyThreadTestDemo {
public static void main(String[] args) {
// 创建线程对象,可以使用多态的方法创建, Thread m = new MyThreadDemo();
MyThreadDemo m = new MyThreadDemo();
// 开启线程
m.start();
// 线程开启后进入独立模块,CPU调度随机,也就是说两个线程随机运行
for (int i = 0; i < 10; i++) {
System.out.println("睡了" + (i + 1) + "天");
}
}
}
接口实现方式创建
自定义类实现Runnable接口
重写run方法,把线程任务封装进run方法
创建自定义对象
创建Thread类对象,将自定义对象作为参数传入Thread类创建对象的构造器
Thread对象调用start()方法,开启线程
// 实现Runnable接口创建线程
public class MyRunnable implements Runnable{ // 实现Runnable接口创建线程
// 重写run方法,将线程任务封装进run方法
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println("吃了" + (i + 1) + "顿");
}
}
}
// 启动线程
public class MyRunnableDemo {
public static void main(String[] args) {
// 创建线程对象
MyRunnable m = new MyRunnable();
// 创建Thread类对象,将线程对象作为构造器参数传过去
Thread t = new Thread(m);
// 开启线程
t.start();
for (int i = 0; i < 10; i++) {
System.out.println("睡了" + (i + 1) + "天");
}
}
}
线程的生命周期
当程序创建一个线程以后,线程处于新建状态,无法被CPU调度,需要调用start()方法开启线程,调用start()方法之后线程进入就绪状态,等待CPU调度
当就绪状态的线程被CPU调度时进入运行状态,运行状态和就绪状态可以相互切换,当多个线程切换速度很快时,我们看起来就像多个线程在同步运行,这就是并发
运行状态可以切换到等待状态,它会等待另一个线程来执行一个任务,一个等待状态的线程只有通过另一个线程通知它转到可运行状态,才能继续执行
运行状态转到等待状态可以设置一个计时器,等待特定的时间之后唤醒线程对象
运行状态遇到异常或者其他特殊状况导致不能运行时进入阻塞状态,让出CPU调度,停止自身运行
当线程执行完或者抛出未捕获的异常和错误时,或者调用线程的stop()方法,线程终止,生命周期结束
操作线程的方法
智汇代理申请https://www.kaifx.cn/broker/t...
join方法: 主要作用是同步,它可以使得线程之间的并行执行变为串行执行.比如在A线程中调用了B线程的join()方法时,表示只有当B线程执行完毕时,A线程才能继续执行
sleep方法: 让正在执行的线程暂停一段时间,进入阻塞状态.
// sleep(long milllis) throws InterruptedException; 毫秒为单位
Thread.sleep(1000); // 需要处理异常,使用try-catch
1
2
线程的优先级: 每个线程都有优先级,优先级的高低只和线程获得执行机会的次数多少有关,并不是说优先级高的就一定先执行,哪个线程的先运行取决于CPU的调度.Thread对象的setPriority(int x)和getPriority()用来设置和获得优先级,x的值不能大于线程执行的次数,否则报IllegalThreadStateException异常
后台线程: 守护线程,一般用于为其他线程提供服务,若前台线程都死亡,后台线程自动死亡.Thread对象setDaemon(true)用来设置后台线程,setDaemon(true)必须在start()调用前,否则抛出IllegalThreadStateException异常
线程安全性: 当多线程并发访问同一资源时会导致线程出现安全性的原因
需求: 现有100个苹果,现在有3个人去吃.
继承方式
// 线程类,继承Thread类
public class SafeThread extends Thread {
private int number = 100; // 苹果数量
public SafeThread(String name) { // 构造器,调用父类构造器把值传给父类
super(name);
}
// 重写run方法,把线程任务封装进去
public void run() {
for (int i = 0; i < 100; i++) {
if (number > 0) {
System.out.println(Thread.currentThread().getName()
+ "吃了第" + number-- + "个苹果");
}
}
}
}
// 测试类
public class SafeThreadDemo {
public static void main(String[] args) {
// 创建对象
Thread s1 = new SafeThread("xx");
Thread s2 = new SafeThread("yy");
Thread s3 = new SafeThread("zz");
// 开启线程
s1.start();
s2.start();
s3.start();
}
}
// 结果是3个人每个人都吃了100个苹果,继承方式多个线程不能共享同一资源
实现接口方式
// 创建线程类实现Runnable接口
public class SafeRunnable implements Runnable {
private int number = 100; // 苹果数量
// 重写run方法,把线程任务封装进去
public void run() {
for (int i = 0; i < 100; i++) {
if (number > 0) {
System.out.println(Thread.currentThread().getName()
+ "吃了第" + number-- + "个苹果");
}
}
}
}
// 测试类
public class SafeRunnableDemo {
public static void main(String[] args) {
// 创建线程对象
SafeRunnable s = new SafeRunnable();
// 创建Thread对象把线程对象当作参数参数传过去,开启线程
new Thread(s,"xx").start();
new Thread(s,"yy").start();
new Thread(s,"zz").start();
}
}
// 结果是3个人平分了100个苹果,实现方式可以多个线程共享同一资源
继承方式与实现方式的区别
Java中类是单继承,如果继承了Thread类就不能再继承其他类了,而实现方式不但可以再继承其他类,还可以实现多个接口,所以实现方法比较好用
继承方式多个线程没法共享同一资源,实现方式可以做到多个线程共享同一资源
线程同步
当多线程访问同一资源对象的时候可能会出现线程不安全的问题,实现Runnable接口创建线程的时候看起来可能没有问题,但是出现网络延迟的时候就会出现,这里用线程睡眠来模拟网络延迟
// 添加线程睡眠
public class SafeRunnable implements Runnable {
private int number = 100; // 苹果数量
// 重写run方法,把线程任务封装进去
public void run() {
for (int i = 0; i < 100; i++) {
if (number > 0) {
try {
Thread.sleep(100); // 让线程休眠0.1秒再运行,相当于网络出现0.1秒延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "吃了第" + number-- + "个苹果");
}
}
}
}
// 测试类
public class SafeRunnableDemo {
public static void main(String[] args) {
// 创建线程对象
SafeRunnable s = new SafeRunnable();
// 创建Thread对象把线程对象当作参数参数传过去,开启线程
new Thread(s,"xx").start();
new Thread(s,"yy").start();
new Thread(s,"zz").start();
}
}
解决思路:在一个线程执行该任务的时候其他的线程不能来打扰,也就是设置一个同步锁,比如说A线程进入同步锁进行操作的时候,B和C以及其他的线程只能在外面等着,A操作结束,释放同步锁,A,B,C以及其他线程才会有机会去抢同步锁(A线程只要没有执行完所有任务,同样进入争夺同步锁的行列,谁获得同步锁,谁才能执行代码)
同步代码块
同步锁:
对于非static方法,同步锁就是this
对于static方法,同步锁就是当前方法所在类的字节码对象(.class文件)
public class SafeRunnable implements Runnable {
private int number = 100; // 苹果数量
// 重写run方法,把线程任务封装进去
public void run() {
for (int i = 0; i < 100; i++) {
/*
加同步锁
java允许使用任何对象作为同步监听对象
一般我们把当前并发访问的共同资源作为同步监听对象
这里的SafeRunnable对象拥有三个线程共同的资源,
而且只有一份,所以可以用来做同步锁
*/
synchronized (this) { // 加同步锁
if (number > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "吃了第" + number-- + "个苹果");
}
}
}
}
}
同步方法
public class SafeRunnable implements Runnable {
private int number = 100; // 苹果数量
// 重写run方法,把线程任务封装进去
public void run() {
for (int i = 0; i < 100; i++) {
doWork();
}
}
// synchronized修饰的方法是同步方法
synchronized public void doWork() {
if (number > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+ "吃了第" + number-- + "个苹果");
}
}
}
synchronized的优劣势
好处: 保证了多线程并发访问时的同步操作,避免线程的安全性问题
缺点: 使用synchronized的方法/代码块的性能要低一些
建议: 尽量减小synchronized的作用域
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。