多线程理论--笔记

volatile关键字的作用是:使变量在多个线程间可见(可见性)

将变量的值维护在内存中,可以确保每个线程的操作访问到的值都是来自内存,可以保证多个线程读取到的是同一份数据

当两个线程读取一个共享变量是,他会默认加载到本线程的私有内存中,从私有内存中读取变量的值的时候!volatile关键字就强制当前线程更新该变量值为当前内存中维护的这个值!--这是我的理解如果错误请告知!!

详细解释地址

volatile线程安全问题

 解决线程安全问题,就是synchronize关键字,在多个线程对同一个变量进行访问时,做一下控制,只让一个线程来操作,这样就不会出现线程安全问题

所以说:volatile 可以保证可见性,但不能保证原子性(一个操作不可分割,比如事务的特性)。某种意义上是线程不安全的

线程的具体

定义子类继承Thread:重写run()方法;在main方法中创建定义子类,调用start()方法

Thread类是线程类继承他就会是一个线程对象;

run()相当于一个规定一个模板把代码放入方法内;

不能调用run()方法;调用run方法相当于是个单线程程序:

start方法用来开启线程,虚拟机会自动寻找run方法;

run()本身就是个普通方法等着虚拟机去执行他,调用run方法仅仅只是去执行他!

创建线程程序,就是让程序单独运行;开启一个新的栈内存空间去执行run()方法

代码如下:

/**
 * 定义子类,继承Thread
 * 重写run方法
 */
class SubThread extends  Thread{

     public   void run() {
          for (int i=0;i<700;i++){
               System.out.println("run方法"+i);
          }
     }
}
/**
 * 创建和启动一个线程
 *    创建Thread子类对象
 *     子类对象调用方法start()
 */
class ThreadDemo{
     public static void main(String[] args) {
          SubThread subThread = new SubThread();
          subThread.start();
          for (int i = 0; i < 1000; i++) {
               System.out.println("main方法"+i);
          }
     }
}

获取线程名,设置县成名

/**
 * 定义子类,继承Thread
 * 重写run方法
 * 获取线程名字,父类方法有个  getName()
 */
class SubThread extends  Thread{

     public   void run() {
          System.out.println(getName());

     }
}
/**
 * 创建和启动一个线程
 *    创建Thread子类对象
 *     子类对象调用方法start()
 */
class ThreadDemo{
     public static void main(String[] args) {

          SubThread subThread = new SubThread();

          //用子类调用设置线程方法
         // subThread.setName("大")
          subThread.start();
         
     }
}
 

运行结果:

Thread-0  设置之后的话是  大


Process finished with exit code 0

线程中的静态sleep(Long  毫秒值)方法,如果生命在run方法内只能try{}Cathy{}

class ThreadSleepDemo{
     public static void main(String[] args) throws InterruptedException {
          for (int i = 0; i < 100; i++) {
               //需要声明异常
               Thread.sleep(200);
               System.out.println(i);
          }
     }
}

实现线程另一种方式:

接口的实现;实现接口Runnerable重写run()方法

分离了任务与线程对象,降低程序耦合性,实现了资源的共享

匿名内部类实现方式代码如下:

/**
 * 实现接口方式的线程
 * 匿名内部类的实现方法
 */
class SubRunnerAbleDemo{
     public static void main(String[] args) {
          new Thread(){
               @Override
               public void run() {
                    for (int i = 0; i < 1000; i++) {
                         System.out.println("Thread---" + i);
                    }
               }
          }.start();
          new Thread(new Runnable() {
               @Override
               public void run() {
                    for (int i = 0; i < 1000; i++) {
                         System.out.println("Runnable---" + i);
                    }
               }
          }).start();
          for (int i = 0; i < 1000; i++) {
               System.out.println("main"+i);
          }
     }
}

线程状态: 

新建状态 new  Thread()  -->  运行状态  start()方法 --> run方法完成 死亡状态(stop()方法也可以使线程结束已经过时不推荐;可以设置一个boolean的变量来控制线程的结束)

               受阻塞状态(cpu繁忙;同步锁;程序无法控制)具有CPU的执行资格等待CPU的资源;

               等待休眠状态(运行到方法才会休眠sleep方法;有可能转到阻塞有可能到运行状态去) 

               无限等待状态(等待方法wait()方法在Object类中唤醒方法notify()方法)

线程池:

   线程多的话创建线程销毁线程浪费资源,所以要创建一个线程池来存放线程减小资源开销!

   程序一开始创建多个线程对象,当需要线程的时候就从线程中取出来;跑完线程之后回到容器中在放到容器中

   jdk5内置线程池技术直接使用;

使用线程池的方式:通过线程池工厂生产线程对象

concurrent包中,有executors : 线程池创建工厂类

   

/**
 * jdk1.5新特性,实现线程池程序
 * 使用工厂类中的 Executors中的静态方法创建线程对象
 *   static ExecutorService  newFixedThreadPoll
 *   返回一个线程池对象ExecutorService接口的实现类
 *   接口实现类的对象,调用submit(Runnerable r) 来提交线程任务
 *
 */
