基本概念
程序
程序是在一个时间上按严格前后相继的操作序列,这些操作是机器指令或者高级语言编写的语句。
进程
进程是具有一定独立功能的程序在某个数据集合上的一次运行活动,是系统进行资源分配和调度的一个独立单位。
线程
线程是进程的一个实体,是处理器调度和分派的基本单位,线程自己基本上不拥有系统资源,只拥有少量在运行中必不可少的资源(如程序计数器、一组寄存器和栈等),但它可与同属一个进程的其他线程共享进程所有的全部资源。
单核CPU
多核CPU
同时多核执行进程
并行
并发
一个CPU(采用时间片)同时执行多个任务。
使用多线程的有点
- 提高应用程序的响应
- 提高CPU的利用率
- 改善程序结构
需要使用多线程
- 需要执行多个任务
- 需要执行等待的任务
- 需要后台运行的程序时
线程的创建和使用
继承Thread
匿名内部类
实现Runnable
package com.wujing.thread.ThreadCreat;
import org.junit.Test;
/**
* @ClassName: ThreadCreat
* @Description: 创建线程的方式
*
* 比较:
* 使用那种方式创建线程
* 使用实现方式
* 继承是单继承
* 实现可以实现数据共享,仅是单线程。
* @Author liujiexin
* @Date 2021/6/6 1:34 下午
*/
public class ThreadCreat {
public static void main(String[] args) {
for (int a=0; a< 100; a++){
if(a % 2 == 0){
System.out.println(" main线程偶数:"+a);
}
}
ThreadCreatByExtendsThread threadCreatByExtendsThread = new ThreadCreatByExtendsThread();
threadCreatByExtendsThread.start();
/**
* 匿名内部类创建线程
*
*/
new Thread(){
@Override
public void run() {
for (int a=0; a< 100; a++){
if(a % 2 != 0){
System.out.println(" 匿名内部类子线程奇数:"+a);
}
}
}
}.start();
}
@Test
public void test(){
ThreadCreatByRunnable threadCreatByRunnable = new ThreadCreatByRunnable();
Thread thread = new Thread(threadCreatByRunnable);
thread.start();
}
}
/**
* @ClassName: ThreadCreat
* @Description: 创建线程的方式 一: 继承 thread
* 使用步骤:
* 1、继承thread
* 2、重写 run
* 3、创建对象
* 4、调用start
* @Author liujiexin
* @Date 2021/6/6 1:34 下午
*/
class ThreadCreatByExtendsThread extends Thread{
@Override
public void run(){
for (int a=0; a< 100; a++){
if(a % 2 == 0){
System.out.println(" 子线程偶数:"+a);
}
}
}
}
/**
* @ClassName: ThreadCreat
* @Description: 创建线程的方式 二: 实现Runnable 接口
* 使用步骤:
* 1、实现Runnable 接口
* 2、实现run 抽象方法
* 3、创建对象
* 4、将对象放入Thread的类
* 5、调用start
* @Author liujiexin
* @Date 2021/6/6 1:34 下午
*/
class ThreadCreatByRunnable implements Runnable{
@Override
public void run(){
for (int a=0; a< 100; a++){
if(a % 2 == 0){
System.out.println(" ThreadCreatByRunnable : 子线程偶数:"+a);
}
}
}
}
实现Callable 创建线程 JDK1.5后
Thead的常用方法
start()
run()
通常需要重写Thread类中的方法,将创建的线程需要执行的操纵声明在此方法中
currentThread()
getName()
setName()
设置当前线程的名字
线程的生命周期
创建(NEW)
当一个Thread 类或其他子类的对象被声明并创建时,新生的线程对象处于新建状态。
就绪(RUNNABLE)
处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备运行的条件,只是没有分配到CPU资源。
运行(RUNNING)
当就绪的线程被调度并获得CPU资源时,便进入运行状态,run()方法定义了线程的操作和功能。
阻塞
在某种特殊情况下,被人为挂起或执行输入输出操作时,让出CPU并临时终止自己的执行,进入阻塞状态
死亡
线程完成了他的全部工作或线程被提前强制性地终止或出现异常导致结束。
生命周期流转图
线程安全
四个维度考量线程安全
1、数据单线程内可见
单线程总是安全的。通过限制数据仅在单线程内可见, 可以避免数据被其他结程篡改。最典型的就是线程局部变量,它存储在独立虚拟机枝 帧的局部变量表中,与其他线程毫无瓜葛。 ThreadLocal 就是采用这种方式来实现线 程安全的。
2、只读对象
只读对象总是安全的。它的特性是允许复制、拒绝写人。最典 型的只读对象有 String、 Integer等。一个对象想要拒绝任何写人,必须要满足以下条件, 使用 final 关键字修饰类,避免被继承,使用 private final 关键字避免属性被中途修改; 没有任何更新方法;返回值不能可变对象为引用。
3、线程安全类
某些线程安全类的内部有非常明确的结程安全机制。比如 StringBuffer 就是一个线程安全类,它采用 synchronized 关键字来修饰相关方法。
4、同步和锁机制
如果想要对某个对象进行并发更新操作,但又不属于上述 三类,需要开发工程师在代码中实现安全的同步机制。虽然这个机制支持的并发场景 很有价值,但非常复杂且容易出现问题。线程安全的核心理念就是“要么只读,要么加锁”。合理利用好JDK提供的并发包, 往往能化腐朽为神奇。 Java 并发包( java.util.concurrent,只JC ) 中大多数类注释都写有 @author Doug Lea。如果说 Java 是一本史书,那么 Doug Lea 绝对是开疆拓土的伟大 人物。 Doug Lea 在当大学老师时,专攻并发编程和并发数据结构设计,主导设计了 JUC 并发包,提高了 Java 并发编程的易用性,大大推进了 Java 的商用进程。并发包 主要分成以下几个类族·
并发包 主要分成以下几个类族
( 1 )线程同步类。
这些类使线程间的协调更加容易,支持了更加丰富的线程 协调场景,逐步淘汰了使用 Object 的 wait()和 notify()进行同步的方式。主要代表为 CountDownLatch、 Semaphore、 CyclicBarrier 等。
( 2 )并发集合类。
集合并发操作的要求是执行速度快,提取数据准。最著名的 类非 ConcurrentHashMap 莫属,它不断地优化,由刚开始的锁分段到后来的 CAS, 不断地提升并发性能。其他还有 ConcurrentSkipListMap、 CopyOnWriteArrayList、 BlockingQueue 等。
( 3 )线程管理类。
虽然 Thread 和 ThreadLocal 在 JDKl.O 就已经引入,但是真 正把 Thread 发扬光大的是线程池。根据实际场景的需要,提供了多种创建线程池的快捷方式,如使用 Executors 静态工厂或者使用 ThreadPoolExecutor 等。另外,通过 ScheduledExecutorService 来执行定时任务。
( 4 )锁相关类。
锁以 Lock 接口为核心,派生出在一些实际场景中进行互斥操 作的锁相关类。最有名的是 ReentrantLock。锁的很多概念在弱化,是因为锁的实现 在各种场景中已经通过类库封装进去了。并发包中的类族有很多,差异比较微妙,开发工程师需要有很好的 Java 基础、 逻辑思维能力,还需要有定的数据结构基础,才能够彻底分清各个类族的优点、缺 点及差异点。
线程同步
package com.wujing.thread.ThreadSecurity;
import org.junit.Test;
import java.lang.reflect.Method;
/**
* @ClassName: ThreadSecurityTicket
*
* @Description: 线程安全售票
*
* 此处出现线程安全问题 ,如 错票 、重票、余票错误
* 问题原因: 同时有两个线程访问共享数据
* 解决办法: 加锁 ,同一时间只有一个线程访问共享数据
* 在Java 中通过同步机制 ,解决线程安全问题
* 方式一: 同步代码块
* synchronized(同步监视器){package com.wujing.thread.ThreadSecurity;
import org.junit.Test;
import java.lang.reflect.Method;
/**
* @ClassName: ThreadSecurityTicket
*
* @Description: 线程安全售票
*
* 此处出现线程安全问题 ,如 错票 、重票、余票错误
* 问题原因: 同时有两个线程访问共享数据
* 解决办法: 加锁 ,同一时间只有一个线程访问共享数据
* 在Java 中通过同步机制 ,解决线程安全问题
* 方式一: 同步代码块
* synchronized(同步监视器){
* // 需要同步的代码
* }
* 说明: 1、操作共享数据的代码, 即为需要被同步的代码
* 2、共享数据:多个线程共同操作的变量。
* 3、同步监视器:俗称:锁
* 哪些可以作为锁:任何一个类的对象,都可以作为锁。
* 有个关键选择,就是多个线程共用一个锁(就是多个线程对象使用同一个 对象)
* 有个通用的锁:可以使用当前对象 也就是this
* 还可以是(类.class)
* 方式二: 同步方法
* public void synchronized 方法名(){
* //
* }
* 说明
* 1、同步方法仍然涉及同步监视器,只是不需要显示声明
* 2、非静态的同步方法,同步监视器:this
* 静态的同步方法,同步监视器:当前类本身
* 同步操作的好处
* 同步的方式,解决了线程安全问题
* 同步操作的缺点
* 同一时间只有一个线程在执行,效率低
* @Author liujiexin
* @Date 2021/6/12 10:56 下午
*/
public class ThreadSecurityTicket {
public static void main(String[] args) {
ThreadWindowsOfSynchMethod thread1 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread2 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread3 = new ThreadWindowsOfSynchMethod();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 实现 Runnable 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test1(){
RunnableWindowsOfSynchCodeBlock windows = new RunnableWindowsOfSynchCodeBlock();
System.out.println("哈哈哈哈哈哈哈");
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 实现 Runnable 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test1_1(){
RunnableWindowsOfSynchMethod windows = new RunnableWindowsOfSynchMethod();
System.out.println("哈哈哈哈哈哈哈");
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 继承 Thread 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test2(){
ThreadWindowsOfSynchCodeBlock thread1 = new ThreadWindowsOfSynchCodeBlock();
ThreadWindowsOfSynchCodeBlock thread2 = new ThreadWindowsOfSynchCodeBlock();
ThreadWindowsOfSynchCodeBlock thread3 = new ThreadWindowsOfSynchCodeBlock();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 继承 Thread 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test2_1(){
ThreadWindowsOfSynchMethod thread1 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread2 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread3 = new ThreadWindowsOfSynchMethod();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
/**
* 使用同步代码块
*/
class RunnableWindowsOfSynchCodeBlock implements Runnable{
/**
* 默认100张票
*/
private int ticket = 20;
Object obj = new Object();
@Override
public void run() {
while (true){
// 此种写法起不到锁的作用
// synchronized(new Object()){
// 使用 this对象作为 锁 ,此处的this 对象是 Windows windows = new Windows(); Thread thread1 = new Thread(windows); 中的 window 实例
// 即 synchronized(this) 同样可以达到 锁的作用
synchronized(obj){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}else {
break;
}
}
}
}
}
/**
* 使用同步方法处理
*
*/
class RunnableWindowsOfSynchMethod implements Runnable{
public RunnableWindowsOfSynchMethod() {
super();
System.out.println(" RunnableWindowsOfSynchMethod 使用同步方法处理");
}
/**
* 默认100张票
*/
private int ticket = 20;
Object obj = new Object();
@Override
public void run() {
while (true){
sale();
}
}
/**
* @Description: 使用同步方法实现同步处理
*
* 此时的同步监视器 是 this 当前调用对象
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 3:58 下午
*/
public synchronized void sale(){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}
}
}
class ThreadWindowsOfSynchCodeBlock extends Thread{
/**
* 默认100张票
*
* 此处 如果未被 static 修饰 则 是每个窗口是票 20 张 ,逻辑错误,但线程安全
*/
private static int ticket = 20;
static Object obj = new Object();
@Override
public void run() {
while (true){
// synchronized(new Object()) 此种写法起不到锁的作用
// synchronized(obj) Object obj = new Object(); 前提 obj 没被static 修饰 使用此方式在 继承方式中同样 达不到线程安全 ,因为 在创建线程时,对应的对象创建了多个,同时 obj 也是多个,则不能作为锁
// synchronized(this) 此写法也不行,因为在继承中,此时的this对象 就是创建线程的对象,而继承是创建多个对象,则不能作为锁
// 解决方法 使用static 修饰 对象 static Object obj = new Object();
// 还可以使用 synchronized(Windows2.class)
synchronized(obj){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}else {
break;
}
}
}
}
}
/**
* 使用同步方法实现同步处理
*/
class ThreadWindowsOfSynchMethod extends Thread{
/**
* 默认100张票
*
* 此处 如果未被 static 修饰 则 是每个窗口是票 20 张 ,逻辑错误,但线程安全
*/
private static int ticket = 20;
public ThreadWindowsOfSynchMethod() {
super();
System.out.println("ThreadWindowsOfSynchMethod 同步方法");
}
static Object obj = new Object();
@Override
public void run() {
while (true){
sale();
}
}
/**
* @Description:
* 未使用 static 修饰 同步修饰器为 this 当使用Thread 的方式创建线程,需要创建多个对象,则此时的this 不是同一个,不能作为同步监视器
* public synchronized void sale()
* 使用static 修饰后 同步监视器是当前 类 也就是 类.class
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 4:20 下午
*/
public static synchronized void sale(){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}
}
}
* // 需要同步的代码
* }
* 说明: 1、操作共享数据的代码, 即为需要被同步的代码
* 2、共享数据:多个线程共同操作的变量。
* 3、同步监视器:俗称:锁
* 哪些可以作为锁:任何一个类的对象,都可以作为锁。
* 有个关键选择,就是多个线程共用一个锁(就是多个线程对象使用同一个 对象)
* 有个通用的锁:可以使用当前对象 也就是this
* 还可以是(类.class)
* 方式二: 同步方法
* public void synchronized 方法名(){
* //
* }
* 说明
*
* 同步操作的好处
* 同步的方式,解决了线程安全问题
* 同步操作的缺点
* 同一时间只有一个线程在执行,效率低
* @Author liujiexin
* @Date 2021/6/12 10:56 下午
*/
public class ThreadSecurityTicket {
public static void main(String[] args) {
ThreadWindowsOfSynchMethod thread1 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread2 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread3 = new ThreadWindowsOfSynchMethod();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 实现 Runnable 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test1(){
RunnableWindowsOfSynchCodeBlock windows = new RunnableWindowsOfSynchCodeBlock();
System.out.println("哈哈哈哈哈哈哈");
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 实现 Runnable 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test1_1(){
RunnableWindowsOfSynchMethod windows = new RunnableWindowsOfSynchMethod();
System.out.println("哈哈哈哈哈哈哈");
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 继承 Thread 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test2(){
ThreadWindowsOfSynchCodeBlock thread1 = new ThreadWindowsOfSynchCodeBlock();
ThreadWindowsOfSynchCodeBlock thread2 = new ThreadWindowsOfSynchCodeBlock();
ThreadWindowsOfSynchCodeBlock thread3 = new ThreadWindowsOfSynchCodeBlock();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
/**
* @Description: 使用 继承 Thread 的方式 安全处理
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 12:10 上午
*/
@Test
public void test2_1(){
ThreadWindowsOfSynchMethod thread1 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread2 = new ThreadWindowsOfSynchMethod();
ThreadWindowsOfSynchMethod thread3 = new ThreadWindowsOfSynchMethod();
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
/**
* 使用同步代码块
*/
class RunnableWindowsOfSynchCodeBlock implements Runnable{
/**
* 默认100张票
*/
private int ticket = 20;
Object obj = new Object();
@Override
public void run() {
while (true){
// 此种写法起不到锁的作用
// synchronized(new Object()){
// 使用 this对象作为 锁 ,此处的this 对象是 Windows windows = new Windows(); Thread thread1 = new Thread(windows); 中的 window 实例
// 即 synchronized(this) 同样可以达到 锁的作用
synchronized(obj){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}else {
break;
}
}
}
}
}
/**
* 使用同步方法处理
*
*/
class RunnableWindowsOfSynchMethod implements Runnable{
public RunnableWindowsOfSynchMethod() {
super();
System.out.println(" RunnableWindowsOfSynchMethod 使用同步方法处理");
}
/**
* 默认100张票
*/
private int ticket = 20;
Object obj = new Object();
@Override
public void run() {
while (true){
sale();
}
}
/**
* @Description: 使用同步方法实现同步处理
*
* 此时的同步监视器 是 this 当前调用对象
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 3:58 下午
*/
public synchronized void sale(){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}
}
}
class ThreadWindowsOfSynchCodeBlock extends Thread{
/**
* 默认100张票
*
* 此处 如果未被 static 修饰 则 是每个窗口是票 20 张 ,逻辑错误,但线程安全
*/
private static int ticket = 20;
static Object obj = new Object();
@Override
public void run() {
while (true){
// synchronized(new Object()) 此种写法起不到锁的作用
// synchronized(obj) Object obj = new Object(); 前提 obj 没被static 修饰 使用此方式在 继承方式中同样 达不到线程安全 ,因为 在创建线程时,对应的对象创建了多个,同时 obj 也是多个,则不能作为锁
// synchronized(this) 此写法也不行,因为在继承中,此时的this对象 就是创建线程的对象,而继承是创建多个对象,则不能作为锁
// 解决方法 使用static 修饰 对象 static Object obj = new Object();
// 还可以使用 synchronized(Windows2.class)
synchronized(obj){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}else {
break;
}
}
}
}
}
/**
* 使用同步方法实现同步处理
*/
class ThreadWindowsOfSynchMethod extends Thread{
/**
* 默认100张票
*
* 此处 如果未被 static 修饰 则 是每个窗口是票 20 张 ,逻辑错误,但线程安全
*/
private static int ticket = 20;
public ThreadWindowsOfSynchMethod() {
super();
System.out.println("ThreadWindowsOfSynchMethod 同步方法");
}
static Object obj = new Object();
@Override
public void run() {
while (true){
sale();
}
}
/**
* @Description:
* 未使用 static 修饰 同步修饰器为 this 当使用Thread 的方式创建线程,需要创建多个对象,则此时的this 不是同一个,不能作为同步监视器
* public synchronized void sale()
* 使用static 修饰后 同步监视器是当前 类 也就是 类.class
* @Param:
* @return: void
* @Author: liujiexin
* @Date: 2021/6/13 4:20 下午
*/
public static synchronized void sale(){
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}
}
}
死锁问题
package com.wujing.thread.ThreadSecurity;
import org.springframework.aop.scope.ScopedProxyUtils;
/**
* @ClassName: TreadDeadLock
* @Description: 线程死锁问题
* 解决方法
* 专门的算法、原则
* 尽量减少同步资源的定义
* 尽量避免嵌套同步
* @Author liujiexin
* @Date 2021/6/13 4:45 下午
*/
public class TreadDeadLock {
public static void main(String[] args) {
StringBuffer stringBuffer1 = new StringBuffer();
StringBuffer stringBuffer2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (stringBuffer1){
stringBuffer1.append("a");
stringBuffer2.append("1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized(stringBuffer2){
stringBuffer1.append("b");
stringBuffer2.append("2");
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (stringBuffer2){
stringBuffer1.append("c");
stringBuffer2.append("3");
synchronized(stringBuffer1){
stringBuffer1.append("d");
stringBuffer2.append("4");
System.out.println(stringBuffer1);
System.out.println(stringBuffer2);
}
}
}
}).start();
}
}
Lock(锁) Jdk5.0后
package com.wujing.thread.ThreadSecurity;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassName: LockSecurity
* @Description: Lock 解决 线程安全问题
*
* synchronized 与 Lock 的异同
* 同: 用于解决 线程安全问题
* 异: synchronized 执行完同步代码,自动执行释放 同步监视器
* Lock 需要使用 unlock() 释放 同步监视器
* Lock 使用必须在 try 后第一行代码, 在 finally 中释放
*
* 同步操作的先后方式
* Lock -> 同步代码块 —> 同步方法
* 如何解决线程安全问题? 有几种方式
* 两种
* @Author liujiexin
* @Date 2021/6/13 4:59 下午
*/
public class LockSecurity {
public static void main(String[] args) {
RunnableWindowsOfLock windows = new RunnableWindowsOfLock();
System.out.println("哈哈哈哈哈哈哈");
Thread thread1 = new Thread(windows);
Thread thread2 = new Thread(windows);
Thread thread3 = new Thread(windows);
thread1.setName("窗口一");
thread2.setName("窗口二");
thread3.setName("窗口三");
thread1.start();
thread2.start();
thread3.start();
}
}
/**
* 使用同步代码块 创建线程
* 使用 Lock 解决 线程安全问题
*/
class RunnableWindowsOfLock implements Runnable{
/**
* 默认100张票
*/
private int ticket = 20;
private ReentrantLock lock1 = new ReentrantLock();
@Override
public void run() {
while (true){
try {
lock1.lock();
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 线程卖票 ,票号为:"+ ticket);
ticket--;
}else {
break;
}
}finally {
lock1.unlock();
}
}
}
}
线程通信
package com.wujing.thread.threadCommunication;
/**
* @ClassName: ThreadCommunication
*
* 多个线程实现 交替 打印 1~100
*
* wait() : 一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器
* notify(): 一旦执行此方法,就会唤醒被wait()的 一个线程,如果有多个线程被wait,唤醒优先级高的那个
* notifyAll(): 一旦执行此方法,就会唤醒被wait()的 所有的线程,如果有多个线程被wait,唤醒优先级高的那个
*
* 注意:
* 1、上述三个方法定义在在 Object 中
* 2、上述三个方法是在 同步方法 或同步块中使用,使用Lock 的方式的锁 不能使用。
* TODO Lock 加锁中的 实现线程通信
* 3、上述三个方法调用者必须是 同步方法 或同步块中的同步监视器,否则出现异常,IllegalMonitorStateException
*
* 面试题
* sleep()、 wait() 异同?
* 相同点:
* 使当前线程进入阻塞状态
* 不同点
* 1、定义位置:
* sleep() 定义在Thread 中, wait() 定义在 Object
* 2、调用要求
* sleep(): 任何场景
* wait(): 只能在 同步代码和同步方法中使用
* 3、是否释放同步监视器
* 如果两个方法都使用在同步代码块或同步方法中,
* sleep不会释放锁,
* wait()会释放锁
*
*
* @Description: 线程通信
* @Author liujiexin
* @Date 2021/6/13 10:49 下午
*/
public class ThreadCommunication {
public static void main(String[] args) {
NumberTest numberTest = new NumberTest();
Thread thread1 = new Thread(numberTest);
Thread thread2 = new Thread(numberTest);
Thread thread3 = new Thread(numberTest);
thread1.setName("线程1");
thread2.setName("线程2");
thread3.setName("线程3");
thread1.start();
thread2.start();
thread3.start();
}
}
class NumberTest implements Runnable{
private int num = 1;
@Override
public void run() {
while (true){
synchronized (this){
this.notifyAll();
if(num <= 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 当前数:"+ num);
num++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
面试题
生产者\消费者问题
package com.wujing.thread.threadInterview;
/**
* @ClassName: ProductCustomerTest
* @Description: 生产者消费者测试
*
* 题目:
* 一个栗子:就拿北京烤鸭来说,烤鸭子的厨师就是“生产者”,买烤鸭的顾客就是“消费者”,
* 烤鸭子的炉子就是有限缓冲区,比如说一个炉子只能放10只鸭子,那这个缓冲区的大小就是10,
* 鸭子就是缓冲区中的数据。当炉子里已经有10只鸭子的时候,厨师就不能再添加烤鸭了,没地方放了,
* 就必须等待顾客来买鸭子;而当炉子里没有鸭子时,顾客就必须等待厨师烤鸭子。
* 题目分析
*
* @Author liujiexin
* @Date 2021/6/13 11:34 下午
*/
public class ProductCustomerTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
Product product1 = new Product(clerk);
Product product2 = new Product(clerk);
product1.setName(" 生产者1");
product2.setName(" 生产者2");
product1.start();
product2.start();
Customer customer1 = new Customer(clerk);
Customer customer2 = new Customer(clerk);
customer1.setName("消费者1");
customer2.setName("消费者2");
customer1.start();
customer2.start();
}
}
/**
* @ClassName: Clerk
* @Description: 店员
* @Author liujiexin
* @Date 2021/6/13 11:40 下午
*/
class Clerk {
/**
* 店员取得产品数
*/
private Integer number = 0;
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public synchronized void productProduct() {
// 生产者生产 产品
if( number < 20){
number++;
System.out.println( Thread.currentThread().getName() + " , 开始生产第 "+number + "个产品");
notifyAll();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void customerProduct() {
// 消费者消费 产品
if( number > 0){
number--;
System.out.println( Thread.currentThread().getName() + " , 开始消费第 "+number + "个产品");
notifyAll();
}else {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* @ClassName: Customer
* @Description: 消费者
* @Author liujiexin
* @Date 2021/6/13 11:34 下午
*/
class Customer extends Thread{
private Clerk clerk;
public Customer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ", 开始消费");
while (true){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.customerProduct();
}
}
}
/**
* @ClassName: Product
* @Description: 生产者
* @Author liujiexin
* @Date 2021/6/13 11:33 下午
*/
class Product extends Thread{
private Clerk clerk;
public Product(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "开始生产");
while (true){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.productProduct();
}
}
}