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

多线程与高并发

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

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

线程

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

线程的概念

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

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

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

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

3ad8eb98b31ab5c48889db21324c124c.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,**优先级高并不一定会优先执行,只是优先执行的概率更高。**

3d8b20c9b4d630e8444660c79af4d40c.png

6、interrupt()

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

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

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

7、wait()-- 来自Object方法

某个对象调用wait方法,会让当前线程进入等待状态,需要注意的是,这个wait()方法会释放线程持有的锁,意味着进入等待状态后,别的线程就有机会获得该线程释放出来的锁了。

8、notify() / notifyAll()-- 来自Object方法

某对象调用notify()方法,随机唤醒一个wait线程,让其进入就绪状态。

某对象调用notifyAll()方法,唤醒全部wait线程,让其进入就绪状态。

图解线程

4cb1ef4ca635f060496869e51ef68fd1.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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值