Java线程概述 (二)线程实例演示

news2024/11/25 23:22:48

文章目录

  • 🐒个人主页
  • 🏅JavaSE系列专栏
    • 📖前言:
    • 🎀创建线程的三种方式
      • 🐕使用java.lang包下的Thread类
      • 🪅实现Runnable接口
      • 🦓实现Callable接口
    • 🐕synchronized简单介绍
      • 🏨synchronized的使用

🐒个人主页

🏅JavaSE系列专栏

📖前言:

本篇博客主要以介绍线程类的相关用法

🎀创建线程的三种方式

🐕使用java.lang包下的Thread类

Thread t=new Thread(count,"客户1");
 t.start();//启动线程

也能【自定义线程】:·通过·继承·Thread类,重写它的run()方法来实现

/**
 * 自定义线程
 */
public class MyThread extends Thread{
    String s="";
    @Override
    public void run() {
        for (int i = 0; i <1000 ; i++) {
            s="my线程 "+i;
            System.out.println(s);
        }

    }
}

在这里插入图片描述

🪅实现Runnable接口

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("实现Runnable接口");
    }

}

在这里插入图片描述

🦓实现Callable接口

背景:由于使用Thred类与Runnable接口的run()方法返回值只能是void,有时我们向返回一些数据,故使用起来不太方便。而实现Callable接口,重写里面的cell()方法效果与run()相同并且可以自定义返回类型

import java.util.concurrent.Callable;

public class CallableTest implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int i=0;
        for (int j = 0; j <5 ; j++) {
            i+=j;
        }
        return i;
    }
}
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class CallableDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableTest test=new CallableTest();//创建Callable接口实现类对象
        FutureTask futureTask=new FutureTask(test);//借助FutureTask类,可以获取Call()返回值("相当于有参的run()")
        Thread t=new Thread(futureTask);
        t.start();
        System.out.println(futureTask.get());
    }
}

🐕synchronized简单介绍

在这里插入图片描述

synchronized中文意思是同步,也称之为”同步锁“。

synchronized的作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以达到保证并发安全的效果。
synchronized是Java中解决并发问题的一种最常用的方法,也是最简单的一种方法。
在JDK1.5之前synchronized是一个重量级锁,相对于j.u.c.Lock,它会显得那么笨重,随着Javs SE
1.6对synchronized进行的各种优化后,synchronized并不会显得那么重了。

synchronized的作用主要有三个:

原子性:确保线程互斥地访问同步代码;
可见性:保证共享变量的修改能够及时可见,其实是通过Java内存模型中的“对一个变量unlock操作之前,必须要同步到主内存中;如果对一个变量进行lock操作,则将会清空工作内存中此变量的值,在执行引擎使用此变量前,需要重新从主内存中load操作或assign操作初始化变量值” 来保证的;
有序性:有效解决重排序问题,即 “一个unlock操作先行发生(happen-before)于后面对同一个锁的lock操作”;

🏨synchronized的使用

synchronized的3种使用方式:

修饰实例方法:作用于当前实例加锁
修饰静态方法:作用于当前类对象加锁
修饰代码块:指定加锁对象,对给定对象加锁
synchronized的代码范例:

修饰方法
Synchronized修饰一个方法很简单,就是在方法的前面加synchronized,synchronized修饰方法和修饰一个代码块类似,只是作用范围不一样,修饰代码块是大括号括起来的范围,而修饰方法范围是整个函数。

方法一:

public synchronized void method()
{
   // todo
}
方法二:

public void method()
{
   synchronized(this) {
      // todo
   }
}

写法一修饰的是一个方法,写法二修饰的是一个代码块,但写法一与写法二是等价的,都是锁定了整个方法时的内容。

synchronized关键字不能·继承。
虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。

这两种方式的例子代码如下:

在子类方法中加上synchronized关键字

class Parent {
   public synchronized void method() { }
}
class Child extends Parent {
   public synchronized void method() { }
}

在子类方法中调用父类的同步方法

class Parent {
   public synchronized void method() {   }
}
class Child extends Parent {
   public void method() { super.method();   }
} 

注意:

在定义接口方法时不能使用synchronized关键字。
构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。
修饰一个代码块
(1) 一个线程访问一个对象中的synchronized(this)同步代码块时,其他试图访问该对象的线程将被阻塞。

注意下面两个程序的区别:

class SyncThread implements Runnable {
       private static int count;
 
       public SyncThread() {
          count = 0;
       }
 
       public  void run() {
          synchronized(this) {
             for (int i = 0; i < 5; i++) {
                try {
                   System.out.println(Thread.currentThread().getName() + ":" + (count++));
                   Thread.sleep(100);
                } catch (InterruptedException e) {
                   e.printStackTrace();
                }
             }
          }
       }
 
