java 多线程和同步_JAVA的多线程与高并发(一)、线程与同步锁

多线程与高并发

写下本系列文章,用以记录学习多线程与高并发的过程。

一些比较基础的知识会直接带过,不做详细说明。

线程

下面会从介绍线程到实现java多线程来讲。

线程的概念

线程的概念在我之前写的《Java开发者需要了解的硬件知识 (二)、操作系统篇》中已经讲述过了。搬过来用一用。

进程是OS分配资源的基本单位,线程是执行调度的基本单位。

进程最重要的分配资源是:独立的内存空间。

线程调度执行(线程共享进程的内存空间,没有自己独立的内存空间)

5bc0951a05d6053b1ba8c0bbeb17538e.png

线程在Linux中的实现

线程在Linux就是一个普通的进程,只不过和其他进程共享资源(内存空间,全局数据等。。。)

在JVM中的线程

创建一个JVM中线程,即申请一个操作系统的线程(重量级线程),1:1关系

JAVA中的线程

创建线程的两种方式

public class createThred {

static class Thread1 extends Thread {

@Override

public void run() {

System.out.println("Thread1");

}

}

static class Runnable1 implements Runnable {

public void run() {

System.out.println("Thread2");

}

}

public static void main(String[] args) {

// 第一种:写一个类继承Thread类,重写它的run方法

Thread1 thread1 = new Thread1();

thread1.start();

// 第二种:写一个类实现Runnable接口,然后使用新类作为参数来创建新线程

Thread thread2 = new Thread(new Runnable1());

thread2.start();

// 第二种的拓展,在Jdk8后使用lambda表达式

Thread thread3 = new Thread(() -> System.out.println("Thread3"));

thread3.start();

}

}

创建线程有两种方式,启动线程上有三种方式。

前两种就是创建线程的那两种,第三种启动线程的方式是在线程池中创建线程(使用Executors.newCachedThread,实际上内部还是前两种来实现的)

线程的状态

JAVA中线程的状态可以主要分成6种

新生状态(NEW):线程刚被创建出来,还未调用start()方法

可运行状态(RUNNABLE):处于该状态的线程随时可以被操作系统调度执行,它还可以根据线程工作情况细分为①就绪状态Ready②正在执行状态Running。

阻塞状态(BLOCKED):线程进入同步代码块前,需要获取锁,而在等待获取锁时,线程就会进入阻塞状态,获得锁后则进入就绪状态。

等待状态(WAITING):等待状态的线程都在等待另一个线程的特定操作(以解除它的等待状态),等待状态结束回到就绪状态。

超时等待状态(TIMED_WAITING):线程在等待另一个线程的特定操作后的指定时间内,会处于超时等待状态,直至操作完成并等待足够的时间后线程会被自动唤醒,并回到就绪状态

终结状态(TERMINATED):线程完成了他的执行方法后即进入终结状态,而且终结状态的线程无法再start()。/**

* Thread state for a thread which has not yet started.

*/

NEW,

/**

* Thread state for a runnable thread. A thread in the runnable

* state is executing in the Java virtual machine but it may

* be waiting for other resources from the operating system

* such as processor.

*/

RUNNABLE,

/**

* Thread state for a thread blocked waiting for a monitor lock.

* A thread in the blocked state is waiting for a monitor lock

* to enter a synchronized block/method or

* reenter a synchronized block/method after calling

* {@link Object#wait() Object.wait}.

*/

BLOCKED,

/**

* Thread state for a waiting thread.

* A thread is in the waiting state due to calling one of the

* following methods:

*

*

{@link Object#wait() Object.wait} with no timeout

*

{@link #join() Thread.join} with no timeout

*

{@link LockSupport#park() LockSupport.park}

*

*

*

A thread in the waiting state is waiting for another thread to

* perform a particular action.

*

* For example, a thread that has called {@code Object.wait()}

* on an object is waiting for another thread to call

* {@code Object.notify()} or {@code Object.notifyAll()} on

* that object. A thread that has called {@code Thread.join()}

* is waiting for a specified thread to terminate.

*/

WAITING,

