//第一种:通过构造器传递String参数来命名,这在Thread源码中是具备的!
ThreadMethods tm = new ThreadMethods(“线程一”);
//第二种:可以自己给tm线程重新命名
//tm.setName(“线程一”);
tm.start();
//怎样给主线程命名呢?
//直接通过Thread.currentThread().setName(“XXX”)设置就可以了
Thread.currentThread().setName(“主线程”);
for (int i=0;i<100;i++){
if (i % 2 == 0)
System.out.println(Thread.currentThread().getName() + “:” +i);
if (i == 20){
try {
//join()方法使当前线程进入阻塞状态,等待另一个线程调用结束,再进行。
tm.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断tm线程是否还存在
System.out.println(tm.isAlive());
}
}
class ThreadMethods extends Thread{
@Override
public void run(){
for (int i=0;i<100;i++){
if (i % 2 == 0){
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “:” +i);
}
//if(i % 20 == 0){
//释放当前CPU的执行权,当然有可能下一刻又分配到当前的线程
//yield();
//}
}
}
public ThreadMethods(String name){
//通过继承Thread的构造器方法,来命名线程名字
super(name);
}
}
线程优先级:
-
MAX_PRIORITY :10
-
MIN_PRIORITY :1
-
NORM_PRIORITY :5
如何获取和设置当前线程的优先级:
-
getPriority() :获取线程的优先级。
-
setPriority(int p) :设置线程的优先级。
注意:高优先级的线程要抢占低优先级线程cpu执行权,但是只是从概率上来讲,并不是从一开始只有当高优先级线程执行完以后再执行低优先级。
package com.holmes.java;
public class ThreadMethodTest {
public static void main(String[] args) {
ThreadMethods tm = new ThreadMethods(“线程一”);
//设置线程优先级
tm.setPriority(Thread.MAX_PRIORITY);
tm.start();
//怎样给主线程命名呢?
//直接通过Thread.currentThread().setName(“XXX”)设置就可以了
Thread.currentThread().setName(“主线程”);
for (int i=0;i<100;i++){
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + “:” + Thread.currentThread().getPriority() +“–”+ i);
}
if (i == 20){
try {
//join()方法使当前线程进入阻塞状态,等待另一个线程调用结束,再进行。
tm.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断tm线程是否还存在
System.out.println(tm.isAlive());
}
}
class ThreadMethods extends Thread{
@Override
public void run(){
for (int i=0;i<100;i++){
if (i % 2 == 0){
//getPriority()获取线程优先级,提示这里不写TThread.currentThread(),默认就是当前的this,还是该线程。
System.out.println(Thread.currentThread().getName() + “:” + getPriority() + “–”+i);
}
}
}
public ThreadMethods(String name){
//通过继承Thread的构造器方法,来命名线程名字
super(name);
}
}
=======================================================================================
package com.holmes.java;
/*
创建多线程方式二:实现Runnable接口
-
创建一个实现了Runnable接口的类。
-
实现类去实现Runnable中的抽象方法:run()方法。
-
创建实现类的对象。
-
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象。
-
通过Thread类的对象调用start()方法。
*/
public class ThreadTest2 {
public static void main(String[] args) {
MyThead3 mt3 = new MyThead3();
//将实现类对象作为参数传递给Thread构造器
//整体上而言就是一个多态的效果
Thread th1 = new Thread(mt3);
//此时这个线程是th1,而不是mt3!
//而这里的start()方法:开启线程调用run()方法,这里的run()方法自然也是Thread中的run()方法。
//之所以还是执行的MyThread3得看源码,如下面图:
th1.setName(“线程一”);
th1.start();
Thread th2 = new Thread(mt3);
th2.setName(“线程二”);
th2.start();
}
}
class MyThead3 implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
if (i%2==0){
System.out.println(Thread.currentThread().getName() +“:”+ i);
}
}
}
}
start()方法:开启线程调用run()方法,这里的run()方法自然也是Thread中的run()方法。
问题来了,既然是Thread中得run()方法,为什么最后还是调用得实现类MyThread中的run()方法呢?看源码。
==============================================================================
- 首先,一个继承,一个实现接口。
为什么开发中,优先选择:实现Runable接口的方式?
-
实现的方式没有类的单继承性的局限性。
-
实现的方式更适合来处理多个线程有共享数据的情况。
相同点:两种方式都需要重写run()方法,将线程要执行的逻辑声明在run()中。
通过看Thread源码,差不多也都知道,它们的联系。
=============================================================================
线程分为两种:一种守护线程,一种用户线程。
通过调用thread.setDaemon(true),可以将用户线程变成一个守护线程:
=========================================================================
Thread.State类定义了线程的几种状态:
新建,就绪,运行,阻塞,死亡
生命周期流程如下:
=========================================================================
出现线程安全的关键所在,就是多线程是否存在共享数据。
线程的安全问题:
package com.holmes.java02;
public class WindowTest2 {
public static void main(String[] args) {
MyThread4 m = new MyThread4();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName(“窗口一”);
t2.setName(“窗口二”);
t3.setName(“窗口三”);
t1.start();
t2.start();
t3.start();
}
}
class MyThread4 implements Runnable{
private static int ticket = 100;
@Override
public void run() {
while (true){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() +“: 买票,票号为:” + ticket);
ticket–;
}else {
break;
}
}
}
}
就取票而言,三个窗口(t1,t2,t3)如果都进入阻塞状态,而票最后仅仅剩余1张,随后三个窗口进入就绪状态就变成了下面的情形:
(也就是票成了0和成了负数的这种错误情况!就是出现了重票或者错票的问题。)
这就是一种线程安全问题!
为什么出现这种线程安全问题?
当某个线程操作车票的过程中,尚未操作完成时(像上面的sleep()方法阻塞了),其他线程接着参与进来,也来操作车票,就会出现线程的安全问题。
解决方式两种:一种是同步代码块 ,一种是同步方法。
9. 使用同步代码块解决 实现Runnable接口的 线程安全问题
===============================================================================================
如何解决这种线程安全问题?
当一个线程a在操作共享数据(车票ticket),其他线程不能参与进来。直到线程a操作完ticket时,其他线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。
在Java中,我们通过同步机制,来解决线程的安全问题。
方式一:同步代码块
上面synchronized的同步监视器是什么? 俗称:锁
对于Runnable实现线程,同步监视器用this时最好的,也是最简单的。
任何一个类的对象,都可以充当锁,充当同步监视器。意思就是只要是对象都可以,他只不过起到了一个锁的效果。
这个同步代码块的方式,要求多个线程必须要共用同一把锁。
package com.holmes.java02;
public class WindowTest2 {
public static void main(String[] args) {
MyThread4 m = new MyThread4();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName(“窗口一”);
t2.setName(“窗口二”);
t3.setName(“窗口三”);
t1.start();
t2.start();
t3.start();
}
}
class MyThread4 implements Runnable{
private static int ticket = 100;
//这里定义的obj同步监视器,不能定义到run()方法中,否则三个线程一调用,就是三个锁在执行了!
Object obj = new Object();
@Override
public void run() {
while (true){
//这里的synchronized就是同步代码块,来解决线程安全的问题(重票,错票)
//这里的obj也就是锁,必须是所有线程共用的一把锁,不能不同!!
//对于Runnable实现线程,同步监视器用this时最好的,也是最简单的。
//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;
}
}
}
}
}
该方式的优缺点:
-
解决了线程的安全问题。
-
但是再操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程过程,效率低。
10. 使用同步代码块解决 继承Thread的 线程安全问题
============================================================================================
通过继承Thread类创建的线程安全问题,就不能按照上面实现Runnable创建的线程安全解决方式来解决。
原因就是它是创建了多个对象线程,每个对象线程都对应了一把锁,换句话说,这三个线程对象,都对应自己的一把锁,不是同步的。
怎么解决?
加一个static就可以了,把对应的同步监测器(锁),设置为静态共享就可以了。
package com.holmes.java02;
/*
三个窗口买票,总票数为100张
*/
public class WindowTest {
public static void main(String[] args) {
Window w1 = new Window();
Window w2 = new Window();
Window w3 = new Window();
w1.setName(“窗口1”);
w2.setName(“窗口2”);
w3.setName(“窗口3”);
w1.start();
w2.start();
w3.start();
}
}
class Window extends Thread{
private static int ticket = 100;
//通过加static,把obj变成每个对象共享,这样3个线程就还是共用一把锁。
private static Object obj = new Object();
@Override
public void run(){
//用这种方式是可以并且最简便的。
//synchronized (Window.class){…}
//这里的obj,三个线程必须都相同!
synchronized (obj){
while (true) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “: 买票,票号为:” + ticket);
ticket–;
} else {
break;
}
}
}
}
}
对于Runnable实现线程,同步监视器用this时最好的,也是最简单的。那么对于继承Thread类线程,同步监视器用XXX.class(当前类) ,是最好的,例如上面的:Window.class (像)。
11. 使用同步方法解决 实现Runnable接口的 线程安全问题
===============================================================================================
在Java中,我们通过同步机制,来解决线程的安全问题。
方式二:同步方法
给方法赋予一个关键字:synchronized。
package com.holmes.java02;
/*
使用同步方法来解决实现Runnable接口的线程安全问题
*/
public class WindowTest03 {
public static void main(String[] args) {
Window3 m = new Window3();
Thread t1 = new Thread(m);
Thread t2 = new Thread(m);
Thread t3 = new Thread(m);
t1.setName(“窗口一”);
t2.setName(“窗口二”);
t3.setName(“窗口三”);
t1.start();
t2.start();
t3.start();
}
}
class Window3 implements Runnable{
private static int ticket = 100;
@Override
//这里不适合直接给run()方法操作同步,因为3个线程共享代码仅仅是while里面的内容,不能多余也不能少!
public void run() {
while (true){
show();
}
}
//需要注意的是synchronized在这里也是有同步监视器,默认就是this,当前对象。
private synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “: 买票,票号为:” + ticket);
ticket–;
}
}
}
12. 使用同步方法解决 继承Thread类的 线程安全问题
============================================================================================
继承Thread类的线程,变量和方法都要求是静态的!
package com.holmes.java02;
/*
使用同步方法解决 继承Thread类的 线程安全问题
*/
public class WindowTest4 {
public static void main(String[] args) {
Window2 w1 = new Window2();
Window2 w2 = new Window2();
Window2 w3 = new Window2();
w1.setName(“窗口1”);
w2.setName(“窗口2”);
w3.setName(“窗口3”);
w1.start();
w2.start();
w3.start();
}
}
class Window2 extends Thread{
//继承thread类的线程,必须都要静态才对
private static int ticket = 100;
@Override
public void run(){
while (true) {
show();
}
}
//继承thread类的线程,必须都要静态才对
//但是这里的同步监视器就不是this了,而是当前的类Window4.class!
private static synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “: 买票,票号为:” + ticket);
ticket–;
}
}
}
注意:一旦涉及到多线程共享数据(变量,对象等),一定考虑线程安全问题!!!
=======================================================================
死锁的定义很重要:
出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态。
因此,像synchronized同步机制,我们要避免这种死锁情况发生!!
package com.holmes.java02;
public class ThreadTest {
//因此,像synchronized同步机制,我们要避免这种死锁情况发生!!
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run(){
//这里调用了s1,进入sleep,等待s2.
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() {
//这里调用了s2,进入sleep,等待s2.
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();
//想这种就会造成死锁。
}
}
死锁解决办法:
-
专门的算法,原则。
-
尽量减少同步资源的定义。
-
尽量避免嵌套同步。
====================================================================================
Lock锁是JDK5.0 ,新增的特性。
Lock锁创建步骤:
-
1.实例化ReentrantLock。
-
2.调用锁定方法(上锁):lock()方法。
-
3.调用解锁方法(解锁):unlock()方法。
package com.holmes.java02;
import java.util.concurrent.locks.ReentrantLock;
class Window5 implements Runnable{
private int ticket = 100;
//1. 实例化ReentrantLock
//ReentrantLock:参数为true(公平锁),参数默认为false(非公平锁)
//公平锁就是按照先到先得顺序来执行线程顺序,非公平锁就是看谁先抢到cpu资源谁先执行。
private ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while(true){
try{
//2. 调用锁定方法(上锁):lock()方法
lock.lock();
if (ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+“,售票,票号为:”+ticket);
ticket–;
}else{
break;
}
}finally {
//3. 调用解锁方法(解锁):unlock()方法
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window5 w = new Window5();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName(“窗口1”);
t2.setName(“窗口2”);
t3.setName(“窗口3”);
t1.start();
t2.start();
t3.start();
}
}
面试题:synchronized 于 Lock 的异同?
建议优先使用顺序:
面试题:如何解决线程安全问题?有几种方式?
- 两种,synchronized和lock。
=======================================================================
本质上,就是wait()方法和notify()方法的使用。
-
wait()方法:一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器(释放锁)。
-
notify()方法:一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的线程。
-
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
package com.holmes.java03;
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
synchronized (this){
//notify()和notifyAll():前者只能唤醒一个,后者能唤醒所有!
notify();
if (number < 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “:” + number);
number++;
//wait()方法:使得调用如下wait()方法的线程进入阻塞状态
//wait方法会释放锁!!!因此才能交互运行。
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName(“线程1”);
t2.setName(“线程2”);
t1.start();
t2.start();
}
最后
本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们:
目录:
Java面试核心知识点
一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!
Java面试核心知识点
65d875712beeaf3a48b.png)
建议优先使用顺序:
面试题:如何解决线程安全问题?有几种方式?
- 两种,synchronized和lock。
=======================================================================
本质上,就是wait()方法和notify()方法的使用。
-
wait()方法:一旦执行此方法,当前线程进入阻塞状态,并释放同步监视器(释放锁)。
-
notify()方法:一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的线程。
-
notifyAll():一旦执行此方法,就会唤醒所有被wait的线程。
package com.holmes.java03;
class Number implements Runnable{
private int number = 1;
@Override
public void run() {
while (true){
synchronized (this){
//notify()和notifyAll():前者只能唤醒一个,后者能唤醒所有!
notify();
if (number < 100){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + “:” + number);
number++;
//wait()方法:使得调用如下wait()方法的线程进入阻塞状态
//wait方法会释放锁!!!因此才能交互运行。
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName(“线程1”);
t2.setName(“线程2”);
t1.start();
t2.start();
}
最后
本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们:
目录:
[外链图片转存中…(img-irEpNXrr-1725878572130)]
Java面试核心知识点
一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!
[外链图片转存中…(img-Y0acC8Dg-1725878572131)]
Java面试核心知识点