       public int getCount() {
          return count;
       }
}
 
public class Demo00 {
    public static void main(String args[]){
    //调用方式一:test01
    //SyncThread s1 = new SyncThread();
    //SyncThread s2 = new SyncThread();
    //Thread t1 = new Thread(s1);
    //Thread t2 = new Thread(s2);
    //调用方式二:test02        
        SyncThread s = new SyncThread();
        Thread t1 = new Thread(s);
        Thread t2 = new Thread(s);
        
        t1.start();
        t2.start();
    }
}

调用方式二种,当两个并发线程(thread1和thread2)访问同一个对象(syncThread)中的synchronized代码块时,在同一时刻只能有一个线程得到执行,另一个线程受阻塞,必须等待当前线程执行完这个代码块以后才能执行该代码块。Thread1和thread2是互斥的,因为在执行synchronized代码块时会锁定当前的对象,只有执行完该代码块才能释放该对象锁,下一个线程才能执行并锁定该对象

调用方式一中,thread1和thread2同时在执行。这是因为synchronized只锁定对象,每个对象只有一个锁(lock)与之相关联。

(2)

class Counter implements Runnable{
   private int count;
 
   public Counter() {
      count = 0;
   }
 
   public void countAdd() {
      synchronized(this) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   //非synchronized代码块,未对count进行读写操作,所以可以不用synchronized
   public void printCount() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + " count:" + count);
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public void run() {
      String threadName = Thread.currentThread().getName();
      if (threadName.equals("A")) {
         countAdd();
      } else if (threadName.equals("B")) {
         printCount();
      }
   }
}
 
public class Demo00{
    public static void main(String args[]){
        Counter counter = new Counter();
        Thread thread1 = new Thread(counter, "A");
        Thread thread2 = new Thread(counter, "B");
        thread1.start();
        thread2.start();
    }
}

可以看见B线程的调用是非synchronized,并不影响A线程对synchronized部分的调用。从上面的结果中可以看出一个线程访问一个对象的synchronized代码块时,别的线程可以访问该对象的非synchronized代码块而不受阻塞。

(3) 指定要给某个对象加锁

/**
 * 银行账户类
 */
class Account {
   String name;
   float amount;
 
