@Override
public void run() {
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+“卖票”+ticket);
ticket–;
} else {
break;
}
}
}
}
public class RunnableTest {
public static void main(String[] args) {
Run run=new Run();
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();
thread2.start();
thread3.start();
}
}
class Run implements Runnable{
private int ticket=100;//这里不用加static
@Override
public void run() {
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+“卖票”+ticket);
ticket–;
}else {
break;
}
}
}
}
多线程创建两种方式的比较
开发中:优先选择实现Runnable接口的方式
-
实现的方式没有单继承的局限性
-
实现的方式更适合来处理多个线程共享数据
联系:Thread继承了Runnable接口
相同点:都需要重写run方法,将线程执行的逻辑写在run方法中
JDK5.0新增线程创建方式
实现Callable接口
-
相比Runnable,可以有返回值
-
方法可以抛出异常
-
支持泛型的返回值
-
需要借助FutureTask类,比如获取返回结果
创建线程的方式三:
-
创建一个实现callable的接口
-
实现call方法,将此线程需要执行的操作声明在call()中
-
创建callable接口实现类的对象
-
将此callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象
-
将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象并调用start方法
-
获取callable中call方法的返回值
public class CallableTest {
public static void main(String[] args) {
ThreadTests t = new ThreadTests();
FutureTask futureTask1 = new FutureTask(t);
new Thread(futureTask1).start();
try {
Object sum = futureTask1.get();
System.out.println(“总合”+sum);
}catch (Exception e){
e.printStackTrace();
}
}
}
class ThreadTests implements Callable{
@Override
public Object call() throws Exception {
int num=0;
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0){
System.out.println(i);
num += i;
}
}
return num;
}
}
线程池创建线程
优点:
-
提高响应速度(减少了创建新线程的时间)
-
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
-
便于线程管理
public class ThreadPool {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.execute(new NumberThreadTest());//适合Runnable接口
service.execute(new NumberThreadTest());
//service.submit()适合使用和callable
service.shutdown();//关闭线程池
}
}
class NumberThreadTest implements Runnable{
private static int ticket=100;
@Override
public void run() {
while(true){
if (ticket>0){
System.out.println(Thread.currentThread().getName()+“:”+ticket);
ticket–;
}else {
break;
}
}
}
}
线程常用方法
yield();//释放当前cpu的执行权
join();//在线程a中调用线程b的join方法,线程a会陷入阻塞状态直到线程b执行完毕
stop();//强制线程生命期结束,不推荐使用
boolean isAlive();//判断线程是否还或者
sleep(long timemilltime);//让当前线程睡眠指定的milltime毫秒,在指定的milltime毫秒时间内,当前线程是阻塞状态
**以下三个方法必须在同步代码块或者同步方法中使用,并且调用者必须是同步代码块或同步方法中的同步监视器(同一把锁)**否则会出现IllegalMonitorStateException异常
属于Object类中的方法
wait():一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器
notify():一旦执行此方法,就会唤醒被wait的一个线程,如果有多个线程被wait,则唤醒优先级高的
notifyAll():唤醒所有线程
线程的优先级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5
默认优先级都为5
如何获取:
-
getPriority():获取线程的优先级
-
setPriority(int p):设置线程的优先级
并不是优先级高就一定先被CPU执行,只能从概率上讲更容易被CPU执行
线程的生命周期
-
新建
-
就绪
-
运行
-
阻塞
-
死亡
synchronized
操作共享数据的代码,即为需要被同步的代码
-
共享数据:多个线程共同操作的变量
-
同步监视器:锁
-
任何一个类的对象都可以充当锁
-
多个线程必须用同一把锁
synchronized同步代码块解决线程安全问题
Runnable同步代码块解决线程安全的问题
/**
* 操作共享数据的代码,即为需要被同步的代码
* 共享数据:多个线程共同操作的变量
* 同步监视器:锁
* 任何一个类的对象都可以充当锁
* 多个线程必须用同一把锁
*/
public class RunnableTest {
public static void main(String[] args) {
Run run=new Run();
Thread thread1 = new Thread(run);
Thread thread2 = new Thread(run);
Thread thread3 = new Thread(run);
thread1.start();
thread2.start();
thread3.start();
}
}
class Run implements Runnable{
private int ticket=100;
Object object=new Object();
@Override
public void run() {
while(true){
synchronized(object){//可以使用this充当锁,this为当前对象
if(ticket>0){
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“卖票”+ticket);
ticket–;
}else {
break;
}
}
}
}
}
执行结果
Thread-0卖票100
Thread-0卖票99
Thread-2卖票98
Thread-1卖票97
Thread-1卖票96
Thread-1卖票95
Thread-2卖票94
Thread-2卖票93
Thread-2卖票92
Thread-2卖票91
Thread-2卖票90
Thread-2卖票89
Thread-2卖票88
Thread-2卖票87
Thread-2卖票86
Thread-2卖票85
Thread-0卖票84
Thread-0卖票83
Thread-0卖票82
Thread-0卖票81
Thread-0卖票80
Thread-0卖票79
Thread-0卖票78
Thread-0卖票77
Thread-0卖票76
Thread-0卖票75
Thread-0卖票74
Thread-0卖票73
Thread-0卖票72
Thread-0卖票71
Thread-0卖票70
Thread-0卖票69
Thread-0卖票68
Thread-0卖票67
Thread-0卖票66
Thread-0卖票65
Thread-0卖票64
Thread-0卖票63
Thread-0卖票62
Thread-0卖票61
Thread-0卖票60
Thread-2卖票59
Thread-2卖票58
Thread-2卖票57
Thread-2卖票56
Thread-2卖票55
Thread-2卖票54
Thread-2卖票53
Thread-2卖票52
Thread-2卖票51
Thread-2卖票50
Thread-2卖票49
Thread-2卖票48
Thread-2卖票47
Thread-2卖票46
Thread-2卖票45
Thread-2卖票44
Thread-2卖票43
Thread-1卖票42
Thread-1卖票41
Thread-1卖票40
Thread-1卖票39
Thread-1卖票38
Thread-1卖票37
Thread-1卖票36
Thread-1卖票35
Thread-1卖票34
Thread-1卖票33
Thread-1卖票32
Thread-1卖票31
Thread-1卖票30
Thread-1卖票29
Thread-1卖票28
Thread-1卖票27
Thread-1卖票26
Thread-2卖票25
Thread-2卖票24
Thread-2卖票23
Thread-2卖票22
Thread-2卖票21
Thread-2卖票20
Thread-2卖票19
Thread-2卖票18
Thread-2卖票17
Thread-2卖票16
Thread-2卖票15
Thread-2卖票14
Thread-2卖票13
Thread-2卖票12
Thread-2卖票11
Thread-2卖票10
Thread-2卖票9
Thread-2卖票8
Thread-2卖票7
Thread-2卖票6
Thread-2卖票5
Thread-2卖票4
Thread-2卖票3
Thread-0卖票2
Thread-0卖票1
Process finished with exit code 0
同步的方式解决了线程安全的问题,操作同步代码时,只有一个线程参与,其他线程等待,相当于一个单线程的过程,效率低
Thread方式同步代码块解决线程安全的问题
public class ThreadTest {
public static void main(String[] args) {
MyThread m1 = new MyThread();
MyThread m2 = new MyThread();
MyThread m3 = new MyThread();
m1.start();
m2.start();
m3.start();
}
}
class MyThread extends Thread{
private static int ticket=100;
Object object=new Object();
@Override
public void run() {
while(true){
synchronized(object){//不可以使用this充当锁,这里this代表了m1,m2,m3三个对象
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+“卖票:”+ticket);
ticket–;
}else {
break;
}
}
}
}
}
执行结果
Thread-0卖票:100
Thread-1卖票:100
Thread-2卖票:100
Thread-2卖票:97
Thread-1卖票:97
Thread-0卖票:97
Thread-2卖票:94
Thread-0卖票:94
Thread-1卖票:94
Thread-1卖票:91
Thread-2卖票:91
Thread-0卖票:90
Thread-2卖票:88
Thread-1卖票:88
Thread-0卖票:88
Thread-2卖票:85
Thread-0卖票:84
Thread-1卖票:84
Thread-2卖票:82
Thread-0卖票:82
Thread-1卖票:82
Thread-2卖票:79
Thread-0卖票:79
Thread-1卖票:78
Thread-2卖票:76
Thread-1卖票:76
Thread-0卖票:76
Thread-2卖票:73
Thread-1卖票:73
Thread-0卖票:73
Thread-0卖票:70
Thread-1卖票:70
Thread-2卖票:70
Thread-1卖票:67
Thread-0卖票:67
Thread-2卖票:66
Thread-2卖票:64
Thread-1卖票:64
Thread-0卖票:62
Thread-2卖票:61
Thread-1卖票:61
Thread-0卖票:61
Thread-1卖票:58
Thread-0卖票:58
Thread-2卖票:56
Thread-0卖票:55
Thread-1卖票:55
Thread-2卖票:55
Thread-0卖票:52
Thread-1卖票:52
Thread-2卖票:52
Thread-1卖票:49
Thread-2卖票:49
Thread-0卖票:49
Thread-2卖票:46
Thread-0卖票:46
Thread-1卖票:46
Thread-2卖票:43
Thread-0卖票:43
Thread-1卖票:43
Thread-2卖票:40
Thread-1卖票:40
Thread-0卖票:40
Thread-0卖票:37
Thread-2卖票:37
Thread-1卖票:37
Thread-2卖票:34
Thread-1卖票:34
Thread-0卖票:34
Thread-2卖票:31
Thread-0卖票:31
Thread-1卖票:31
Thread-1卖票:28
Thread-2卖票:28
Thread-0卖票:28
Thread-0卖票:25
Thread-2卖票:25
Thread-1卖票:25
Thread-1卖票:22
Thread-0卖票:22
Thread-2卖票:22
Thread-2卖票:19
Thread-0卖票:19
Thread-1卖票:19
Thread-2卖票:16
Thread-1卖票:16
Thread-0卖票:16
Thread-2卖票:13
Thread-0卖票:13
Thread-1卖票:13
Thread-2卖票:10
Thread-0卖票:10
Thread-1卖票:10
Thread-2卖票:7
Thread-0卖票:7
Thread-1卖票:7
Thread-2卖票:4
Thread-1卖票:4
Thread-0卖票:4
Thread-1卖票:1
Thread-2卖票:1
Thread-0卖票:1
Process finished with exit code 0
会发现还是线程不安全,这是因为三个对象使用的锁不是同一把锁了
一定要注意给object 加上static 才行
static Object object=new Object();
加入后就会发现线程又安全了
或者使用 MyThread.class 充当锁
Synchronized同步方法解决线程安全问题
使用同步方法解决线程安全问题
实现Runnable接口方式
public class SynchronizedMethod {
public static void main(String[] args) {
ThreadMethod threadMethod = new ThreadMethod();
Thread thread1 = new Thread(threadMethod);
Thread thread2 = new Thread(threadMethod);
Thread thread3 = new Thread(threadMethod);
thread1.start();
thread2.start();
thread3.start();
}
}
class ThreadMethod implements Runnable{
private int ticket=100;
public synchronized void show(){//这里用的锁是this
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+“卖票:”+ticket);
ticket–;
}
}
@Override
public void run() {
while(ticket>0){
show();
}
}
}
继承Thread类方式
public class SynchronizedMethodThread {
public static void main(String[] args) {
ThreadMethod threadMethod1 = new ThreadMethod();
ThreadMethod threadMethod2 = new ThreadMethod();
ThreadMethod threadMethod3 = new ThreadMethod();
threadMethod1.start();
threadMethod2.start();
threadMethod3.start();
}
}
class ThreadMethod extends Thread{
private static int ticket=100;
public static synchronized void show(){//这里必须加上static,不加上static锁使用的为this对象,这里创建了三个对象。加上static锁使用的为ThreadMethod.class
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(ticket > 0){
System.out.println(Thread.currentThread().getName()+“卖票:”+ticket);
ticket–;
}
}
@Override
public void run() {
while(ticket>0){
show();
}
}
}
同步方法总结:
-
同步方法仍涉及到同步监视器,只是不需要我们显示的声明
-
非静态的同步方法,同步监视器(锁)是:this
-
静态的同步方法,同步监视器(锁)是:当前类本身
锁方式解决线程安全问题
public class ReentrantLockTest {
public static void main(String[] args) {
Window window = new Window();
Thread thread1 = new Thread(window);
Thread thread2 = new Thread(window);
Thread thread3 = new Thread(window);
thread1.start();
thread2.start();
thread3.start();
}
}
class Window implements Runnable{
private int ticket=100;
private ReentrantLock reentrantLock=new ReentrantLock();
@Override
public void run() {
while (true){
reentrantLock.lock();//加锁
try {
if(ticket>0){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“卖票”+ticket);
ticket–;
}else {
break;
}
}finally {
reentrantLock.unlock();//释放锁
}
}
}
}
注意:如果用继承Thread类的方式去使用lock
必须在 锁前面加上static
private static ReentrantLock reentrantLock=new ReentrantLock();
Synchronized和锁(Lock)方式的异同?
-
相同:两者都可以解决线程安全问题
-
不同:Synchronized机制在执行完相应的同步代码后,自动释放同步监视器,Lock需要手动启动同步(Lock()),同时结束也需要手动结束同步(unlock())
单例模式线程安全
class Bank{
private static Bank instance = null;
private Bank(){}
public static Bank getInstance(){
// synchronized(Bank.class){//方式一:效率稍差
// if (instance == null){
// instance=new Bank();
// }
// return instance;
// }
//方式二:效率更高
if (instance == null){
synchronized(Bank.class){
if (instance == null){
instance=new Bank();
}
}
}
return instance;
}
}
死锁
–
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
死锁案例
public class DeadLock {
public static void main(String[] args) {
StringBuffer s1=new StringBuffer();
StringBuffer s2=new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append(“a”);
s2.append(“1”);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append(“b”);
s2.append(“2”);
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s2){
s1.append(“c”);
s2.append(“3”);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append(“d”);
s2.append(“4”);
System.out.println(s1);
System.out.println(s2);
}
}
}
}).start();
}
}
多线程通信列题
public class CommunicationTest {
/**
* 实现两个线程交替打印1-100
*/
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(test);
Thread t2 = new Thread(test);
Thread t3 = new Thread(test);
t1.start();
t2.start();
t3.start();
}
}
class Test implements Runnable{
private int number=1;
@Override
public void run() {
while (true){//while条件不能用number <= 100 因为这样就没有全锁住操作共享数据的代码,线程就不安全了
synchronized (this){ //也不能锁住while 锁住了就只有一个线程能进来操作了
if (number <= 100){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“:”+number);
number++;
}else {
break;
}
}
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
完结
Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
if (number <= 100){
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“:”+number);
number++;
}else {
break;
}
}
}
}
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-DISaHB1F-1713433148588)]
[外链图片转存中…(img-HbtxofjY-1713433148589)]
[外链图片转存中…(img-aTRohPgU-1713433148589)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
完结
Redis基于内存,常用作于缓存的一种技术,并且Redis存储的方式是以key-value的形式。Redis是如今互联网技术架构中,使用最广泛的缓存,在工作中常常会使用到。Redis也是中高级后端工程师技术面试中,面试官最喜欢问的问题之一,因此作为Java开发者,Redis是我们必须要掌握的。
Redis 是 NoSQL 数据库领域的佼佼者,如果你需要了解 Redis 是如何实现高并发、海量数据存储的,那么这份腾讯专家手敲《Redis源码日志笔记》将会是你的最佳选择。
[外链图片转存中…(img-X5pZl9V3-1713433148591)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!