文章目录
程序、进程、线程
程序:是为完成特定任务、用某种语言编写的一组指令的集合。即一段静态的代码,静态对象。
进程:是程序产生的一次执行过程,或是正在运行的一个程序。是一个动态的过程:由它自身
的产生、存在和消亡的过程——生命周期。
>如:运行中的QQ,运行中的MP3播放器
>程序是静态的,进程是动态的
>进程作为资源分配的单位,系统在运行时会为每个进程分匹配不同的内存区域
线程:进程可进一步细化为线程,是一个程序内部的一条执行的路径。
>若一个进程同时并行执行多个线程,就是支持多线程的
>线程作为进程调度和执行的最小单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
>一个进程中的多个线程共享相同的内存单元/内存地址空间->他们从同一堆中分配对象,可以访问呢相同的
变量和对象。这就使得线程间通信更便捷、高效、。但是多个线程操作共享的系统资源可能会带来安全的隐患。
创建线程
1、创建线程的方式
-1:继承Thread类(Thread类实现了Runnable接口)
-2:实现Runnable接口(推荐使用。类实现了接口,还可以继承其他类,接口多实现,继承是单继承)
----------------------------------------
继承Thread类:
public class ThreadA extends Thread{
//重写 run方法
@Override
public void run(){
}
}
实现Runnable接口:
public class ThreadA implements Runnable{
//重写 run方法
@Override
public void run(){
}
}
启动线程
1、调用线程的start()方法。注:调用start()方法才是真正的启动线程,而调用run()方法,相当于
调用了一个普通的方法,不能启动线程。
-------------------------------------------------------
继承Thread的启动线程方式:
ThreadA t = new ThreadA();
t.start();
实现Runnable接口的启动线程方式:
/**
* Thread类中的方法
* ThreadA t1= new Thread(Runnable target)
*/
ThreadA t = new Thread(new ThreadA());
t.start();
线程安全
-
出现线程安全的原因
1、多个线程执行的不确定性引起执行结果的不稳定 2、如:多个线程对账本的共享,会造成操作的不完整性,会破坏数据
如下图,账户原始余额为3000,A和B同时(并发)从账户中取出2000元,账户余额变成-1000,这种问题就是线程安全问题。
-
解决线程安全问题——线程同步
方式一:同步代码块 1、同步监视器,俗称:锁。任何一个类的对象都可以充当锁,要求多个线程必须共用同一把锁 2、同步监视器可以是:当前类.class(Class clazz = 当前类.class(),类只加载一次, 所以这种方式创建的对象是唯一的)、其他类对象、this(当前类的对象) 3、在继承Thread类创建多线程的方式中,慎用this(当前对象)充当同步监视器(确保) 4、在实现Runnable接口创建多线程的方式中,我们可以考虑使用this作为同步监视器 synchronized(同步监视器){ //需要同步的代码 (操作共享数据的代码,如:火车票的数量),同步的代码不能包含少了,也不能包含多了 } 方式二:同步方法 优点:解决了线程安全的问题 缺点:操作同步代码时。只能一个线程参与,其他线程等待。相当于是一个单线程过程,效率低 1、如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明为同步方法 2、总结: (1)同步方法仍然涉及到同步监视器,只是不需要我们显示的声明 (2)非静态的同步方法,同步监视器是 this; 静态的同步方法,同步监视器是:当前类本身(类.class)。
解决线程安全问题(实现Runnable)——同步代码块
public class ThreadA implements Runnable{
private int ticket = 100;
/**
* 正确方式
*/
//Object obj = new Object();
@Override
public void run() {
//错误方式 创建了多个Object对象
//Object obj = new Object();
while (true){
//错误方式 创建了多个Object对象
//synchronized(new Object()) {
//synchronized(ThreadA .class) {
synchronized(this) {
//共用一把锁
//synchronized(obj) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":买票,票号为" + ticket);
ticket--;
} else {
break;
}
}
}
}
public static void main(String[] args) {
ThreadA t = new ThreadA();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
解决线程安全问题(实现Runnable)——同步方法
同步方法的同步监视器为this
public class ThreadC implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
}
}
//同步方法,使用关键字synchronized修饰方法
private synchronized void sellTicket(){
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",买票,票号:" + ticket);
ticket--;
}
}
public static void main(String[] args) {
ThreadC t = new ThreadC();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
解决线程安全问题(继承Thread)——同步代码块
public class ThreadB extends Thread{
private int ticket = 100;
//正确方式
//private Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (ThreadB.class){ //正确方式
// synchronized (obje){ //正确方式
// synchronized (this){ //该方式会有错误
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",买票,票号:"+ticket);
ticket--;
}else{
break;
}
}
}
}
public static void main(String[] args) {
ThreadB t = new ThreadB();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
解决线程安全问题(继承Thread)——同步方法
public class ThreadD extends Thread{
private int ticket = 100;
@Override
public void run() {
while (true){
sellTicket();
}
}
//private synchronized void sellTicket(){ //错误的解决方式
private static synchronized void sellTicket(){ // 监视器为ThreadD.class
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ",买票,票号:" + ticket);
ticket--;
}
}
public static void main(String[] args) {
ThreadD t = new ThreadD();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
Thread t3 = new Thread(t);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
解决线程安全问题方式三:Lock锁 — JDK5.0新增
class Window implements Runnable{
private int ticket = 100;
/**
* 实例化锁
* 参数 fair(boolean):
* true-公平的竞争锁
* false(默认)-随机竞争锁
* 当使用继承的方式实现多线程时,由于创建了多个对象,就需要将lock设置为static转为类层级,保证锁的唯一
*/
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true){
try {
//加锁 保证执行下面的代码是单线程的
lock.lock();
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",买票,票号:"+ticket);
ticket--;
}else {
break;
}
} finally {
//解锁
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口1");
t3.setName("窗口1");
t1.start();
t2.start();
t3.start();
}
}
Synchronized与Lock的异同
▼相同:
二者都可以解决线程安全问题
▼不同:
1、Synchronized会在执行完相应的同步代码以后会自动释放锁(同步监视器)。
2、Lock需要我们手动添加锁,调用lock()方法,并且需要手动的释放锁,调用unlock()方法。
3、使用Lock锁,JVM会花费较少的时间去调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)。
▼两种方式优先使用顺序:
Lock→同步代码块(已经进入了方法体,分配了相应的资源)→同步方法(在方法体之外)
来源:CSDN
作者:wwhcome
链接:https://blog.csdn.net/qq_36875246/article/details/102055971