多线程简学

多线程学习

线程相关概念

进程:是计算机中程序关于某数据集合上的一次运行活动,是操作系统进行资源分配与调度的基本单位。简单的理解就是操作系统中正在运行的一个程序。

线程:就是进程的一个执行单元,一个单一顺序的控制流。一个进程至少有一个线程(main函数),进程是线程的容器。每个线程都有自己的线程栈,自己的寄存器环境,自己的线程本地存储。

主线程:JVM启动时会创建一个主线程,该主线程负责执行main方法,主线程就是执行main方法的线程,线程之间存在父子关系,子线程和父线程 属于包含和被包含

串行、并行、并发关系图

多线程简学

 

 

多线程简学

 

多线程简学

 

并行可以理解为严格更理想的并发

在java中,创建一个线程就是创建一个Thread类(子类)的对象(实例)

Thread类有两个常用的构造方法:Thread()与Thread(Runnable)

定义Thread子类

public class ThreadDemo extends  Thread {
   @Override
   public void run() {
       for (int i = 0; i < 10; i++) {
           try {
               Thread.sleep(1000);
               System.out.println(i);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
  }
}

定义一个Runnable接口的实现类

public class RunnableDemo  implements Runnable{
   @Override
   public void run() {
       for (int i = 0; i < 10; i++) {
           try {
               Thread.sleep(1000);
               System.out.println(i);
          } catch (InterruptedException e) {
               e.printStackTrace();
          }
      }
  }
}

这俩种创建线程的实质没有区别

主线程调用

 public static void main(String[] args) {
       ThreadDemo basic = new ThreadDemo();
       basic.start();
       new Thread(new RunnableDemo()).start();
  }

start()方法来启动线程,但是start()的调用顺序并不一定是一定的,是线程调度器来分配执行顺序

线程的设置名称和获取名称 名称的作用主要是为了测试时使用

Thread.currentThread().getName() 获取线程名称
Thread.currentThread().setName("t1") 设置线程名称

isAlive方法测试线程活动状态

boolean alive = basic.isAlive();判断线程是否是激活状态

sleep()方法 让当前线程休眠毫秒数

 Thread.sleep(1100);

getId()可以获得线程的唯一标识

 long id = basic.getId();

yield()方法的作用是放弃当前CPU资源

 basic.yield();
basic.setPriority(5); 设置线程的优先值  1到10
 basic.setDaemon(true);守护线程  主线程结束后 子线程也会停止

线程的生命周期是线程对象的生老病死,即线程的状态,线程的生命周期可以通过getState()方法获得,线程的状态是枚举类型定义的 具体的可以查看javaApi Enum Thread.State

public enum State {
/**
* 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:
* <ul>
* <li>{@link Object#wait() Object.wait} with no timeout</li>
* <li>{@link #join() Thread.join} with no timeout</li>
* <li>{@link LockSupport#park() LockSupport.park}</li>
* </ul>
*
* <p>A thread in the waiting state is waiting for another thread to
* perform a particular action.
*
* For example, a thread that has called <tt>Object.wait()</tt>
* on an object is waiting for another thread to call
* <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
* that object. A thread that has called <tt>Thread.join()</tt>
* 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:
* <ul>
* <li>{@link #sleep Thread.sleep}</li>
* <li>{@link Object#wait(long) Object.wait} with timeout</li>
* <li>{@link #join(long) Thread.join} with timeout</li>
* <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
* <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
* </ul>
*/
TIMED_WAITING,

/**
* Thread state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}

线程安全问题

非线程安全:主要是指多个线程对同一个对象的实例变量进行操作,会出现值被更改,值不同步的情况。

原子性:就是不可分割的意思,多个线程对共享变量进行操作时,要么已经开始 要么已经结束

访问(读写)某个共享变量的操作从其他线程来看,该操作要么已经执行完毕,要么尚未发生,即其他线程到当前操作的中间结果,如从ATM机取款

class MyInt{
//java中提供了一个线程安全的AtomicInteger类 ,保证了操作的原子性
AtomicInteger integer = new AtomicInteger();
public int getInt(){
return integer.getAndIncrement();
}

可见性:一个线程对某个共享变量进行更新,后续其他的线程可能无法立即读到这个更新的结果,这就是线程安全问题的另外一种形式。

如果一个线程对共享变量更新后,后续访问该线程的其他线程可以读到更新的结果,称这个线程对共享变量的更新对其他线程可见。

java内存

多线程简学

 

锁概述

线程安全问题就是多个线程并发访问共享数据

将多个线程对共享数据的并发访问转换为串行访问,即一个共享数据一次只能被一个线程访问。

锁可以理解为对共享数据进行保护的一个许可证,任何线程想访问这些共享数据必须先持有该许可证。访问后释放其持有的锁(许可证)

锁具有排他性,即一个锁一次只能被一个线程使用。

多线程简学

 

JVM把锁分为内部锁和显示锁,内部锁通过synchronized关键字实现,显示锁通过java.concurrent.locks.Lock接口来实现

锁的作用

锁可以实现对共享数据的安全访问,保障线程的原子性、可见性和有序性

锁是通过互斥保障原子性,一个锁只能被一个线程持有,这就保障临界区的代码一次只能被一个线程执行。

锁的可重入性

如果一个线程持有一个锁的时候还能够继续申请该锁,称该锁是可重入的

void methodA(){
//申请A锁
methodB();
//释放A锁
}
void methodB(){
//申请A锁
.....
//释放A锁
}

内部锁:java中每个对象都有一个与之关联的内部锁(Intrinsic Lock) 这种锁也称为监视器。这种内部锁是一种排它锁,可以保障原子性。

内部锁是通过synchronized关键字实现的

修饰代码块:

synchronized(对象锁){
同步代码块,可以在同步代码块中访问共享数据
}

修饰方法

public void synchronized method(){
代码块
}
public class SynchronizedClass {
public static void main(String[] args) {
SynchronizedClass obj = new SynchronizedClass();
new Thread(()->obj.mm()).start();
new Thread(()->obj.mm()).start();
}
public void mm(){
synchronized (this){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
}

锁对象不同不能实现同步

public class SynchronizedClass {
public static void main(String[] args) {
SynchronizedClass obj = new SynchronizedClass();
SynchronizedClass obj1 = new SynchronizedClass();
new Thread(()->obj.mm()).start();
new Thread(()->obj1.mm()).start();
}
public void mm(){
synchronized (this){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
}

使用常量作为锁对象

public class SynchronizedClass {
public static void main(String[] args) {
SynchronizedClass obj = new SynchronizedClass();
SynchronizedClass obj1 = new SynchronizedClass();
new Thread(()->obj.mm()).start();
new Thread(()->obj1.mm()).start();
}
private static final Object obj = new Object();
public void mm(){
synchronized (obj){
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName()+"-->"+i);
}
}
}
}

 

上一篇:JUC概述


下一篇:python模拟进程状态