   public Account(String name, float amount) {
      this.name = name;
      this.amount = amount;
   }
   //存钱
   public  void deposit(float amt) {
      amount += amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
   //取钱
   public  void withdraw(float amt) {
      amount -= amt;
      try {
         Thread.sleep(100);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
   }
 
   public float getBalance() {
      return amount;
   }
}
 
/**
 * 账户操作类
 */
class AccountOperator implements Runnable{
   private Account account;
   public AccountOperator(Account account) {
      this.account = account;
   }
 
   public void run() {
      synchronized (account) {
         account.deposit(500);
         account.withdraw(500);
         System.out.println(Thread.currentThread().getName() + ":" + account.getBalance());
      }
   }
}
 
public class Demo00{
    
    //public static final Object signal = new Object(); // 线程间通信变量
    //将account改为Demo00.signal也能实现线程同步
    public static void main(String args[]){
        Account account = new Account("zhang san", 10000.0f);
        AccountOperator accountOperator = new AccountOperator(account);
 
        final int THREAD_NUM = 5;
        Thread threads[] = new Thread[THREAD_NUM];
        for (int i = 0; i < THREAD_NUM; i ++) {
           threads[i] = new Thread(accountOperator, "Thread" + i);
           threads[i].start();
        }
    }
}

在AccountOperator 类中的run方法里,我们用synchronized 给account对象加了锁。这时,当一个线程访问account对象时,其他试图访问account对象的线程将会阻塞,直到该线程访问account对象结束。也就是说谁拿到那个锁谁就可以运行它所控制的那段代码。
当有一个明确的对象作为锁时,就可以用类似下面这样的方式写程序:

public void method3(SomeObject obj)
{
   //obj 锁定的对象
   synchronized(obj)
   {
      // todo
   }
}

当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的对象来充当锁:

class Test implements Runnable
{
   private byte[] lock = new byte[0];  // 特殊的instance变量
   public void method()
   {
      synchronized(lock) {
         // todo 同步代码块
      }
   }
 
   public void run() {
 
   }
}

修改一个静态方法
synchronized也可修饰一个静态方法,用法如下:

public synchronized static void method() {
// todo
}
静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。

/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public synchronized static void method() {
      for (int i = 0; i < 5; i ++) {
         try {
            System.out.println(Thread.currentThread().getName() + ":" + (count++));
            Thread.sleep(100);
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}
 
public class Demo00{
    
    public static void main(String args[]){
        SyncThread syncThread1 = new SyncThread();
        SyncThread syncThread2 = new SyncThread();
        Thread thread1 = new Thread(syncThread1, "SyncThread1");
        Thread thread2 = new Thread(syncThread2, "SyncThread2");
        thread1.start();
        thread2.start();
    }
}

syncThread1和syncThread2是SyncThread的两个对象,但在thread1和thread2并发执行时却保持了线程同步。这是因为run中调用了静态方法method,而静态方法是属于类的,所以syncThread1和syncThread2相当于用了同一把锁。

修饰一个类
Synchronized还可作用于一个类,用法如下:

class ClassName {
   public void method() {
      synchronized(ClassName.class) {
         // todo
      }
   }
}
/**
 * 同步线程
 */
class SyncThread implements Runnable {
   private static int count;
 
   public SyncThread() {
      count = 0;
   }
 
   public static void method() {
      synchronized(SyncThread.class) {
         for (int i = 0; i < 5; i ++) {
            try {
               System.out.println(Thread.currentThread().getName() + ":" + (count++));
               Thread.sleep(100);
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
      }
   }
 
   public synchronized void run() {
      method();
   }
}

本例的的给class加锁和上例的给静态方法加锁是一样的,所有对象公用一把锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/977249.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java手写数组队列和链表队列并使用java里面现成的队列方法

Java队列实现与常用使用方法介绍 说明使用数组手动实现队列功能使用单向链表手动实现队列功能Java中ArrayBlockingQueue和ArrayQueue和LinkedBlockingQueue使用ArrayQueue使用方法如下:ArrayBlockingQueue常用使用方法如下:LinkedBlockingQueue常用使用方法如下:ArrayBlockingQ…

Spring-mvc的参数传递与常用注解的解答及页面的跳转方式---综合案例

目录 一.slf4j--日志 二.常用注解 2.1.RequestMapping 2.2.RequestParam 2.3.RequestBody 2.4.PathVariable 三.参数的传递 3.1 基础类型 3.2 复杂类型 3.3 RequestParam 3.4 PathVariable 3.5 RequestBody 3.6 增删改查 四.返回值 4.1 void 返回值 4.2 String 返…

用go实现dns请求

一、DNS报文格式详解 1.1 报文格式 DNS分为查询请求和查询响应&#xff0c;两者的报文结构基本相同。DNS报文格式如下表展示 0 1516 31事务ID&#…

CVE-2023-28303(截图修复)

在做羊城杯的misc的时候发现了一个图片&#xff0c;典型的图片高度不对&#xff0c;修改之后对图片的高度进行不断修改尝试&#xff0c;发现图片很大&#xff0c;但是内容没有出现&#xff0c; 从这里指知道存在feld文件x 截出zip文件内容&#xff0c;把04 03 改为03 04 这里的…

SOLIDWORKS放样是什么意思?

SOLIDWORKS是一款广受欢迎的三维计算机辅助设计&#xff08;CAD&#xff09;软件&#xff0c;提供了许多强大的功能来帮助工程师实现他们的创意。其中一个重要的功能是放样功能&#xff0c;它在设计过程中起着至关重要的作用。本文将介绍SOLIDWORKS放样的概念、特点和应用。 放…

怎么将火狐浏览器中的书签或密码迁移到谷歌浏览器

文章目录 一、点击谷歌浏览器右上角二、选择书签–导入书签和设置…三、点击导入 一、点击谷歌浏览器右上角 二、选择书签–导入书签和设置… 在下拉框中选中Firefox&#xff0c;点击导入 三、点击导入

sublime text 格式化json快捷键配置

以 controlcommandj 为例。 打开Sublime Text&#xff0c;依次点击左上角菜单Sublime Text->Preferences->Key Bindings&#xff0c;出现以下文件&#xff1a; 左边的是Sublime Text默认的快捷键&#xff0c;不可编辑。右边是我们自定义快捷键的地方&#xff0c;在中括号…

手机便签功能在哪里?如何在便签里添加文字图片视频?

手机已成为我们生活中不可或缺的工具&#xff0c;而在使用手机的过程中&#xff0c;我们经常需要随手记录一些重要的事情。那么&#xff0c;如何高效便捷地记录这些事情呢&#xff1f;答案就是使用手机便签软件。但是&#xff0c;有很多人不知道手机便签功能在哪里&#xff1f;…

使用scp在两个linux系统之间传输文件

使用scp在两个linux系统之间传输文件 问题背景拷贝文件首先我们要从源设备&#xff08;本文中是矩池云自己的服务器&#xff09;传输文件至目标设备&#xff08;本文中是A100设备&#xff09;传输一个文件传输一个文件夹 从目标设备&#xff08;本文中是A100设备&#xff09;下…

Python基础之高级函数

异常捕获 Python中&#xff0c;使用trycatch两个关键字来实现对异常的处理。在我们平时的工作中&#xff0c;异常的出现是在所难免的&#xff0c;但是异常一旦出现&#xff0c;极有可能会直接导致程序崩溃&#xff0c;无法正常运行&#xff0c;所以异常一定要及时的做出对应的…

QT(9.5)QT连接OpenCV库完成人脸识别,c语言中的static和c++中的static的用法,在c和c++中const关键字的用法,Qt中基于TCP通信中的服务器和客户端的操作

1.QT完成人脸识别 pro文件: 头文件: #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <opencv2/opencv.hpp> #include <iostream> #include <math.h> #include<opencv2/face.hpp> #include <vector> #include <map&g…

【STL】模拟实现map和set {map和set的封装;核心结构;插入和查找;红黑树的迭代器;STL中的红黑树结构}

模拟实现map和set map和set是红黑树的两种不同封装形式&#xff0c;底层使用同一颗泛型结构的红黑树&#xff0c;只是存储类型不同。set是红黑树的K模型&#xff0c;存储key&#xff1b;map是红黑树的KV模型&#xff0c;存储pair<key,value>。 下面的代码和讲解着重体现…

Vue进阶(三十三)Content-Security-Policy(CSP)详解

文章目录 一、前言二、XSS 攻击都有哪些类型&#xff1f;三、CSP介绍3.1 使用HTTP的 Content-Security-Policy头部3.2 使用 meta 标签 四、CSP 实施策略五、Vue中可使用的防XSS攻击方式六、拓展阅读 一、前言 作为前端工程师你真的了解 XSS 吗&#xff1f;越来越多超级应用基于…

预付费电表和断路器的连接方式及注意事项

随着智能电网技术的不断发展&#xff0c;预付费电表已经在我国得到了广泛应用。预付费电表不仅可以实现远程自动抄表、实时监控用电量等功能&#xff0c;还可以有效防止偷电行为&#xff0c;提高用电安全。断路器作为低压配电系统中的重要组成部分&#xff0c;具有保护电路、防…

详细介绍 弹性盒子(display:flex)

文章目录 什么是弹性盒子 如何使用弹性盒子flex系列flex-direction 对齐方向 水平对齐垂直对齐flex-wrap 换行flex-flowflex模型说明容器的属性 justify-content X轴对齐方式align-content Y轴对齐方式总结属性值Y轴对齐的另外一种&#xff1a;align-itemsalign-content和alig…

基于Springboot跟rabbitmq实现的死信队列

概述 RabbitMQ是流行的开源消息队列系统&#xff0c;使用erlang语言开发。为了保证订单业务的消息数据不丢失&#xff0c;需要使用到RabbitMQ的死信队列机制&#xff0c;当消息消费发生异常时&#xff0c;将消息投入死信队列中。但由于对死信队列的概念及配置不熟悉&#xff0…

自然语言处理实战项目17-基于多种NLP模型的诈骗电话识别方法研究与应用实战

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目17-基于NLP模型的诈骗电话识别方法研究与应用&#xff0c;相信最近小伙伴都都看过《孤注一掷》这部写实的诈骗电影吧&#xff0c;电影主要围绕跨境网络诈骗展开&#xff0c;电影取材自上万起真…

PPO代码研究(2)

好&#xff0c; 因为我没怎么看懂&#xff0c; 所以我决定再看一遍PPO的代码&#xff0c; 再研究一遍。 事实证明&#xff0c; 重复是一个非常好&#xff0c;非常好的方法。 学习方法。 世界上几乎没有任何新知识是你一遍就能学会的。 你只能学一遍&#xff0c;再来一遍&…

大剧院订座系统源码,大剧院订票,大剧院场馆租赁,大剧院订票系统完整源码

大剧院订座系统源码,大剧院订票&#xff0c;大剧院场馆租赁&#xff0c;大剧院订票系统完整源码 大剧院系统1、管理后台--系统说明2、订票小程序--系统说明3、验票端--系统说明4、系统源码说明 大剧院系统 1、管理后台–系统说明 项目管理&#xff1a;用于创建剧院演出项目 2…

【广州华锐互动】AR技术在配电系统运维中的应用

随着科技的不断发展&#xff0c;AR(增强现实)技术逐渐走进了我们的生活。在电力行业&#xff0c;AR技术的应用也为巡检工作带来了许多新突破&#xff0c;提高了巡检效率和安全性。本文将从以下几个方面探讨AR配电系统运维系统的新突破。 首先&#xff0c;AR技术可以实现虚拟巡检…