class ThreadPool{
     public static void main(String[] args) {
          //调用工厂类静态方法,创建线程池对象

          ExecutorService pool = Executors.newFixedThreadPool(2);
          pool.submit(new Thread(new Runnable() {
               @Override
               public void run() {
                    System.out.println("线程提交任务");
                    System.out.println(Thread.currentThread().getName());
               }
          })); 
     }

}

Callable接口线程的第三种实现方式

/**
 * 实现线程程序的第三种方式,实现Callable接口方式的静态方法
 * 使用工厂类中的 Executors中的静态方法创建线程对象
 *   static ExecutorService  newFixedThreadPoll
 *   返回一个线程池对象ExecutorService接口的实现类
 *   接口实现类的对象,调用submit(Callable c) 来提交线程任务
 *
 */
class ThreadPoolDeme1{
     public static void main(String[] args) throws ExecutionException, InterruptedException {
          ExecutorService ex = Executors.newFixedThreadPool(3);
          Future<String> stringFuture = ex.submit(new Callable<String>() {
               @Override
               public String call() throws Exception {

                    return "call方法执行";
               }
          });
          String s = stringFuture.get();
          System.out.println(s);
     }
}

控制带输出

"C:\Program Files\Java\jdk1.8.0_162\bin\java.exe" "-javaagent:C:\Program 
call方法执行

案例:使用多线程技术,求和

两个线程,1个线程计算1+100之间的和;另一个线程实现1+200的和;多线程的异步计算

class ThreadCallabledemo implements  Callable<Integer>{
     private Integer a;
     public ThreadCallabledemo( Integer a){
          this.a=a;
     }
     @Override
     public Integer call() throws Exception {
          //定义求和变量;
          int sum=0;
          for (int i = 1; i <= a; i++) {
                sum+=i;
          }
          return sum;
     }
}
class TestCallable{
     public static void main(String[] args) throws ExecutionException, InterruptedException {
          //调用自定义求和方法
          getSumBynum(100);
          getSumBynum(200);
     }
     //求和方法
     public static void getSumBynum(Integer a) throws ExecutionException, InterruptedException {
          //创建线程池静态工厂
          ExecutorService es = Executors.newFixedThreadPool(2);
          Future<Integer> submit = es.submit(new ThreadCallabledemo(a));
          System.out.println(submit.get());
     }

}

线程安全问题:多个线程同时运性同一个共享数据

对线程同时并发访问同一个共享组员

使用同步技术解决线程安全问题!

1.同步代码块

 公式:synchronize(任意对象){

      线程共享数据

]}

在方法加上synchronize让这个方法变成同步方法;同步方法对象锁对应本类对象引用this

静态方法中同步有锁,是本类自己;

2.concurrent.locaks包中Lock接口

Lock接口可以替代synchronize

首先创建接口的实现类对象;

Lock lock = new ReentrantLock();

方法:lock() 获取锁  unLock() 释放锁

多线程死锁:多个同步锁嵌套,锁的顺序放的位置不正确;

由于两个线程间相互等待各自的锁,并且不释放,就会导致程序一直等待下去,发生死锁;

所谓死锁是指多个线程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。

在多线程编程中,为了避免发生这样的情况,可以有两种方法进行预防,这样可以避免死锁的发生;

  • 加锁顺序一致       如果一个线程(比如线程3)需要一些锁,那么它必须按照确定的顺序获取锁。它只有获得了从顺序上排在前面的锁之后,才能获取后面的锁。
  • 避免锁未释放的场景   一个可行的做法是释放所有锁,回退,并且等待一段随机的时间后重试。这个和简单的加锁超时类似,不一样的是只有死锁已经发生了才回退,而不会是因为加锁的请求超时了。虽然有回退和等待,但是如果有大量的线程竞争同一批锁,它们还是会重复地死锁

多线程下一边写入一边读取(等待与唤醒)

写操作先获得执行权,(添加一个变量 Boolean flag 如果为真说明赋值已经完成,为假获取值完成,所以需不需要赋值看标记)

标记具体:  flag=true 说明写入赋值完成  flag=false 读取值完成

输入:需要赋值看标记标记为true赋值成功那么就等待 wait()方法,false 读取完成,不需要等待唤醒写入线程notify()让读取线程等待wait()方法

写入完成后必须等待,等到读出结束后才能下一次写入

写入完成赋值之后,执行wait()方法永远等待

读出操作:读出变量值后,必须等待,等待写入成功充重新变量中取出进行下一次读出

变量读出后在读出wait()方法等待之前唤醒写入的线程notify()方法

写入被唤醒后对变量重新赋值,赋值后先唤醒读出的线程,然后在使用wait()方法进入无限等待

猜你喜欢

转载自blog.csdn.net/adminBfl/article/details/82699123