day15 :线程 -> java基础大神进阶学习总结之19天(知识点全面覆盖,细节)

java基础总结


day15 线程

1.关于进程与线程的理解

进程:是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以有多个线程

线程:是指进程中的一个执行任务(控制单元),一个进程中可以运行多个线程,多个线程可共享数据。

主线程 main(了解)

在运行一个简单的Java程序的时候,就已经存在了两个线程,一个是主线程,一个是后台线程——维护的垃圾回收。主线程很特殊,在启动JVM的时候自动启动的。

2.线程的创建和启动(两种方式)以及优缺点对比

1.继承Thread类

class MyThread extends Thread {
	public void run() {
		//自定义线程中的for循环打印i,打印顺序是完全随机的。
		for (int i = 0; i < 10; i++) {
			System.out.println("MyThread  ==> " + i);
		}
	}
}

public class Demo {
	public static void main(String[] args) {
		MyThread mt = new MyThread();
		mt.start();
		//主线程中的for循环打印i
		for (int i = 0; i < 10; i++) {
			System.out.println("main  ==> " + i);
		}
	}
}

2.实现Runable接口

class MyRunnable implements Runnable {
	public void run() {
		//线程体,线程启动时,会自动调用本方法,所有这里是我们写代码的主体部分
	}
}

public class ThreadDemo2 {
	public static void main(String[] args) {
		MyRunnable target = new MyRunnable();
		Thread t = new Thread(target);
		t.start();
	}
}

注意:启动一个新线程,不能使用run()方法,只能使用start方法。

3.对比:

继承方式

  • Java中类是单继承的,如果继承了Thread了,该类就不能再有其他的直接父类了。

  • 从操作上分析,继承方式更简单,获取线程名字也简单。

  • 从多线程共享同一个资源上分析,继承方式不能多个线程共享同一个资源

实现方式

  • Java中类可以多实现接口,此时该类还可以继承其他类,并且还可以实现其他接口(设计上,更优雅)

  • 从操作上分析,获取线程名字也比较复杂,得使用Thread.currentThread()来获取当前线程的引用。

  • 从多线程共享同一个资源上分析,实现方式可以多线程共享同一个资源

3.线程的同步

class Apple implements Runnable {
	private int num = 50;//苹果总数

public void run() {
	try {
		for (int i = 0; i < 50; i++) {
			if (num > 0) {
				Thread.sleep(10);
				System.out.println(
                   Thread.currentThread().getName()+ "吃了编号为:" + num-- + "的苹果"
                );
			}
		}
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

}

分析运行结果,为什么有错误的结果。

在这里,打印苹果的编号和苹果数量的减少,应该是一个原子操作,也就说是一个不能分割的操作,两个步骤之间不能被其他线程插一脚。

第一步:System.out.println(Thread.currentThread().getName()
"吃了编号为:" + num + "的苹果");
第二步:num --;

4.synchronized同步代码块

注意:在任何时候,最多允许一个线程拥有同步锁,谁拿到锁就执行,其他的线程只能在代码块外等着。

当同一个对象

class Apple implements Runnable {
	private int num = 50;//苹果总数

public void run() {
	try {
		for (int i = 0; i < 50; i++) {
			synchronized (this) {
				if (num > 0) {
					Thread.sleep(10);
					System.out.println(Thread.currentThread().getName() 
"吃了编号为:" + num-- + "的苹果");
				}
			}
		}
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}
}

此时的同步锁this表示Apple对象,而程序中Apple对象只有一份,故可以作为同步锁。

当new 对象多个时,锁不能用this

package 多线程.第五题;

public class Student1 extends Thread{

    public Student1(String name){
        super(name);
    }
    private static int number = 50;
    private static Object c = new Object();
    @Override
    public void run() {
        for(int i = 0; i < 50; i++){
           synchronized (c){
               if(number > 0){
                   System.out.println(getName()+"吃苹果:"+number--);
                   try {
                       Thread.sleep(200);
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
           }
        }
    }
}

5.synchronized同步方法

synchronized  public  void  doWork(){
     ///TODO
}

此时同步锁是谁——其实就是,调用当前同步方法的对象:

  • 对于非static方法,同步锁就是this。

  • 对于static方法,同步锁就是当前方法所在类的字节码对象。

class Apple implements Runnable {
	private int num = 50;//苹果总数

public void run() {
	for (int i = 0; i < 50; i++) {
		this.eat();
	}
}

synchronized private void eat() {
	try {
		if (num > 0) {
			Thread.sleep(10);
			System.out.println(
                Thread.currentThread().getName() + "吃了编号为:" + num-- + "的苹果"
            );
		}
	} catch (InterruptedException e) {
		e.printStackTrace();
	}
}

}

6.synchronized的优劣(掌握)

好处:保证了多线程并发访问时的同步操作,避免线程的安全性问题。(多线程并发修改同一个资源时存在问题)

缺点:使用synchronized的方法/代码块的性能要低一些。

建议:尽量减小synchronized的作用域。

通过源代码会发现,主要就是方法有没有使用synchronized的区别,比如StringBuilder和StringBuffer。

在这里插入图片描述

因此得出结论:使用synchronized修饰的方法性能较低,但是安全性较高,反之则反。

猜你喜欢

转载自blog.csdn.net/weixin_41340417/article/details/107889839