/**

* Thread state for a waiting thread with a specified waiting time.

* A thread is in the timed waiting state due to calling one of

* the following methods with a specified positive waiting time:

*

*

{@link #sleep Thread.sleep}

*

{@link Object#wait(long) Object.wait} with timeout

*

{@link #join(long) Thread.join} with timeout

*

{@link LockSupport#parkNanos LockSupport.parkNanos}

*

{@link LockSupport#parkUntil LockSupport.parkUntil}

*

*/

TIMED_WAITING,

/**

* Thread state for a terminated thread.

* The thread has completed execution.

*/

TERMINATED;

线程的基本方法

1、sleep(long millis)

​使线程睡眠millis毫秒,线程进入时间等待状态(TIMED_WAITING),睡眠完后线程回到就绪状态。

2、yeild()

​使当前线程进入等待队列(就绪状态),等待操作系统下一次调度。

3、join()

​在T1线程中调用T2的join()方法时,T1线程则会等待T2线程结束才进入等待队列(就绪状态)。(调用自己线程的join方法是没有意义的)

4、isAlive()

​判断线程是否活动,返回boolean,在调用start()后与线程死亡前都是活动状态。

5、getPriority()/setPriority(int newPriority)

​获取线程优先级与设置线程优先级,下图为线程优先级参考表,一般为5,优先级高并不一定会优先执行,只是优先执行的概率更高。

3626c2b2e660c451de970dcf58fd6b40.png

6、interrupt()

>中断线程,**interrupt() 并不能真正的中断线程,这点要谨记。需要被调用的线程自己进行配合才行。也就是说,一个线程如果有被中断的需求,那么就需要这样做:**

>

>1. 在正常运行任务时,经常检查本线程的中断标志位,如果被设置了中断标志就自行停止线程。

>2. 在调用阻塞方法时正确处理InterruptedException异常。(例如:catch异常后就结束线程。)

图解线程

o_200917162157%E7%BA%BF%E7%A8%8B%E6%89%A7%E8%A1%8C%E5%9B%BE.png

同步锁Synchronized

本篇只是简要说明synchronized关键字的一些使用场景和使用细节。

在下一篇中,会详细的分析synchronized锁的实现和它的内部原理。

synchronized的锁定对象

1、指定对象

你可以指定一个对象,使用synchronized关键字来加锁

public class T {

private int count = 10;

private Object o = new Object();

public void m() {

// 你也可以指定this对象

// synchronized (this)

synchronized (o)

{

count--;

}

}

}

2、修饰方法

也可以直接在方法属性中使用synchronized,如此一来,会默认把调用该方法的对象作为锁(也就是this)

public class T {

private int count = 10;

public synchronized void m() {

// 此时与上面第一种锁定this对象的效果是一样的

count--;

}

}

3、修饰静态方法

使用静态方法是不需要对象的,如果synchronized修饰静态方法,那么锁定的对象将是T.class,类的对象

public class T {

private int count = 10;

public synchronized static void m() {

// 此时与上面第一种锁定this对象的效果是一样的

count--;

}

}

可重入性

如果在同一个线程中,对于一个对象,允许同步方法A的代码块中调用同步方法B。(前提是处于一个类中,锁的是同一个对象)

比如下面这块代码

public class T {

synchronized void m1 () {

System.out.println("m1");

m2();

}

synchronized void m2 () {

System.out.println("m1");

}

}

synchronized必须是可重入锁,因为java中A继承B,A重写B的方法时的super.m()是可以正常执行的(无论这个方法是不是加了synchronized)。如果不是可重入的,那么这样的继承重写就没办法实现。

异常

如果在程序执行中抛出异常,那么锁会被释放。可能会因此产生不可预知的结果。

如果不想释放锁,可以主动catch产生的异常。

锁升级

synchronized在JDK早期,使用时会向操作系统申请重量级锁。在随后的升级版本中,加入了锁升级的概念。

意味着,synchronized不再是直接向操作系统申请重量级锁,而是一步步升级为重量级锁。

其中的过程有:

1、偏向锁

​当没有任何竞争时(单线程执行synchronized代码时)

2、自旋锁

​少量线程竞争

3、系统锁

​大量线程竞争

具体实现

由于synchronized具体实现牵扯到了许多方面的知识,新开一篇文章讲,会写在下一篇里。。。。。。

内容来源于网络如有侵权请私信删除

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值